diff --git a/pyproject.toml b/pyproject.toml index 7ad5b26abc0..94625befd06 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -103,7 +103,10 @@ combine_as_imports = true combine_star = true [tool.pytest.ini_options] -markers = "xfail_dask: marks tests as expected to fail with Dask arrays" +markers = [ + "xfail_dask: marks tests as expected to fail with Dask arrays", + "network: make tests requiring access to the internet", +] norecursedirs = "build docs .idea" doctest_optionflags = "NORMALIZE_WHITESPACE" mpl-results-path = "test_output" diff --git a/tests/calc/test_basic.py b/tests/calc/test_basic.py index dc8770ef4d7..aaf6e4a714c 100644 --- a/tests/calc/test_basic.py +++ b/tests/calc/test_basic.py @@ -814,6 +814,7 @@ def test_altimeter_to_sea_level_pressure_hpa(array_type): assert_array_almost_equal(res, truth, 3) +@pytest.mark.network def test_zoom_xarray(): """Test zoom_xarray on 2D DataArray.""" data = xr.open_dataset(get_test_data('GFS_test.nc', False)) diff --git a/tests/calc/test_calc_tools.py b/tests/calc/test_calc_tools.py index c2a071bc53c..ba53cb9260c 100644 --- a/tests/calc/test_calc_tools.py +++ b/tests/calc/test_calc_tools.py @@ -1306,6 +1306,7 @@ def test_remove_nans(): assert_almost_equal(y_expected, y_test, 0) +@pytest.mark.network @pytest.mark.parametrize('subset', (False, True)) @pytest.mark.parametrize('datafile, assign_lat_lon, no_crs, transpose', [('GFS_test.nc', False, False, False), @@ -1583,6 +1584,7 @@ def test_peak_persistence_minima(peak_data): assert per == [((2, 3), np.inf), ((3, 0), 2)] +@pytest.mark.network def test_find_peaks(peak_data): """Test find_peaks correctly identifies peaks.""" data = xr.open_dataset(get_test_data('GFS_test.nc', as_file_obj=False)) @@ -1596,6 +1598,7 @@ def test_find_peaks(peak_data): assert_array_almost_equal(hgt.metpy.x[xind], [3.665191, 5.235988], 6) +@pytest.mark.network def test_find_peaks_minima(peak_data): """Test find_peaks correctly identifies peaks.""" data = xr.open_dataset(get_test_data('GFS_test.nc', as_file_obj=False)) diff --git a/tests/calc/test_cross_sections.py b/tests/calc/test_cross_sections.py index 03dc4f865c8..dbf65e716aa 100644 --- a/tests/calc/test_cross_sections.py +++ b/tests/calc/test_cross_sections.py @@ -323,6 +323,7 @@ def test_absolute_momentum_given_xy(test_cross_xy): assert_xarray_allclose(momentum, true_momentum) +@pytest.mark.network def test_absolute_momentum_xarray_units_attr(): """Test absolute momentum when `u` and `v` are DataArrays with a `units` attribute.""" data = xr.open_dataset(get_test_data('narr_example.nc', False)) diff --git a/tests/calc/test_indices.py b/tests/calc/test_indices.py index 19430da646d..d775f3db47e 100644 --- a/tests/calc/test_indices.py +++ b/tests/calc/test_indices.py @@ -16,6 +16,7 @@ from metpy.units import concatenate, units +@pytest.mark.network def test_precipitable_water(): """Test precipitable water with observed sounding.""" data = get_upper_air_data(datetime(2016, 5, 22, 0), 'DDC') @@ -24,6 +25,7 @@ def test_precipitable_water(): assert_array_almost_equal(pw, truth, 4) +@pytest.mark.network def test_precipitable_water_no_bounds(): """Test precipitable water with observed sounding and no bounds given.""" data = get_upper_air_data(datetime(2016, 5, 22, 0), 'DDC') @@ -83,6 +85,7 @@ def test_precipitable_water_descriptive_bound_error(): precipitable_water(pressure, dewpoint, bottom=units.Quantity(999, 'hPa')) +@pytest.mark.network def test_mean_pressure_weighted(): """Test pressure-weighted mean wind function with vertical interpolation.""" data = get_upper_air_data(datetime(2016, 5, 22, 0), 'DDC') @@ -95,6 +98,7 @@ def test_mean_pressure_weighted(): assert_almost_equal(v, 7.966031839967931 * units('m/s'), 7) +@pytest.mark.network def test_mean_pressure_weighted_temperature(): """Test pressure-weighted mean temperature function with vertical interpolation.""" data = get_upper_air_data(datetime(2016, 5, 22, 0), 'DDC') @@ -105,6 +109,7 @@ def test_mean_pressure_weighted_temperature(): assert_almost_equal(t, 281.535035296836 * units('kelvin'), 7) +@pytest.mark.network def test_mean_pressure_weighted_elevated(): """Test pressure-weighted mean wind function with a base above the surface.""" data = get_upper_air_data(datetime(2016, 5, 22, 0), 'DDC') @@ -118,6 +123,7 @@ def test_mean_pressure_weighted_elevated(): assert_almost_equal(v, 1.7392601775853547 * units('m/s'), 7) +@pytest.mark.network def test_weighted_continuous_average(): """Test pressure-weighted mean wind function with vertical interpolation.""" data = get_upper_air_data(datetime(2016, 5, 22, 0), 'DDC') @@ -130,6 +136,7 @@ def test_weighted_continuous_average(): assert_almost_equal(v, 6.900543760612305 * units('m/s'), 7) +@pytest.mark.network def test_weighted_continuous_average_temperature(): """Test pressure-weighted mean temperature function with vertical interpolation.""" data = get_upper_air_data(datetime(2016, 5, 22, 0), 'DDC') @@ -140,6 +147,7 @@ def test_weighted_continuous_average_temperature(): assert_almost_equal(t, 279.07450928270185 * units('kelvin'), 7) +@pytest.mark.network def test_weighted_continuous_average_elevated(): """Test pressure-weighted mean wind function with a base above the surface.""" data = get_upper_air_data(datetime(2016, 5, 22, 0), 'DDC') @@ -153,6 +161,7 @@ def test_weighted_continuous_average_elevated(): assert_almost_equal(v, 1.616638856115755 * units('m/s'), 7) +@pytest.mark.network def test_precipitable_water_xarray(): """Test precipitable water with xarray input.""" data = get_upper_air_data(datetime(2016, 5, 22, 0), 'DDC') @@ -163,6 +172,7 @@ def test_precipitable_water_xarray(): assert_almost_equal(pw, truth) +@pytest.mark.network def test_bunkers_motion(): """Test Bunkers storm motion with observed sounding.""" data = get_upper_air_data(datetime(2016, 5, 22, 0), 'DDC') @@ -174,6 +184,7 @@ def test_bunkers_motion(): assert_almost_equal(motion.flatten(), truth, 8) +@pytest.mark.network def test_corfidi_motion(): """Test corfidi MCS motion with observed sounding.""" data = get_upper_air_data(datetime(2016, 5, 22, 0), 'DDC') @@ -184,6 +195,7 @@ def test_corfidi_motion(): assert_almost_equal(motion_full.flatten(), truth_full, 8) +@pytest.mark.network def test_corfidi_motion_override_llj(): """Test corfidi MCS motion with overridden LLJ.""" data = get_upper_air_data(datetime(2016, 5, 22, 0), 'DDC') @@ -204,6 +216,7 @@ def test_corfidi_motion_override_llj(): data['v_wind'], v_llj=10 * units('kt')) +@pytest.mark.network def test_corfidi_corfidi_llj_unaivalable(): """Test corfidi MCS motion where the LLJ is unailable.""" data = get_upper_air_data(datetime(2016, 5, 22, 0), 'DDC') @@ -211,6 +224,7 @@ def test_corfidi_corfidi_llj_unaivalable(): corfidi_storm_motion(data['pressure'][6:], data['u_wind'][6:], data['v_wind'][6:]) +@pytest.mark.network def test_corfidi_corfidi_cloudlayer_trimmed(): """Test corfidi MCS motion where sounding does not include the entire cloud layer.""" data = get_upper_air_data(datetime(2016, 5, 22, 0), 'DDC') @@ -221,6 +235,7 @@ def test_corfidi_corfidi_cloudlayer_trimmed(): assert_almost_equal(motion_no_top.flatten(), truth_no_top, 8) +@pytest.mark.network def test_corfidi_motion_with_nans(): """Test corfidi MCS motion with observed sounding with nans.""" data = get_upper_air_data(datetime(2016, 5, 22, 0), 'DDC') @@ -235,6 +250,7 @@ def test_corfidi_motion_with_nans(): assert_almost_equal(motion_with_nans.flatten(), truth_with_nans, 8) +@pytest.mark.network def test_bunkers_motion_with_nans(): """Test Bunkers storm motion with observed sounding.""" data = get_upper_air_data(datetime(2016, 5, 22, 0), 'DDC') @@ -250,6 +266,7 @@ def test_bunkers_motion_with_nans(): assert_almost_equal(motion.flatten(), truth, 8) +@pytest.mark.network def test_bulk_shear(): """Test bulk shear with observed sounding.""" data = get_upper_air_data(datetime(2016, 5, 22, 0), 'DDC') @@ -261,6 +278,7 @@ def test_bulk_shear(): assert_almost_equal(v.to('knots'), truth[1], 8) +@pytest.mark.network def test_bulk_shear_no_depth(): """Test bulk shear with observed sounding and no depth given. Issue #568.""" data = get_upper_air_data(datetime(2016, 5, 22, 0), 'DDC') @@ -271,6 +289,7 @@ def test_bulk_shear_no_depth(): assert_almost_equal(v.to('knots'), truth[1], 8) +@pytest.mark.network def test_bulk_shear_elevated(): """Test bulk shear with observed sounding and a base above the surface.""" data = get_upper_air_data(datetime(2016, 5, 22, 0), 'DDC') @@ -325,6 +344,7 @@ def test_sigtor_scalar(): assert_almost_equal(sigtor, truth, 6) +@pytest.mark.network def test_critical_angle(): """Test critical angle with observed sounding.""" data = get_upper_air_data(datetime(2016, 5, 22, 0), 'DDC') @@ -335,6 +355,7 @@ def test_critical_angle(): assert_almost_equal(ca, truth, 8) +@pytest.mark.network def test_critical_angle_units(): """Test critical angle with observed sounding and different storm motion units.""" data = get_upper_air_data(datetime(2016, 5, 22, 0), 'DDC') diff --git a/tests/calc/test_kinematics.py b/tests/calc/test_kinematics.py index 65118f27dfa..f14dc042259 100644 --- a/tests/calc/test_kinematics.py +++ b/tests/calc/test_kinematics.py @@ -426,6 +426,7 @@ def test_advection_z_y(): assert_array_equal(a, truth) +@pytest.mark.network def test_advection_4d_vertical(data_4d): """Test 4-d vertical advection with parsed dims.""" data_4d['w'] = -abs(data_4d['u']) @@ -1029,6 +1030,7 @@ def test_potential_vorticity_baroclinic_isobaric_real_data(): assert_almost_equal(pvor, true_pv, 10) +@pytest.mark.network def test_potential_vorticity_baroclinic_4d(data_4d): """Test potential vorticity calculation with latlon+xarray spatial handling.""" theta = potential_temperature(data_4d.pressure, data_4d.temperature) @@ -1421,12 +1423,14 @@ def data_4d(): 2.84689950e-05]]]]) * units('s^-1') +@pytest.mark.network def test_vorticity_4d(data_4d): """Test vorticity on a 4D (time, pressure, y, x) grid.""" vort = vorticity(data_4d.u, data_4d.v) assert_array_almost_equal(vort.data, true_vort4d, 12) +@pytest.mark.network def test_absolute_vorticity_4d(data_4d): """Test absolute_vorticity on a 4D (time, pressure, y, x) grid.""" vort = absolute_vorticity(data_4d.u, data_4d.v) @@ -1435,6 +1439,7 @@ def test_absolute_vorticity_4d(data_4d): assert_array_almost_equal(vort.data, truth, 12) +@pytest.mark.network def test_divergence_4d(data_4d): """Test divergence on a 4D (time, pressure, y, x) grid.""" div = divergence(data_4d.u, data_4d.v) @@ -1478,6 +1483,7 @@ def test_divergence_4d(data_4d): assert_array_almost_equal(div.data, truth, 12) +@pytest.mark.network def test_shearing_deformation_4d(data_4d): """Test shearing_deformation on a 4D (time, pressure, y, x) grid.""" shdef = shearing_deformation(data_4d.u, data_4d.v) @@ -1521,6 +1527,7 @@ def test_shearing_deformation_4d(data_4d): assert_array_almost_equal(shdef.data, truth, 12) +@pytest.mark.network def test_stretching_deformation_4d(data_4d): """Test stretching_deformation on a 4D (time, pressure, y, x) grid.""" stdef = stretching_deformation(data_4d.u, data_4d.v) @@ -1564,6 +1571,7 @@ def test_stretching_deformation_4d(data_4d): assert_array_almost_equal(stdef.data, truth, 10) +@pytest.mark.network def test_total_deformation_4d(data_4d): """Test total_deformation on a 4D (time, pressure, y, x) grid.""" totdef = total_deformation(data_4d.u, data_4d.v) @@ -1607,6 +1615,7 @@ def test_total_deformation_4d(data_4d): assert_array_almost_equal(totdef.data, truth, 12) +@pytest.mark.network def test_frontogenesis_4d(data_4d): """Test frontogenesis on a 4D (time, pressure, y, x) grid.""" theta = potential_temperature(data_4d.pressure, data_4d.temperature) @@ -1657,6 +1666,7 @@ def test_frontogenesis_4d(data_4d): assert_array_almost_equal(frnt.data, truth, 13) +@pytest.mark.network def test_geostrophic_wind_4d(data_4d): """Test geostrophic_wind on a 4D (time, pressure, y, x) grid.""" u_g, v_g = geostrophic_wind(data_4d.height) @@ -1772,6 +1782,7 @@ def test_geostrophic_wind_4d(data_4d): assert_array_almost_equal(v_g.data, v_g_truth, 4) +@pytest.mark.network def test_inertial_advective_wind_4d(data_4d): """Test inertial_advective_wind on a 4D (time, pressure, y, x) grid.""" u_g, v_g = geostrophic_wind(data_4d.height) @@ -1860,6 +1871,7 @@ def test_inertial_advective_wind_4d(data_4d): assert_array_almost_equal(v_i.data, v_i_truth, 4) +@pytest.mark.network def test_q_vector_4d(data_4d): """Test q_vector on a 4D (time, pressure, y, x) grid.""" u_g, v_g = geostrophic_wind(data_4d.height) diff --git a/tests/interpolate/test_grid.py b/tests/interpolate/test_grid.py index 573aad2589b..28983a0d9ce 100644 --- a/tests/interpolate/test_grid.py +++ b/tests/interpolate/test_grid.py @@ -151,6 +151,7 @@ def test_generate_grid_coords(): assert pts.flags['C_CONTIGUOUS'] # need output to be C-contiguous +@pytest.mark.network def test_natural_neighbor_to_grid(test_data, test_grid): r"""Test natural neighbor interpolation to grid function.""" xp, yp, z = test_data @@ -167,6 +168,7 @@ def test_natural_neighbor_to_grid(test_data, test_grid): interp_methods = ['cressman', 'barnes'] +@pytest.mark.network @pytest.mark.parametrize('method', interp_methods) def test_inverse_distance_to_grid(method, test_data, test_grid): r"""Test inverse distance interpolation to grid function.""" @@ -243,6 +245,7 @@ def test_interpolate_to_isosurface(): assert_array_almost_equal(truth, dt_theta) +@pytest.mark.network @pytest.mark.parametrize('assume_units', [None, 'mbar']) @pytest.mark.parametrize('method', interp_methods) @pytest.mark.parametrize('boundary_coords', boundary_types) diff --git a/tests/interpolate/test_points.py b/tests/interpolate/test_points.py index 608938f4d60..70a7bd8c17c 100644 --- a/tests/interpolate/test_points.py +++ b/tests/interpolate/test_points.py @@ -96,6 +96,7 @@ def test_barnes_point(test_data): assert_almost_equal(barnes_point(dists, values, 5762.7), 4.0871824) +@pytest.mark.network def test_natural_neighbor_to_points(test_data, test_points): r"""Test natural neighbor interpolation to grid function.""" xp, yp, z = test_data @@ -109,6 +110,7 @@ def test_natural_neighbor_to_points(test_data, test_points): assert_array_almost_equal(truth, img) +@pytest.mark.network def test_inverse_distance_to_points_invalid(test_data, test_points): """Test that inverse_distance_to_points raises when given an invalid method.""" xp, yp, z = test_data @@ -117,6 +119,7 @@ def test_inverse_distance_to_points_invalid(test_data, test_points): inverse_distance_to_points(obs_points, z, test_points, kind='shouldraise', r=40) +@pytest.mark.network @pytest.mark.parametrize('assume_units', [None, 'mbar']) @pytest.mark.parametrize('method', ['cressman', 'barnes']) def test_inverse_distance_to_points(method, assume_units, test_data, test_points): @@ -138,6 +141,7 @@ def test_inverse_distance_to_points(method, assume_units, test_data, test_points assert_array_almost_equal(truth, img) +@pytest.mark.network def test_interpolate_to_points_invalid(test_data): """Test that interpolate_to_points raises when given an invalid method.""" xp, yp, z = test_data @@ -150,6 +154,7 @@ def test_interpolate_to_points_invalid(test_data): interpolate_to_points(obs_points, z, test_points, interp_type='shouldraise') +@pytest.mark.network @pytest.mark.parametrize('assume_units', [None, 'mbar']) @pytest.mark.parametrize('method', ['natural_neighbor', 'cressman', 'barnes', 'linear', 'nearest', 'rbf', 'cubic']) diff --git a/tests/io/test_gempak.py b/tests/io/test_gempak.py index 62108242e5e..cbcdf6d1d63 100644 --- a/tests/io/test_gempak.py +++ b/tests/io/test_gempak.py @@ -17,6 +17,7 @@ logging.getLogger('metpy.io.gempak').setLevel(logging.ERROR) +@pytest.mark.network @pytest.mark.parametrize('order', ['little', 'big']) def test_byte_swap(order): """"Test byte swapping.""" @@ -29,6 +30,7 @@ def test_byte_swap(order): assert_equal(grid, reference) +@pytest.mark.network @pytest.mark.parametrize('grid_name', ['none', 'diff', 'dec', 'grib']) def test_grid_loading(grid_name): """Test reading grids with different packing.""" @@ -44,6 +46,7 @@ def test_grid_loading(grid_name): assert_allclose(gio, gempak, rtol=1e-6, atol=0) +@pytest.mark.network def test_merged_sounding(): """Test loading a merged sounding. @@ -106,6 +109,7 @@ def test_merged_sounding(): np.testing.assert_allclose(gdtar, ddtar, rtol=1e-10, atol=1e-2) +@pytest.mark.network def test_merged_sounding_no_packing(): """Test loading a merged sounding without data packing.""" gso = GempakSounding(get_test_data('gem_merged_nopack.snd')).snxarray( @@ -135,6 +139,7 @@ def test_merged_sounding_no_packing(): assert_allclose(ghght, dhght, rtol=1e-10, atol=1e-1) +@pytest.mark.network @pytest.mark.parametrize('gem,gio,station', [ ('gem_sigw_hght_unmrg.csv', 'gem_sigw_hght_unmrg.snd', 'TOP'), ('gem_sigw_pres_unmrg.csv', 'gem_sigw_pres_unmrg.snd', 'WAML') @@ -170,6 +175,7 @@ def test_unmerged_sounding(gem, gio, station): assert_allclose(ghght, dhght, rtol=1e-10, atol=1e-1) +@pytest.mark.network def test_unmerged_sigw_pressure_sounding(): """Test loading an unmerged sounding. @@ -201,6 +207,7 @@ def test_unmerged_sigw_pressure_sounding(): assert_allclose(ghght, dhght, rtol=1e-10, atol=1e-1) +@pytest.mark.network def test_climate_surface(): """Test to read a cliamte surface file.""" gsf = GempakSurface(get_test_data('gem_climate.sfc')) @@ -219,6 +226,7 @@ def test_climate_surface(): assert val == pytest.approx(gemsfc[param.upper()]) +@pytest.mark.network def test_standard_surface(): """Test to read a standard surface file.""" skip = ['text', 'spcl'] @@ -240,6 +248,7 @@ def test_standard_surface(): assert val == pytest.approx(gemsfc[param.upper()]) +@pytest.mark.network def test_ship_surface(): """Test to read a ship surface file.""" skip = ['text', 'spcl'] @@ -267,6 +276,7 @@ def test_ship_surface(): assert_allclose(decoded_vals, actual_vals) +@pytest.mark.network @pytest.mark.parametrize('proj_type', ['conical', 'cylindrical', 'azimuthal']) def test_coordinates_creation(proj_type): """Test projections and coordinates.""" @@ -282,6 +292,7 @@ def test_coordinates_creation(proj_type): assert_allclose(decode_lon, true_lon, rtol=1e-6, atol=1e-2) +@pytest.mark.network @pytest.mark.parametrize('proj_type, proj_attrs', [ ('conical', { 'grid_mapping_name': 'lambert_conformal_conic', @@ -313,6 +324,7 @@ def test_metpy_crs_creation(proj_type, proj_attrs): assert y_unit == 'meters' +@pytest.mark.network def test_date_parsing(): """Test parsing of dates with leading zeroes.""" sfc_data = GempakSurface(get_test_data('sfc_obs.gem')) @@ -320,6 +332,7 @@ def test_date_parsing(): assert dat == datetime(2000, 1, 2) +@pytest.mark.network @pytest.mark.parametrize('access_type', ['STID', 'STNM']) def test_surface_access(access_type): """Test for proper surface retrieval with multi-parameter filter.""" @@ -334,6 +347,7 @@ def test_surface_access(access_type): date_time='202109070000') +@pytest.mark.network @pytest.mark.parametrize('text_type,date_time', [ ('text', '202109070000'), ('spcl', '202109071600') ]) @@ -349,6 +363,7 @@ def test_surface_text(text_type, date_time): assert text == gem_text +@pytest.mark.network @pytest.mark.parametrize('access_type', ['STID', 'STNM']) def test_sounding_access(access_type): """Test for proper sounding retrieval with multi-parameter filter.""" @@ -363,6 +378,7 @@ def test_sounding_access(access_type): date_time='202101200000') +@pytest.mark.network @pytest.mark.parametrize('text_type', ['txta', 'txtb', 'txtc', 'txpb']) def test_sounding_text(text_type): """Test for proper decoding of coded message text.""" @@ -377,6 +393,7 @@ def test_sounding_text(text_type): assert text == gem_text +@pytest.mark.network def test_special_surface_observation(): """Test special surface observation conversion.""" sfc = get_test_data('gem_surface_with_text.sfc') @@ -400,6 +417,7 @@ def test_special_surface_observation(): assert stn['vsby'] == 2 +@pytest.mark.network def test_multi_level_multi_time_access(): """Test accessing data with multiple levels and times.""" g = get_test_data('gem_multilevel_multidate.grd') @@ -416,6 +434,7 @@ def test_multi_level_multi_time_access(): ) +@pytest.mark.network def test_multi_time_grid(): """Test files with multiple times on a single grid.""" g = get_test_data('gem_multi_time.grd') @@ -429,6 +448,7 @@ def test_multi_time_grid(): assert dattim2 == datetime(1991, 8, 20, 0, 0) +@pytest.mark.network def test_unmerged_no_ttcc(): """Test loading an unmerged sounding. diff --git a/tests/io/test_gini.py b/tests/io/test_gini.py index bb8e3abc9bb..5c6f849b533 100644 --- a/tests/io/test_gini.py +++ b/tests/io/test_gini.py @@ -53,6 +53,7 @@ ] +@pytest.mark.network @pytest.mark.parametrize('filename,pdb,pdb2,proj_info', raw_gini_info, ids=['LCC', 'Stereographic', 'Mercator']) def test_raw_gini(filename, pdb, pdb2, proj_info): @@ -64,6 +65,7 @@ def test_raw_gini(filename, pdb, pdb2, proj_info): assert f.data.shape == (pdb.num_records, pdb.record_len) +@pytest.mark.network def test_gini_bad_size(): """Test reading a GINI file that reports a bad header size.""" f = GiniFile(get_test_data('NHEM-MULTICOMP_1km_IR_20151208_2100.gini')) @@ -95,6 +97,7 @@ def test_gini_bad_size(): ] +@pytest.mark.network @pytest.mark.parametrize('filename,bounds,data_var,proj_attrs,image,dt', gini_dataset_info, ids=['LCC', 'Stereographic', 'Mercator']) def test_gini_xarray(filename, bounds, data_var, proj_attrs, image, dt): @@ -132,6 +135,7 @@ def test_gini_xarray(filename, bounds, data_var, proj_attrs, image, dt): assert np.asarray(dt, dtype='datetime64[ms]') == ds.variables['time'] +@pytest.mark.network @pytest.mark.parametrize('filename,bounds,data_var,proj_attrs,image,dt', gini_dataset_info, ids=['LCC', 'Stereographic', 'Mercator']) @pytest.mark.parametrize('specify_engine', [True, False], ids=['engine', 'no engine']) @@ -168,6 +172,7 @@ def test_gini_xarray_entrypoint(filename, bounds, data_var, proj_attrs, image, d assert np.asarray(dt, dtype='datetime64[ms]') == ds.variables['time'] +@pytest.mark.network def test_gini_mercator_upper_corner(): """Test that the upper corner of the Mercator coordinates is correct.""" f = GiniFile(get_test_data('HI-REGIONAL_4km_3.9_20160616_1715.gini')) @@ -181,6 +186,7 @@ def test_gini_mercator_upper_corner(): assert_almost_equal(lat[0, -1] + (lat[0, -1] - lat[1, -1]), f.proj_info.la2, 4) +@pytest.mark.network def test_gini_str(): """Test the str representation of GiniFile.""" f = GiniFile(get_test_data('WEST-CONUS_4km_WV_20151208_2200.gini')) @@ -191,6 +197,7 @@ def test_gini_str(): assert str(f) == truth +@pytest.mark.network def test_gini_pathlib(): """Test that GiniFile works with `pathlib.Path` instances.""" from pathlib import Path @@ -199,6 +206,7 @@ def test_gini_pathlib(): assert f.prod_desc.sector_id == 'West CONUS' +@pytest.mark.network def test_unidata_composite(): """Test reading radar composites in GINI format made by Unidata.""" f = GiniFile(get_test_data('Level3_Composite_dhr_1km_20180309_2225.gini')) @@ -210,6 +218,7 @@ def test_unidata_composite(): assert f.data[2160, 2130] == 66 +@pytest.mark.network def test_percent_normal(): """Test reading PCT products properly.""" f = GiniFile(get_test_data('PR-NATIONAL_1km_PCT_20200320_0446.gini')) diff --git a/tests/io/test_metar.py b/tests/io/test_metar.py index e5ca99abb9d..4c4be021bc3 100644 --- a/tests/io/test_metar.py +++ b/tests/io/test_metar.py @@ -15,6 +15,7 @@ from metpy.units import is_quantity, units +@pytest.mark.network @pytest.mark.parametrize(['metar', 'truth'], [ # Missing station ('METAR KLBG 261155Z AUTO 00000KT 10SM CLR 05/00 A3001 RMK AO2=', @@ -208,6 +209,7 @@ def test_metar_parser(metar, truth): assert parse_metar(metar, 2017, 5) == truth +@pytest.mark.network def test_date_time_given(): """Test for when date_time is given.""" df = parse_metar_to_dataframe('K6B0 261200Z AUTO 00000KT 10SM CLR 20/M17 A3002 RMK AO2 ' @@ -227,6 +229,7 @@ def test_parse_metar_df_positional_datetime_failure(): 'A3002 RMK AO2 T01990165=', 2019, 6) +@pytest.mark.network def test_parse_metar_to_dataframe(): """Test parsing a single METAR to a DataFrame.""" df = parse_metar_to_dataframe('KDEN 012153Z 09010KT 10SM FEW060 BKN110 BKN220 27/13 ' @@ -241,6 +244,7 @@ def test_parse_metar_to_dataframe(): assert df.dew_point_temperature.values == 13 +@pytest.mark.network def test_parse_file(): """Test the parser on an entire file.""" input_file = get_test_data('metar_20190701_1200.txt', as_file_obj=False) @@ -296,6 +300,7 @@ def test_parse_file(): assert_almost_equal(paku.altimeter.values, [30.02, 30.04]) +@pytest.mark.network def test_parse_file_positional_datetime_failure(): """Test that positional year, month arguments fail for parse_metar_file.""" # pylint: disable=too-many-function-args @@ -304,6 +309,7 @@ def test_parse_file_positional_datetime_failure(): parse_metar_file(input_file, 2016, 12) +@pytest.mark.network def test_parse_file_bad_encoding(): """Test the parser on an entire file that has at least one bad utf-8 encoding.""" input_file = get_test_data('2020010600_sao.wmo', as_file_obj=False) @@ -350,6 +356,7 @@ def test_parse_file_bad_encoding(): assert test.air_pressure_at_sea_level.values == 1024.71 +@pytest.mark.network def test_parse_file_object(): """Test the parser reading from a file-like object.""" input_file = get_test_data('metar_20190701_1200.txt', mode='rt') @@ -364,6 +371,7 @@ def test_parse_file_object(): assert_almost_equal(test.northward_wind.values, 6) +@pytest.mark.network def test_parse_no_pint_objects_in_df(): """Test that there are no Pint quantities in dataframes created by parser.""" input_file = get_test_data('metar_20190701_1200.txt', mode='rt') @@ -401,6 +409,7 @@ def test_repr(): "remarks=TreeNode(text='', offset=47), end=TreeNode(text='', offset=47))") +@pytest.mark.network def test_metar_units_in_place(): """Test that parsing a METAR yields units that can be changed safely in-place.""" df = parse_metar_to_dataframe('KDEN 012153Z 09010KT 10SM FEW060 BKN110 BKN220 27/13 A3010') diff --git a/tests/io/test_nexrad.py b/tests/io/test_nexrad.py index 2ab1bb02eaf..7e67eb5de3e 100644 --- a/tests/io/test_nexrad.py +++ b/tests/io/test_nexrad.py @@ -41,6 +41,7 @@ # ids here fixes how things are presented in pycharm +@pytest.mark.network @pytest.mark.parametrize('fname, voltime, num_sweeps, mom_first, mom_last, expected_logs', level2_files, ids=[i[0].replace('.', '_') for i in level2_files]) def test_level2(fname, voltime, num_sweeps, mom_first, mom_last, expected_logs, caplog): @@ -54,6 +55,7 @@ def test_level2(fname, voltime, num_sweeps, mom_first, mom_last, expected_logs, assert len(caplog.records) == expected_logs +@pytest.mark.network @pytest.mark.parametrize('filename', ['Level2_KFTG_20150430_1419.ar2v', 'TDAL20191021021543V08.raw.gz', 'KTLX20150530_000802_V06.bz2']) @@ -83,6 +85,7 @@ def close(self): Level2File(f) +@pytest.mark.network def test_doubled_file(): """Test for #489 where doubled-up files didn't parse at all.""" with contextlib.closing(get_test_data('Level2_KFTG_20150430_1419.ar2v')) as infile: @@ -92,6 +95,7 @@ def test_doubled_file(): assert len(f.sweeps) == 12 +@pytest.mark.network @pytest.mark.parametrize('fname, has_v2', [('KTLX20130520_201643_V06.gz', False), ('Level2_KFTG_20150430_1419.ar2v', True), ('TDAL20191021021543V08.raw.gz', False)]) @@ -101,6 +105,7 @@ def test_conditional_radconst(fname, has_v2): assert hasattr(f.sweeps[0][0][3], 'calib_dbz0_v') == has_v2 +@pytest.mark.network def test_msg15(): """Check proper decoding of message type 15.""" f = Level2File(get_test_data('KTLX20130520_201643_V06.gz', as_file_obj=False)) @@ -109,12 +114,14 @@ def test_msg15(): assert f.clutter_filter_map['datetime'] == datetime(2013, 5, 19, 5, 15, 0, 0) +@pytest.mark.network def test_msg18_novcps(): """Check handling of message type 18 with VCP info now spares does not crash.""" f = Level2File(get_test_data('KJKL_20240227_102059', as_file_obj=False)) assert 'VCPAT11' not in f.rda +@pytest.mark.network def test_single_chunk(caplog): """Check that Level2File copes with reading a file containing a single chunk.""" # Need to override the test level set above @@ -129,6 +136,7 @@ def test_single_chunk(caplog): assert 'Unable to read volume header' not in caplog.text +@pytest.mark.network def test_build19_level2_additions(): """Test handling of new additions in Build 19 level2 data.""" f = Level2File(get_test_data('Level2_KDDC_20200823_204121.ar2v')) @@ -164,6 +172,7 @@ def test_level3_files(fname): assert f.filename == fname +@pytest.mark.network def test_basic(): """Test reading one specific NEXRAD NIDS file based on the filename.""" f = Level3File(get_test_data('nids/Level3_FFC_N0Q_20140407_1805.nids', as_file_obj=False)) @@ -178,6 +187,7 @@ def test_basic(): assert str(f) +@pytest.mark.network def test_new_gsm(): """Test parsing recent mods to the GSM product.""" f = Level3File(get_test_data('nids/KDDC-gsm.nids')) @@ -194,6 +204,7 @@ def test_new_gsm(): assert str(f) +@pytest.mark.network def test_bad_length(caplog): """Test reading a product with too many bytes produces a log message.""" fname = get_test_data('nids/KOUN_SDUS84_DAATLX_201305202016', as_file_obj=False) @@ -207,23 +218,27 @@ def test_bad_length(caplog): assert 'This product may not parse correctly' in caplog.records[0].message +@pytest.mark.network def test_tdwr(): """Test reading a specific TDWR file.""" f = Level3File(get_test_data('nids/Level3_SLC_TV0_20160516_2359.nids')) assert f.prod_desc.prod_code == 182 +@pytest.mark.network def test_dhr(): """Test reading a time field for DHR product.""" f = Level3File(get_test_data('nids/KOUN_SDUS54_DHRTLX_201305202016')) assert f.metadata['avg_time'] == datetime(2013, 5, 20, 20, 18) +@pytest.mark.network def test_fobj(): """Test reading a specific NEXRAD NIDS files from a file object.""" Level3File(get_test_data('nids/Level3_FFC_N0Q_20140407_1805.nids')) +@pytest.mark.network def test_level3_pathlib(): """Test that reading with Level3File properly sets the filename from a Path.""" fname = Path(get_test_data('nids/Level3_FFC_N0Q_20140407_1805.nids', as_file_obj=False)) @@ -231,6 +246,7 @@ def test_level3_pathlib(): assert f.filename == str(fname) +@pytest.mark.network def test_nids_super_res_width(): """Test decoding a super resolution spectrum width product.""" f = Level3File(get_test_data('nids/KLZK_H0W_20200812_1305')) @@ -238,6 +254,7 @@ def test_nids_super_res_width(): assert np.nanmax(width) == 15 +@pytest.mark.network def test_power_removed_control(): """Test decoding new PRC product.""" f = Level3File(get_test_data('nids/KGJX_NXF_20200817_0600.nids')) @@ -265,6 +282,7 @@ def test31_clear_air(): assert not is_precip_mode(31), 'VCP 31 is not precip' +@pytest.mark.network def test_tracks(): """Check that tracks are properly decoded.""" f = Level3File(get_test_data('nids/KOUN_SDUS34_NSTTLX_201305202016')) @@ -275,6 +293,7 @@ def test_tracks(): assert len(y) +@pytest.mark.network def test_vector_packet(): """Check that vector packets are properly decoded.""" f = Level3File(get_test_data('nids/KOUN_SDUS64_NHITLX_201305202016')) @@ -288,6 +307,7 @@ def test_vector_packet(): assert len(y2) +@pytest.mark.network @pytest.mark.parametrize('fname,truth', [('nids/KEAX_N0Q_20200817_0401.nids', (0, 'MRLE scan')), ('nids/KEAX_N0Q_20200817_0405.nids', (0, 'Non-supplemental scan')), diff --git a/tests/io/test_station_data.py b/tests/io/test_station_data.py index 48f42090d09..8110d4e92aa 100644 --- a/tests/io/test_station_data.py +++ b/tests/io/test_station_data.py @@ -10,6 +10,7 @@ from metpy.io import add_station_lat_lon, station_info +@pytest.mark.network def test_add_lat_lon_station_data(): """Test for when the METAR does not correspond to a station in the dictionary.""" df = pd.DataFrame({'station': ['KOUN', 'KVPZ', 'KDEN', 'PAAA']}) @@ -26,6 +27,7 @@ def test_add_lat_lon_station_data(): assert df['longitude'].dtype == np.float64 +@pytest.mark.network def test_add_lat_lon_station_data_optional(): """Test for when only one argument is passed.""" df = pd.DataFrame({'station': ['KOUN', 'KVPZ', 'KDEN', 'PAAA']}) @@ -51,16 +53,19 @@ def test_add_lat_lon_station_data_existing_col(): add_station_lat_lon(df) +@pytest.mark.network def test_station_lookup_get_station(): """Test that you can get a station by ID from the lookup.""" assert station_info['KOUN'].id == 'KOUN' +@pytest.mark.network def test_station_lookup_len(): """Test that you can get the length of the station data.""" assert len(station_info) == 13798 +@pytest.mark.network def test_station_lookup_iter(): """Test iterating over the station data.""" for stid in station_info: diff --git a/tests/io/test_text.py b/tests/io/test_text.py index 14b6ee5c03f..7c492037b4d 100644 --- a/tests/io/test_text.py +++ b/tests/io/test_text.py @@ -5,12 +5,14 @@ from datetime import datetime import numpy as np +import pytest from metpy.cbook import get_test_data from metpy.io import parse_wpc_surface_bulletin from metpy.testing import needs_module +@pytest.mark.network @needs_module('shapely') def test_parse_wpc_surface_bulletin_highres(): """Test parser reading a high res WPC coded surface bulletin into a dataframe.""" @@ -37,6 +39,7 @@ def test_parse_wpc_surface_bulletin_highres(): assert all(df.valid == datetime(2021, 6, 28, 18, 0, 0)) +@pytest.mark.network @needs_module('shapely') def test_parse_wpc_surface_bulletin(): """Test parser reading a low res WPC coded surface bulletin into a dataframe.""" diff --git a/tests/plots/test_declarative.py b/tests/plots/test_declarative.py index 93a43c2c8f0..177332a86c7 100644 --- a/tests/plots/test_declarative.py +++ b/tests/plots/test_declarative.py @@ -26,6 +26,7 @@ from metpy.units import units +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=True, tolerance=2.58 if version_check('matplotlib<3.10') else 0.0081) @needs_cartopy @@ -50,6 +51,7 @@ def test_declarative_image(): return pc.figure +@pytest.mark.network @needs_cartopy def test_declarative_three_dims_error(): """Test making an image plot with three dimensions.""" @@ -70,6 +72,7 @@ def test_declarative_three_dims_error(): pc.draw() +@pytest.mark.network @needs_cartopy def test_declarative_four_dims_error(): """Test making a contour plot with four dimensions.""" @@ -94,6 +97,7 @@ def test_declarative_four_dims_error(): pc.draw() +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.163 if version_check('cartopy<0.23') else 0.09) @needs_cartopy @@ -123,6 +127,7 @@ def test_declarative_contour(): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=False, tolerance=0.094) @needs_cartopy def test_declarative_titles(): @@ -154,6 +159,7 @@ def test_declarative_titles(): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.159 if version_check('cartopy<0.23') else 0.066) @needs_cartopy @@ -184,6 +190,7 @@ def test_declarative_smooth_contour(): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.155 if version_check('cartopy<0.23') else 0.006) @needs_cartopy @@ -227,6 +234,7 @@ def test_declarative_smooth_contour_calculation(): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.142 if version_check('cartopy<0.23') else 0.0038) @needs_cartopy @@ -257,6 +265,7 @@ def test_declarative_smooth_contour_order(): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.114 if version_check('cartopy<0.23') else 0.058) @needs_cartopy @@ -286,6 +295,7 @@ def test_declarative_figsize(): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.104 if version_check('cartopy<0.23') else 0.033) @needs_cartopy @@ -316,6 +326,7 @@ def test_declarative_smooth_field(): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.828) @needs_cartopy def test_declarative_contour_cam(): @@ -343,6 +354,7 @@ def test_declarative_contour_cam(): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare( remove_text=True, tolerance=3.71 if version_check('matplotlib<3.8') else 0.74) @@ -375,6 +387,7 @@ def test_declarative_contour_options(): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.152 if version_check('cartopy<0.23') else 0.009) @needs_cartopy @@ -406,6 +419,7 @@ def test_declarative_layers_plot_options(): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.055 if version_check('cartopy<0.23') else 0.009) @needs_cartopy @@ -440,6 +454,7 @@ def test_declarative_additional_layers_plot_options(): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare( remove_text=True, tolerance=( @@ -474,6 +489,7 @@ def test_declarative_contour_convert_units(): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=True, tolerance=5.34 if version_check('matplotlib<3.10') else 0.246) @needs_cartopy @@ -518,6 +534,7 @@ def test_declarative_events(): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.009) @needs_cartopy def test_declarative_raster_events(): @@ -549,6 +566,7 @@ def test_declarative_raster_events(): return pc.figure +@pytest.mark.network def test_no_field_error(): """Make sure we get a useful error when the field is not set.""" data = xr.open_dataset(get_test_data('narr_example.nc', as_file_obj=False)) @@ -561,6 +579,7 @@ def test_no_field_error(): contour.draw() +@pytest.mark.network def test_ndim_error_scalar(cfeature): """Make sure we get a useful error when the field is not set.""" data = xr.open_dataset(get_test_data('narr_example.nc', as_file_obj=False)) @@ -585,6 +604,7 @@ def test_ndim_error_scalar(cfeature): plt.close(pc.figure) +@pytest.mark.network def test_ndim_error_vector(cfeature): """Make sure we get a useful error when the field is not set.""" data = xr.open_dataset(get_test_data('narr_example.nc', as_file_obj=False)) @@ -608,6 +628,7 @@ def test_ndim_error_vector(cfeature): plt.close(pc.figure) +@pytest.mark.network def test_no_field_error_barbs(): """Make sure we get a useful error when the field is not set.""" data = xr.open_dataset(get_test_data('narr_example.nc', as_file_obj=False)) @@ -620,6 +641,7 @@ def test_no_field_error_barbs(): barbs.draw() +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.381) def test_projection_object(ccrs, cfeature): """Test that we can pass a custom map projection.""" @@ -643,6 +665,7 @@ def test_projection_object(ccrs, cfeature): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.009) @needs_cartopy def test_colorfill(): @@ -669,6 +692,7 @@ def test_colorfill(): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.238 if version_check('cartopy<0.23') else 0.004) def test_colorfill_with_image_range(cfeature): @@ -696,6 +720,7 @@ def test_colorfill_with_image_range(cfeature): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare( remove_text=True, tolerance=0.238 if version_check('cartopy<0.23') else 0.004, @@ -726,6 +751,7 @@ def test_colorfill_with_normalize_instance_image_range(cfeature): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.02) @needs_cartopy def test_colorfill_horiz_colorbar(): @@ -752,6 +778,7 @@ def test_colorfill_horiz_colorbar(): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.02) def test_colorbar_kwargs(cfeature): """Test that we can use ContourFillPlot with specifying colorbar kwargs.""" @@ -777,6 +804,7 @@ def test_colorbar_kwargs(cfeature): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.370 if version_check('cartopy<0.23') else 0.005) def test_colorfill_no_colorbar(cfeature): @@ -803,6 +831,7 @@ def test_colorfill_no_colorbar(cfeature): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=True, tolerance=1.389 if version_check('matplotlib<3.10') else 0.0012) @needs_cartopy @@ -826,6 +855,7 @@ def test_global(): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=True, tolerance=5.101 if version_check('matplotlib<3.10') else 0.019) @needs_cartopy @@ -858,6 +888,7 @@ def test_latlon(): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.393) @needs_cartopy def test_declarative_barb_options(): @@ -888,6 +919,7 @@ def test_declarative_barb_options(): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.37) @needs_cartopy def test_declarative_arrowplot(): @@ -918,6 +950,7 @@ def test_declarative_arrowplot(): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.37) @needs_cartopy def test_declarative_arrowkey(): @@ -949,6 +982,7 @@ def test_declarative_arrowkey(): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.37) @needs_cartopy def test_declarative_arrow_changes(): @@ -980,6 +1014,7 @@ def test_declarative_arrow_changes(): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.891) @needs_cartopy def test_declarative_barb_earth_relative(): @@ -1019,6 +1054,7 @@ def test_declarative_barb_earth_relative(): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.612) @needs_cartopy def test_declarative_overlay_projections(): @@ -1058,6 +1094,7 @@ def test_declarative_overlay_projections(): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.133 if version_check('cartopy<0.23') else 0.0094) @needs_cartopy @@ -1088,6 +1125,7 @@ def test_declarative_gridded_scale(): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.607) @needs_cartopy def test_declarative_global_gfs(): @@ -1117,6 +1155,7 @@ def test_declarative_global_gfs(): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=True, tolerance=1.42) @needs_cartopy def test_declarative_barb_gfs(): @@ -1146,6 +1185,7 @@ def test_declarative_barb_gfs(): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.665) @needs_cartopy def test_declarative_barb_scale(): @@ -1176,6 +1216,7 @@ def test_declarative_barb_scale(): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.722) @needs_cartopy def test_declarative_barb_gfs_knots(): @@ -1226,6 +1267,7 @@ def pandas_sfc(): return df +@pytest.mark.network def test_plotobs_subset_default_nolevel(sample_obs): """Test PlotObs subsetting with minimal config.""" obs = PlotObs() @@ -1238,6 +1280,7 @@ def test_plotobs_subset_default_nolevel(sample_obs): pd.testing.assert_frame_equal(obs.obsdata, truth) +@pytest.mark.network def test_plotobs_subset_level(sample_obs): """Test PlotObs subsetting based on level.""" obs = PlotObs() @@ -1251,6 +1294,7 @@ def test_plotobs_subset_level(sample_obs): pd.testing.assert_frame_equal(obs.obsdata, truth) +@pytest.mark.network def test_plotobs_subset_level_no_units(sample_obs): """Test PlotObs subsetting based on unitless level.""" obs = PlotObs() @@ -1264,6 +1308,7 @@ def test_plotobs_subset_level_no_units(sample_obs): pd.testing.assert_frame_equal(obs.obsdata, truth) +@pytest.mark.network def test_plotobs_subset_time(sample_obs): """Test PlotObs subsetting for a particular time.""" obs = PlotObs() @@ -1277,6 +1322,7 @@ def test_plotobs_subset_time(sample_obs): pd.testing.assert_frame_equal(obs.obsdata, truth) +@pytest.mark.network def test_plotobs_subset_time_window(sample_obs): """Test PlotObs subsetting for a particular time with a window.""" # Test also using an existing index @@ -1296,6 +1342,7 @@ def test_plotobs_subset_time_window(sample_obs): pd.testing.assert_frame_equal(obs.obsdata, truth) +@pytest.mark.network def test_plotobs_subset_time_window_level(sample_obs): """Test PlotObs subsetting for a particular time with a window and a level.""" # Test also using an existing index @@ -1315,6 +1362,7 @@ def test_plotobs_subset_time_window_level(sample_obs): pd.testing.assert_frame_equal(obs.obsdata, truth) +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.016) def test_plotobs_units_with_formatter(ccrs, pandas_sfc): """Test using PlotObs with a field that both has units and a custom formatter.""" @@ -1352,6 +1400,7 @@ def test_plotobs_units_with_formatter(ccrs, pandas_sfc): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.081 if version_check('cartopy<0.23') else 0.025) def test_declarative_sfc_obs(ccrs, pandas_sfc): @@ -1382,6 +1431,7 @@ def test_declarative_sfc_obs(ccrs, pandas_sfc): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.075 if version_check('cartopy<0.23') else 0.) def test_declarative_sfc_obs_args(ccrs, pandas_sfc): @@ -1413,6 +1463,7 @@ def test_declarative_sfc_obs_args(ccrs, pandas_sfc): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.016) @needs_cartopy def test_declarative_sfc_text(pandas_sfc): @@ -1444,6 +1495,7 @@ def test_declarative_sfc_text(pandas_sfc): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.081 if version_check('cartopy<0.23') else 0.) def test_declarative_sfc_obs_changes(ccrs, pandas_sfc): @@ -1478,6 +1530,7 @@ def test_declarative_sfc_obs_changes(ccrs, pandas_sfc): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.171) def test_declarative_colored_barbs(ccrs, pandas_sfc): """Test making a surface plot with a colored barb (gh-1274).""" @@ -1507,6 +1560,7 @@ def test_declarative_colored_barbs(ccrs, pandas_sfc): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.314) def test_declarative_sfc_obs_full(ccrs, pandas_sfc): """Test making a full surface observation plot.""" @@ -1542,6 +1596,7 @@ def test_declarative_sfc_obs_full(ccrs, pandas_sfc): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.522) @needs_cartopy def test_declarative_upa_obs(): @@ -1579,6 +1634,7 @@ def test_declarative_upa_obs(): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.518) @needs_cartopy def test_declarative_upa_obs_convert_barb_units(): @@ -1622,6 +1678,7 @@ def test_declarative_upa_obs_convert_barb_units(): return pc.figure +@pytest.mark.network def test_attribute_error_time(ccrs, pandas_sfc): """Make sure we get a useful error when the time variable is not found.""" pandas_sfc.rename(columns={'valid': 'vtime'}, inplace=True) @@ -1653,6 +1710,7 @@ def test_attribute_error_time(ccrs, pandas_sfc): plt.close(pc.figure) +@pytest.mark.network def test_attribute_error_station(ccrs, pandas_sfc): """Make sure we get a useful error when the station variable is not found.""" pandas_sfc.rename(columns={'station': 'location'}, inplace=True) @@ -1684,6 +1742,7 @@ def test_attribute_error_station(ccrs, pandas_sfc): plt.close(pc.figure) +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.082 if version_check('cartopy<0.23') else 0.) def test_declarative_sfc_obs_change_units(ccrs): @@ -1718,6 +1777,7 @@ def test_declarative_sfc_obs_change_units(ccrs): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.125 if version_check('cartopy<0.23') else 0.0) def test_declarative_multiple_sfc_obs_change_units(ccrs): @@ -1754,6 +1814,7 @@ def test_declarative_multiple_sfc_obs_change_units(ccrs): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=False, tolerance=0.607) @needs_cartopy def test_declarative_title_fontsize(): @@ -1784,6 +1845,7 @@ def test_declarative_title_fontsize(): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=False, tolerance=0.951 if version_check('cartopy<0.23') else 0.) @needs_cartopy @@ -1815,6 +1877,7 @@ def test_declarative_colorbar_fontsize(): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.607) @needs_cartopy def test_declarative_station_plot_fontsize(): @@ -1850,6 +1913,7 @@ def test_declarative_station_plot_fontsize(): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.607) @needs_cartopy def test_declarative_contour_label_fontsize(): @@ -1880,6 +1944,7 @@ def test_declarative_contour_label_fontsize(): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.02) @needs_cartopy def test_declarative_raster(): @@ -1906,6 +1971,7 @@ def test_declarative_raster(): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.02) @needs_cartopy def test_declarative_raster_options(): @@ -1933,6 +1999,7 @@ def test_declarative_raster_options(): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.607) @needs_cartopy def test_declarative_region_modifier_zoom_in(): @@ -1957,6 +2024,7 @@ def test_declarative_region_modifier_zoom_in(): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.377) @needs_cartopy def test_declarative_region_modifier_zoom_out(): @@ -2073,6 +2141,7 @@ def test_copy(): assert obj.plots[i] is not copied_obj.plots[i] +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=False, tolerance=0.607) @needs_cartopy def test_declarative_plot_geometry_polygons(): @@ -2118,6 +2187,7 @@ def test_declarative_plot_geometry_polygons(): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=False, tolerance=2.985) def test_declarative_plot_geometry_lines(ccrs): """Test that `PlotGeometry` correctly plots MultiLineString and LineString objects.""" @@ -2158,6 +2228,7 @@ def test_declarative_plot_geometry_lines(ccrs): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=False, tolerance=0.013) def test_declarative_plot_geometry_fills(ccrs): """Test that `PlotGeometry` correctly plots MultiLineString and LineString objects.""" @@ -2194,6 +2265,7 @@ def test_declarative_plot_geometry_fills(ccrs): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=False, tolerance=1.900) def test_declarative_plot_geometry_points(ccrs): """Test that `PlotGeometry` correctly plots Point and MultiPoint objects.""" @@ -2272,6 +2344,7 @@ def test_attribute_error_no_suggest(): assert 'Perhaps you meant' not in str(excinfo.value) +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=False) @needs_cartopy def test_declarative_plot_surface_analysis_default(): @@ -2301,6 +2374,7 @@ def test_declarative_plot_surface_analysis_default(): return pc.figure +@pytest.mark.network @pytest.mark.mpl_image_compare(remove_text=False) @needs_cartopy def test_declarative_plot_surface_analysis_custom(): diff --git a/tests/plots/test_util.py b/tests/plots/test_util.py index db302cc16ee..258056fdf49 100644 --- a/tests/plots/test_util.py +++ b/tests/plots/test_util.py @@ -50,6 +50,7 @@ def test_add_timestamp_high_contrast(): return fig +@pytest.mark.network def test_add_timestamp_xarray(): """Test that add_timestamp can work with xarray datetime accessor.""" with autoclose_figure() as fig: diff --git a/tests/remote/test_aws.py b/tests/remote/test_aws.py index 5540598a3c5..627c03ff6f5 100644 --- a/tests/remote/test_aws.py +++ b/tests/remote/test_aws.py @@ -6,10 +6,13 @@ from pathlib import Path import tempfile +import pytest + from metpy.remote import GOESArchive, MLWPArchive, NEXRADLevel2Archive, NEXRADLevel3Archive from metpy.testing import needs_aws +@pytest.mark.network @needs_aws def test_nexrad3_single(): """Test getting a single product from the NEXRAD level 3 archive.""" @@ -18,6 +21,7 @@ def test_nexrad3_single(): assert l3.access() +@pytest.mark.network @needs_aws def test_nexrad3_range(): """Test getting a range of products from the NEXRAD level 3 archive.""" @@ -40,6 +44,7 @@ def test_nexrad3_range(): assert (Path(tmpdir) / 'tempprod').exists() +@pytest.mark.network @needs_aws def test_nexrad2_single(): """Test getting a single volume from the NEXRAD level 2 archive.""" @@ -47,6 +52,7 @@ def test_nexrad2_single(): assert l2.name == 'KTLX20130520_201643_V06.gz' +@pytest.mark.network @needs_aws def test_nexrad2_range(): """Test getting a range of products from the NEXRAD level 2 archive.""" @@ -59,6 +65,7 @@ def test_nexrad2_range(): 'KFTG20241214_161349_V06', 'KFTG20241214_162248_V06'] +@pytest.mark.network @needs_aws def test_goes_single(): """Test getting a single product from the GOES archive.""" @@ -70,6 +77,7 @@ def test_goes_single(): '_e20250092356311_c20250092356338.nc') +@pytest.mark.network @needs_aws def test_goes_range(): """Test getting a range of products from the GOES archive.""" @@ -94,6 +102,7 @@ def test_goes_range(): assert names == truth +@pytest.mark.network @needs_aws def test_mlwp_single(): """Test getting a single product from the MLWP archive.""" @@ -102,6 +111,7 @@ def test_mlwp_single(): '2025/0130/GRAP_v100_GFS_2025013012_f000_f240_06.nc') +@pytest.mark.network @needs_aws def test_mlwp_range(): """Test getting a single product from the MLWP archive.""" diff --git a/tests/test_xarray.py b/tests/test_xarray.py index e9fbb3cfdff..5e5e6e9690c 100644 --- a/tests/test_xarray.py +++ b/tests/test_xarray.py @@ -58,6 +58,7 @@ def test_var_multidim_no_xy(test_var_multidim_full): return test_var_multidim_full.drop_vars(['y', 'x']) +@pytest.mark.network def test_projection(test_var, ccrs): """Test getting the proper projection out of the variable.""" crs = test_var.metpy.crs @@ -66,6 +67,7 @@ def test_projection(test_var, ccrs): assert isinstance(test_var.metpy.cartopy_crs, ccrs.LambertConformal) +@pytest.mark.network def test_pyproj_projection(test_var): """Test getting the proper pyproj projection out of the variable.""" proj = test_var.metpy.pyproj_crs @@ -74,6 +76,7 @@ def test_pyproj_projection(test_var): assert proj.coordinate_operation.method_name == 'Lambert Conic Conformal (1SP)' +@pytest.mark.network def test_no_projection(test_ds): """Test getting the crs attribute when not available produces a sensible error.""" var = test_ds.lat @@ -83,6 +86,7 @@ def test_no_projection(test_ds): assert 'not available' in str(exc.value) +@pytest.mark.network def test_globe(test_var, ccrs): """Test getting the globe belonging to the projection.""" globe = test_var.metpy.cartopy_globe @@ -93,6 +97,7 @@ def test_globe(test_var, ccrs): assert isinstance(globe, ccrs.Globe) +@pytest.mark.network def test_geodetic(test_var, ccrs): """Test getting the Geodetic CRS for the projection.""" geodetic = test_var.metpy.cartopy_geodetic @@ -100,6 +105,7 @@ def test_geodetic(test_var, ccrs): assert isinstance(geodetic, ccrs.Geodetic) +@pytest.mark.network def test_unit_array(test_var): """Test unit handling through the accessor.""" arr = test_var.metpy.unit_array @@ -107,11 +113,13 @@ def test_unit_array(test_var): assert arr.units == units.kelvin +@pytest.mark.network def test_units(test_var): """Test the units property on the accessor.""" assert test_var.metpy.units == units.kelvin +@pytest.mark.network def test_units_data(test_var): """Test units property fetching does not touch variable.data.""" with patch.object(xr.Variable, 'data', new_callable=PropertyMock) as mock_data_property: @@ -119,6 +127,7 @@ def test_units_data(test_var): mock_data_property.assert_not_called() +@pytest.mark.network def test_units_percent(): """Test that '%' is handled as 'percent'.""" test_var_percent = xr.open_dataset( @@ -127,6 +136,7 @@ def test_units_percent(): assert test_var_percent.metpy.units == units.percent +@pytest.mark.network def test_magnitude_with_quantity(test_var): """Test magnitude property on accessor when data is a quantity.""" assert isinstance(test_var.metpy.magnitude, np.ndarray) @@ -142,6 +152,7 @@ def test_magnitude_without_quantity(test_ds_generic): ) +@pytest.mark.network def test_convert_units(test_var): """Test conversion of units.""" result = test_var.metpy.convert_units('degC') @@ -154,6 +165,7 @@ def test_convert_units(test_var): assert_almost_equal(result[0, 0, 0, 0], 18.44 * units.degC, 2) +@pytest.mark.network def test_convert_to_base_units(test_ds): """Test conversion of units.""" uwnd = test_ds.u_wind.metpy.quantify() @@ -174,6 +186,7 @@ def test_convert_coordinate_units(test_ds_generic): assert result['b'].metpy.units == units.percent +@pytest.mark.network def test_latlon_default_units(test_var_multidim_full): """Test that lat/lon are given degree units by default.""" del test_var_multidim_full.lat.attrs['units'] @@ -288,12 +301,14 @@ def test_missing_grid_mapping_valid(): ) +@pytest.mark.network def test_missing_grid_mapping_invalid(test_var_multidim_no_xy): """Test not falling back to implicit lat/lon projection when invalid.""" data_var = test_var_multidim_no_xy.to_dataset(name='data').metpy.parse_cf('data') assert 'metpy_crs' not in data_var.coords +@pytest.mark.network def test_xy_not_vertical(test_ds): """Test not detecting x/y as a vertical coordinate based on metadata.""" test_ds.x.attrs['positive'] = 'up' @@ -355,6 +370,7 @@ def func(a, b): assert_array_equal(func(data, b=data2), np.array([1001, 1001, 1001]) * units.m) +@pytest.mark.network def test_coordinates_basic_by_method(test_var): """Test that NARR example coordinates are like we expect using coordinates method.""" x, y, vertical, time = test_var.metpy.coordinates('x', 'y', 'vertical', 'time') @@ -365,6 +381,7 @@ def test_coordinates_basic_by_method(test_var): assert test_var['time'].identical(time) +@pytest.mark.network def test_coordinates_basic_by_property(test_var): """Test that NARR example coordinates are like we expect using properties.""" assert test_var['x'].identical(test_var.metpy.x) @@ -600,6 +617,7 @@ def test_check_axis_regular_expression_match(test_ds_generic, test_tuple): assert check_axis(data[test_tuple[0]], test_tuple[1]) +@pytest.mark.network def test_narr_example_variable_without_grid_mapping(test_ds): """Test that NARR example is parsed correctly, with x/y coordinates scaled the same.""" data = test_ds.metpy.parse_cf() @@ -645,6 +663,7 @@ def add(a, b): add(test_ds_generic['test'], other) +@pytest.mark.network def test_time_deltas(): """Test the time_deltas attribute.""" ds = xr.open_dataset(get_test_data('irma_gfs_example.nc', as_file_obj=False)) @@ -653,21 +672,25 @@ def test_time_deltas(): assert_array_almost_equal(time.metpy.time_deltas, truth) +@pytest.mark.network def test_find_axis_name_integer(test_var): """Test getting axis name using the axis number identifier.""" assert test_var.metpy.find_axis_name(2) == 'y' +@pytest.mark.network def test_find_axis_name_axis_type(test_var): """Test getting axis name using the axis type identifier.""" assert test_var.metpy.find_axis_name('vertical') == 'isobaric' +@pytest.mark.network def test_find_axis_name_dim_coord_name(test_var): """Test getting axis name using the dimension coordinate name identifier.""" assert test_var.metpy.find_axis_name('isobaric') == 'isobaric' +@pytest.mark.network def test_find_axis_name_bad_identifier(test_var): """Test getting axis name using the axis type identifier.""" with pytest.raises(ValueError) as exc: @@ -675,21 +698,25 @@ def test_find_axis_name_bad_identifier(test_var): assert 'axis is not valid' in str(exc.value) +@pytest.mark.network def test_find_axis_number_integer(test_var): """Test getting axis number using the axis number identifier.""" assert test_var.metpy.find_axis_number(2) == 2 +@pytest.mark.network def test_find_axis_number_axis_type(test_var): """Test getting axis number using the axis type identifier.""" assert test_var.metpy.find_axis_number('vertical') == 1 +@pytest.mark.network def test_find_axis_number_dim_coord_number(test_var): """Test getting axis number using the dimension coordinate name identifier.""" assert test_var.metpy.find_axis_number('isobaric') == 1 +@pytest.mark.network def test_find_axis_number_bad_identifier(test_var): """Test getting axis number using the axis type identifier.""" with pytest.raises(ValueError) as exc: @@ -697,12 +724,14 @@ def test_find_axis_number_bad_identifier(test_var): assert 'axis is not valid' in str(exc.value) +@pytest.mark.network def test_data_array_loc_get_with_units(test_var): """Test the .loc indexer on the metpy accessor.""" truth = test_var.loc[:, 850.] assert truth.identical(test_var.metpy.loc[:, 8.5e4 * units.Pa]) +@pytest.mark.network def test_data_array_loc_set_with_units(test_var): """Test the .loc indexer on the metpy accessor for setting.""" temperature = test_var.copy() @@ -711,24 +740,28 @@ def test_data_array_loc_set_with_units(test_var): assert not np.isnan(temperature.loc[:, 700.]).any() +@pytest.mark.network def test_data_array_loc_with_ellipsis(test_var): """Test the .loc indexer using multiple Ellipses to verify expansion behavior.""" truth = test_var[:, :, -1, :] assert truth.identical(test_var.metpy.loc[..., 711.3653535503963 * units.km, ...]) +@pytest.mark.network def test_data_array_loc_non_tuple(test_var): """Test the .loc indexer with a non-tuple indexer.""" truth = test_var[-1] assert truth.identical(test_var.metpy.loc['1987-04-04T18:00']) +@pytest.mark.network def test_data_array_loc_too_many_indices(test_var): """Test the .loc indexer when too many indices are given.""" with pytest.raises(IndexError): test_var.metpy.loc[:, 8.5e4 * units.Pa, :, :, :] +@pytest.mark.network def test_data_array_sel_dict_with_units(test_var): """Test .sel on the metpy accessor with dictionary.""" truth = test_var.squeeze().loc[500.] @@ -736,6 +769,7 @@ def test_data_array_sel_dict_with_units(test_var): 'isobaric': 5e4 * units.Pa})) +@pytest.mark.network def test_data_array_sel_kwargs_with_units(test_var): """Test .sel on the metpy accessor with kwargs and axis type.""" truth = test_var.loc[:, 500.][..., 122] @@ -748,6 +782,7 @@ def test_data_array_sel_kwargs_with_units(test_var): assert truth.identical(selection) +@pytest.mark.network def test_dataset_loc_with_units(test_ds): """Test .loc on the metpy accessor for Datasets using slices.""" truth = test_ds[{'isobaric': slice(6, 17)}] @@ -755,6 +790,7 @@ def test_dataset_loc_with_units(test_ds): 5e4 * units.Pa)}]) +@pytest.mark.network def test_dataset_sel_kwargs_with_units(test_ds): """Test .sel on the metpy accessor for Datasets with kwargs.""" truth = test_ds[{'time': 0, 'y': 50, 'x': 122}] @@ -763,6 +799,7 @@ def test_dataset_sel_kwargs_with_units(test_ds): method='nearest')) +@pytest.mark.network def test_dataset_sel_non_dict_pos_arg(test_ds): """Test that .sel errors when first positional argument is not a dict.""" with pytest.raises(ValueError) as exc: @@ -770,6 +807,7 @@ def test_dataset_sel_non_dict_pos_arg(test_ds): assert 'must be a dictionary' in str(exc.value) +@pytest.mark.network def test_dataset_sel_mixed_dict_and_kwarg(test_ds): """Test that .sel errors when dict positional argument and kwargs are mixed.""" with pytest.raises(ValueError) as exc: @@ -778,12 +816,14 @@ def test_dataset_sel_mixed_dict_and_kwarg(test_ds): assert 'cannot specify both keyword and positional arguments' in str(exc.value) +@pytest.mark.network def test_dataset_loc_without_dict(test_ds): """Test that .metpy.loc for Datasets raises error when used with a non-dict.""" with pytest.raises(TypeError): test_ds.metpy.loc[:, 700 * units.hPa] +@pytest.mark.network def test_dataset_parse_cf_keep_attrs(test_ds): """Test that .parse_cf() does not remove attributes on the parsed dataset.""" parsed_ds = test_ds.metpy.parse_cf() @@ -799,6 +839,7 @@ def test_check_axis_with_bad_unit(test_ds_generic): assert not check_axis(var, 'x', 'y', 'vertical', 'time') +@pytest.mark.network def test_dataset_parse_cf_varname_list(test_ds): """Test that .parse_cf() returns correct subset of dataset when given list of vars.""" full_ds = test_ds.copy().metpy.parse_cf() @@ -854,6 +895,7 @@ def test_one_dimensional_lat_lon(test_ds_generic): assert var['e'].identical(var.metpy.longitude) +@pytest.mark.network def test_auxilary_lat_lon_with_xy(test_var_multidim_full): """Test that auxiliary lat/lon coord identification works with other x/y coords present.""" assert test_var_multidim_full['y'].identical(test_var_multidim_full.metpy.y) @@ -862,12 +904,14 @@ def test_auxilary_lat_lon_with_xy(test_var_multidim_full): assert test_var_multidim_full['lon'].identical(test_var_multidim_full.metpy.longitude) +@pytest.mark.network def test_auxilary_lat_lon_without_xy(test_var_multidim_no_xy): """Test that multidimensional lat/lon are recognized in absence of x/y coords.""" assert test_var_multidim_no_xy['lat'].identical(test_var_multidim_no_xy.metpy.latitude) assert test_var_multidim_no_xy['lon'].identical(test_var_multidim_no_xy.metpy.longitude) +@pytest.mark.network def test_auxilary_lat_lon_without_xy_as_xy(test_var_multidim_no_xy): """Test that the pre-v1.0 behavior of multidimensional lat/lon errors.""" with pytest.raises(AttributeError):