diff --git a/conftest.py b/conftest.py index 1720019fc96..199d9257ce9 100644 --- a/conftest.py +++ b/conftest.py @@ -170,6 +170,9 @@ def array_type(request): quantity = metpy.units.units.Quantity if request.param == 'dask': dask_array = pytest.importorskip('dask.array', reason='dask.array is not available') + marker = request.node.get_closest_marker('xfail_dask') + if marker is not None: + request.applymarker(pytest.mark.xfail(reason=marker.args[0])) return lambda d, u, *, mask=None: quantity(dask_array.array(d), u) elif request.param == 'xarray': return lambda d, u, *, mask=None: xarray.DataArray(d, attrs={'units': u}) diff --git a/setup.cfg b/setup.cfg index 61eed990283..cc283e08f67 100644 --- a/setup.cfg +++ b/setup.cfg @@ -96,8 +96,7 @@ extension = MPY = flake8_metpy:MetPyChecker paths = ./tools/flake8-metpy [tool:pytest] -# https://github.com/matplotlib/pytest-mpl/issues/69 -markers = mpl_image_compare +markers = xfail_dask: marks tests as expected to fail with Dask arrays norecursedirs = build docs .idea doctest_optionflags = NORMALIZE_WHITESPACE mpl-results-path = test_output diff --git a/src/metpy/testing.py b/src/metpy/testing.py index 17e48c12a58..36adf002c00 100644 --- a/src/metpy/testing.py +++ b/src/metpy/testing.py @@ -215,12 +215,4 @@ def wrapper(*args, **kwargs): return dec -def xfail_dask(array, reason): - """Xfail a test if `array` is a `dask.array.Array`.""" - dask_array = pytest.importorskip('dask.array', reason='dask.array not available') - - if not isinstance(array, xr.DataArray) and isinstance(array.m, dask_array.Array): - pytest.xfail(reason=reason) - - check_and_silence_deprecation = check_and_silence_warning(MetpyDeprecationWarning) diff --git a/tests/calc/test_basic.py b/tests/calc/test_basic.py index 0984803dab3..e6ae8878e9b 100644 --- a/tests/calc/test_basic.py +++ b/tests/calc/test_basic.py @@ -15,8 +15,7 @@ pressure_to_height_std, sigma_to_pressure, smooth_circular, smooth_gaussian, smooth_n_point, smooth_rectangular, smooth_window, wind_components, wind_direction, wind_speed, windchill) -from metpy.testing import (assert_almost_equal, assert_array_almost_equal, assert_array_equal, - xfail_dask) +from metpy.testing import assert_almost_equal, assert_array_almost_equal, assert_array_equal from metpy.units import units @@ -74,6 +73,7 @@ def test_speed(array_type): assert_array_almost_equal(true_speed, speed, 4) +@pytest.mark.xfail_dask('Item assignment with not supported') def test_direction(array_type): """Test calculating wind direction.""" # The last two (u, v) pairs and their masks test masking calm and negative directions @@ -81,8 +81,6 @@ def test_direction(array_type): u = array_type([4., 2., 0., 0., 1.], 'm/s', mask=mask) v = array_type([0., 2., 4., 0., -1], 'm/s', mask=mask) - xfail_dask(u, 'Item assignment with not supported') - direc = wind_direction(u, v) true_dir = array_type([270., 225., 180., 0., 315.], 'degree', mask=mask) @@ -90,17 +88,16 @@ def test_direction(array_type): assert_array_almost_equal(true_dir, direc, 4) +@pytest.mark.xfail_dask('Boolean index assignment in Dask expects equally shaped arrays') def test_direction_with_north_and_calm(array_type): """Test how wind direction handles northerly and calm winds.""" - mask = [False, False, False, True, True] - u = array_type([0., -0., 0., 1., 1.], 'm/s', mask=mask) - v = array_type([0., 0., -5., 1., 1.], 'm/s', mask=mask) - - xfail_dask(u, 'Boolean index assignment in Dask expects equally shaped arrays') + mask = [False, False, False, True] + u = array_type([0., -0., 0., 1.], 'm/s', mask=mask) + v = array_type([0., 0., -5., 1.], 'm/s', mask=mask) direc = wind_direction(u, v) - true_dir = array_type([0., 0., 360., 225., 225.], 'deg', mask=mask) + true_dir = array_type([0., 0., 360., 225.], 'deg', mask=mask) assert_array_almost_equal(true_dir, direc, 4) @@ -111,14 +108,13 @@ def test_direction_dimensions(): assert str(d.units) == 'degree' +@pytest.mark.xfail_dask('Boolean index assignment in Dask expects equally shaped arrays') def test_oceanographic_direction(array_type): """Test oceanographic direction (to) convention.""" mask = [False, True, False] u = array_type([5., 5., 0.], 'm/s', mask=mask) v = array_type([-5., 0., 5.], 'm/s', mask=mask) - xfail_dask(u, 'Boolean index assignment in Dask expects equally shaped arrays') - direc = wind_direction(u, v, convention='to') true_dir = array_type([135., 90., 360.], 'deg', mask=mask) assert_almost_equal(direc, true_dir, 4) @@ -211,14 +207,13 @@ def test_windchill_face_level(): assert_array_almost_equal(wc, values, 0) +@pytest.mark.xfail_dask('operands could not be broadcast together with shapes (0, 5) (nan,)') def test_heat_index_basic(array_type): """Test the basic heat index calculation.""" mask = [False, True, False, True, False] temp = array_type([80, 88, 92, 110, 86], 'degF', mask=mask) rh = array_type([40, 100, 70, 40, 88], 'percent', mask=mask) - xfail_dask(temp, 'operands could not be broadcast together with shapes (0, 5) (nan,)') - hi = heat_index(temp, rh) values = array_type([80, 121, 112, 136, 104], 'degF', mask=mask) assert_array_almost_equal(hi, values, 0) @@ -413,6 +408,10 @@ def test_coriolis_units(): assert f.units == units('1/second') +@pytest.mark.xfail_dask( + 'boolean index did not match indexed array along dimension 0; dimension is 2 but ' + 'corresponding boolean dimension is 3' +) def test_apparent_temperature(array_type): """Test the apparent temperature calculation.""" temperature = array_type([[90, 90, 70], @@ -422,12 +421,6 @@ def test_apparent_temperature(array_type): wind = array_type([[5, 3, 3], [10, 1, 10]], 'mph') - reason = ( - 'boolean index did not match indexed array along dimension 0; dimension is 2 but ' - 'corresponding boolean dimension is 3' - ) - xfail_dask(temperature, reason) - truth = units.Quantity(np.ma.array([[99.6777178, 86.3357671, 70], [8.8140662, 20, 60]], mask=[[False, False, True], [False, True, True]]), units.degF) @@ -557,11 +550,15 @@ def test_smooth_gaussian_3d_units(): def test_smooth_n_pt_5(array_type): """Test the smooth_n_pt function using 5 points.""" - hght = array_type([[5640., 5640., 5640., 5640., 5640.], - [5684., 5676., 5666., 5659., 5651.], - [5728., 5712., 5692., 5678., 5662.], - [5772., 5748., 5718., 5697., 5673.], - [5816., 5784., 5744., 5716., 5684.]], '') + hght = np.array([[5640., 5640., 5640., 5640., 5640.], + [5684., 5676., 5666., 5659., 5651.], + [5728., 5712., 5692., 5678., 5662.], + [5772., 5748., 5718., 5697., 5673.], + [5816., 5784., 5744., 5716., 5684.]]) + mask = np.zeros_like(hght) + mask[::2, ::2] = 1 + hght = array_type(hght, '', mask=mask) + shght = smooth_n_point(hght, 5, 1) s_true = array_type([[5640., 5640., 5640., 5640., 5640.], [5684., 5675.75, 5666.375, 5658.875, 5651.], @@ -692,11 +689,15 @@ def test_smooth_gaussian_temperature(): def test_smooth_window(array_type): """Test smooth_window with default configuration.""" - hght = array_type([[5640., 5640., 5640., 5640., 5640.], - [5684., 5676., 5666., 5659., 5651.], - [5728., 5712., 5692., 5678., 5662.], - [5772., 5748., 5718., 5697., 5673.], - [5816., 5784., 5744., 5716., 5684.]], 'meter') + hght = np.array([[5640., 5640., 5640., 5640., 5640.], + [5684., 5676., 5666., 5659., 5651.], + [5728., 5712., 5692., 5678., 5662.], + [5772., 5748., 5718., 5697., 5673.], + [5816., 5784., 5744., 5716., 5684.]]) + mask = np.zeros_like(hght) + mask[::2, ::2] = 1 + hght = array_type(hght, 'meter', mask=mask) + smoothed = smooth_window(hght, np.array([[1, 0, 1], [0, 0, 0], [1, 0, 1]])) truth = array_type([[5640., 5640., 5640., 5640., 5640.], [5684., 5675., 5667.5, 5658.5, 5651.], @@ -725,11 +726,15 @@ def test_smooth_window_1d_dataarray(): def test_smooth_rectangular(array_type): """Test smooth_rectangular with default configuration.""" - hght = array_type([[5640., 5640., 5640., 5640., 5640.], - [5684., 5676., 5666., 5659., 5651.], - [5728., 5712., 5692., 5678., 5662.], - [5772., 5748., 5718., 5697., 5673.], - [5816., 5784., 5744., 5716., 5684.]], 'meter') + hght = np.array([[5640., 5640., 5640., 5640., 5640.], + [5684., 5676., 5666., 5659., 5651.], + [5728., 5712., 5692., 5678., 5662.], + [5772., 5748., 5718., 5697., 5673.], + [5816., 5784., 5744., 5716., 5684.]]) + mask = np.zeros_like(hght) + mask[::2, ::2] = 1 + hght = array_type(hght, 'meter', mask=mask) + smoothed = smooth_rectangular(hght, (5, 3)) truth = array_type([[5640., 5640., 5640., 5640., 5640.], [5684., 5676., 5666., 5659., 5651.], @@ -741,11 +746,15 @@ def test_smooth_rectangular(array_type): def test_smooth_circular(array_type): """Test smooth_circular with default configuration.""" - hght = array_type([[5640., 5640., 5640., 5640., 5640.], - [5684., 5676., 5666., 5659., 5651.], - [5728., 5712., 5692., 5678., 5662.], - [5772., 5748., 5718., 5697., 5673.], - [5816., 5784., 5744., 5716., 5684.]], 'meter') + hght = np.array([[5640., 5640., 5640., 5640., 5640.], + [5684., 5676., 5666., 5659., 5651.], + [5728., 5712., 5692., 5678., 5662.], + [5772., 5748., 5718., 5697., 5673.], + [5816., 5784., 5744., 5716., 5684.]]) + mask = np.zeros_like(hght) + mask[::2, ::2] = 1 + hght = array_type(hght, 'meter', mask=mask) + smoothed = smooth_circular(hght, 2, 2) truth = array_type([[5640., 5640., 5640., 5640., 5640.], [5684., 5676., 5666., 5659., 5651.],