[Git][debian-gis-team/metpy][master] 5 commits: Minor changes
Antonio Valentino (@antonio.valentino)
gitlab at salsa.debian.org
Sun Jan 18 17:42:13 GMT 2026
Antonio Valentino pushed to branch master at Debian GIS Project / metpy
Commits:
ef02cfe7 by Antonio Valentino at 2026-01-17T18:21:18+00:00
Minor changes
- - - - -
73c3fed4 by Antonio Valentino at 2026-01-18T17:26:31+00:00
New 0002-No-network.patch
- - - - -
573455e0 by Antonio Valentino at 2026-01-18T17:26:38+00:00
New 0003-xarray-v2025.12.0-compat.patch
- - - - -
20e10f6e by Antonio Valentino at 2026-01-18T17:36:28+00:00
Update dates in d/copyright
- - - - -
2fdd0a2f by Antonio Valentino at 2026-01-18T17:36:28+00:00
Set distribution to unstable
- - - - -
6 changed files:
- debian/changelog
- debian/copyright
- + debian/patches/0002-No-network.patch
- + debian/patches/0003-xarray-v2025.12.0-compat.patch
- debian/patches/series
- debian/rules
Changes:
=====================================
debian/changelog
=====================================
@@ -1,13 +1,21 @@
-metpy (1.7.1+ds-2) UNRELEASED; urgency=medium
+metpy (1.7.1+ds-2) unstable; urgency=medium
- * Team upload.
+ [ Bas Couwenberg ]
* Update lintian overrides.
* Drop Rules-Requires-Root: no, default since dpkg 1.22.13.
* Use test-build-validate-cleanup instead of test-build-twice.
* Drop Priority: optional, default since dpkg 1.22.13.
* Bump Standards-Version to 4.7.3, changes: priority.
- -- Bas Couwenberg <sebastic at debian.org> Fri, 12 Sep 2025 17:40:45 +0200
+ [ Antonio Valentino ]
+ * debian/patches:
+ - New 0002-No-network.patch.
+ - New 0003-xarray-v2025.12.0-compat.patch.
+ * debian/rules:
+ - Use merkers to skip network tests.
+ * Update dates in d/copyright.
+
+ -- Antonio Valentino <antonio.valentino at tiscali.it> Sun, 18 Jan 2026 17:27:28 +0000
metpy (1.7.1+ds-1) unstable; urgency=medium
=====================================
debian/copyright
=====================================
@@ -17,7 +17,7 @@ License: BSD-3-Clause
Files: src/metpy/_vendor/xarray.py
Copyright: 2019, MetPy Developers.
-License: BSD-3-clause and Apache-2.0
+License: BSD-3-Clause and Apache-2.0
All rights reserved.
.
Redistribution and use in source and binary forms, with or without
@@ -71,7 +71,7 @@ Copyright: NONE
License: CC-BY-4.0
Files: debian/*
-Copyright: 2023-2025, Antonio Valentino <antonio.valentino at tiscali.it>
+Copyright: 2023-2026, Antonio Valentino <antonio.valentino at tiscali.it>
License: BSD-3-Clause
License: BSD-3-Clause
=====================================
debian/patches/0002-No-network.patch
=====================================
@@ -0,0 +1,1991 @@
+From: Antonio Valentino <antonio.valentino at tiscali.it>
+Date: Sun, 18 Jan 2026 12:06:23 +0000
+Subject: No network
+
+Makr all tests requiring access to the network
+
+Forwarded: https://github.com/Unidata/MetPy/pull/3985
+---
+ pyproject.toml | 5 ++-
+ tests/calc/test_basic.py | 1 +
+ tests/calc/test_calc_tools.py | 3 ++
+ tests/calc/test_cross_sections.py | 1 +
+ tests/calc/test_indices.py | 21 +++++++++++
+ tests/calc/test_kinematics.py | 12 +++++++
+ tests/interpolate/test_grid.py | 3 ++
+ tests/interpolate/test_points.py | 5 +++
+ tests/io/test_gempak.py | 20 +++++++++++
+ tests/io/test_gini.py | 9 +++++
+ tests/io/test_metar.py | 9 +++++
+ tests/io/test_nexrad.py | 20 +++++++++++
+ tests/io/test_station_data.py | 5 +++
+ tests/io/test_text.py | 4 +++
+ tests/plots/test_declarative.py | 74 +++++++++++++++++++++++++++++++++++++++
+ tests/plots/test_util.py | 1 +
+ tests/remote/test_aws.py | 10 ++++++
+ tests/test_xarray.py | 44 +++++++++++++++++++++++
+ 18 files changed, 246 insertions(+), 1 deletion(-)
+
+diff --git a/pyproject.toml b/pyproject.toml
+index c621ae0..2369170 100644
+--- a/pyproject.toml
++++ b/pyproject.toml
+@@ -102,7 +102,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 teh 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 dc8770e..aaf6e4a 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)
+
+
++ at 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 c2a071b..ba53cb9 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)
+
+
++ at 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)]
+
+
++ at 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)
+
+
++ at 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 03dc4f8..dbf65e7 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)
+
+
++ at 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 d849174..1c90400 100644
+--- a/tests/calc/test_indices.py
++++ b/tests/calc/test_indices.py
+@@ -17,6 +17,7 @@ from metpy.testing import (assert_almost_equal, assert_array_almost_equal, get_u
+ from metpy.units import concatenate, units
+
+
++ at pytest.mark.network
+ def test_precipitable_water():
+ """Test precipitable water with observed sounding."""
+ data = get_upper_air_data(datetime(2016, 5, 22, 0), 'DDC')
+@@ -25,6 +26,7 @@ def test_precipitable_water():
+ assert_array_almost_equal(pw, truth, 4)
+
+
++ at 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')
+@@ -84,6 +86,7 @@ def test_precipitable_water_descriptive_bound_error():
+ precipitable_water(pressure, dewpoint, bottom=units.Quantity(999, 'hPa'))
+
+
++ at 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')
+@@ -96,6 +99,7 @@ def test_mean_pressure_weighted():
+ assert_almost_equal(v, 7.966031839967931 * units('m/s'), 7)
+
+
++ at 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')
+@@ -106,6 +110,7 @@ def test_mean_pressure_weighted_temperature():
+ assert_almost_equal(t, 281.535035296836 * units('kelvin'), 7)
+
+
++ at 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')
+@@ -119,6 +124,7 @@ def test_mean_pressure_weighted_elevated():
+ assert_almost_equal(v, 1.7392601775853547 * units('m/s'), 7)
+
+
++ at 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')
+@@ -131,6 +137,7 @@ def test_weighted_continuous_average():
+ assert_almost_equal(v, 6.900543760612305 * units('m/s'), 7)
+
+
++ at pytest.mark.network
+ @pytest.mark.xfail(condition=version_check('pint<0.21'), reason='hgrecco/pint#1593')
+ def test_weighted_continuous_average_temperature():
+ """Test pressure-weighted mean temperature function with vertical interpolation."""
+@@ -142,6 +149,7 @@ def test_weighted_continuous_average_temperature():
+ assert_almost_equal(t, 279.07450928270185 * units('kelvin'), 7)
+
+
++ at 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')
+@@ -155,6 +163,7 @@ def test_weighted_continuous_average_elevated():
+ assert_almost_equal(v, 1.616638856115755 * units('m/s'), 7)
+
+
++ at 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')
+@@ -165,6 +174,7 @@ def test_precipitable_water_xarray():
+ assert_almost_equal(pw, truth)
+
+
++ at 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')
+@@ -176,6 +186,7 @@ def test_bunkers_motion():
+ assert_almost_equal(motion.flatten(), truth, 8)
+
+
++ at 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')
+@@ -186,6 +197,7 @@ def test_corfidi_motion():
+ assert_almost_equal(motion_full.flatten(), truth_full, 8)
+
+
++ at 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')
+@@ -206,6 +218,7 @@ def test_corfidi_motion_override_llj():
+ data['v_wind'], v_llj=10 * units('kt'))
+
+
++ at 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')
+@@ -213,6 +226,7 @@ def test_corfidi_corfidi_llj_unaivalable():
+ corfidi_storm_motion(data['pressure'][6:], data['u_wind'][6:], data['v_wind'][6:])
+
+
++ at 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')
+@@ -223,6 +237,7 @@ def test_corfidi_corfidi_cloudlayer_trimmed():
+ assert_almost_equal(motion_no_top.flatten(), truth_no_top, 8)
+
+
++ at 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')
+@@ -237,6 +252,7 @@ def test_corfidi_motion_with_nans():
+ assert_almost_equal(motion_with_nans.flatten(), truth_with_nans, 8)
+
+
++ at 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')
+@@ -252,6 +268,7 @@ def test_bunkers_motion_with_nans():
+ assert_almost_equal(motion.flatten(), truth, 8)
+
+
++ at pytest.mark.network
+ def test_bulk_shear():
+ """Test bulk shear with observed sounding."""
+ data = get_upper_air_data(datetime(2016, 5, 22, 0), 'DDC')
+@@ -263,6 +280,7 @@ def test_bulk_shear():
+ assert_almost_equal(v.to('knots'), truth[1], 8)
+
+
++ at 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')
+@@ -273,6 +291,7 @@ def test_bulk_shear_no_depth():
+ assert_almost_equal(v.to('knots'), truth[1], 8)
+
+
++ at 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')
+@@ -327,6 +346,7 @@ def test_sigtor_scalar():
+ assert_almost_equal(sigtor, truth, 6)
+
+
++ at pytest.mark.network
+ def test_critical_angle():
+ """Test critical angle with observed sounding."""
+ data = get_upper_air_data(datetime(2016, 5, 22, 0), 'DDC')
+@@ -337,6 +357,7 @@ def test_critical_angle():
+ assert_almost_equal(ca, truth, 8)
+
+
++ at 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 65118f2..f14dc04 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)
+
+
++ at 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)
+
+
++ at 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 @@ true_vort4d = np.array([[[[-5.72939079e-05, 3.36008149e-05, 4.80394116e-05, 2.24
+ 2.84689950e-05]]]]) * units('s^-1')
+
+
++ at 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)
+
+
++ at 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)
+
+
++ at 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)
+
+
++ at 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)
+
+
++ at 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)
+
+
++ at 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)
+
+
++ at 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)
+
+
++ at 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)
+
+
++ at 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)
+
+
++ at 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 573aad2..28983a0 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
+
+
++ at 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']
+
+
++ at 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)
+
+
++ at 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 608938f..70a7bd8 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)
+
+
++ at 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)
+
+
++ at 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)
+
+
++ at 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)
+
+
++ at 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')
+
+
++ at 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 6210824..cbcdf6d 100644
+--- a/tests/io/test_gempak.py
++++ b/tests/io/test_gempak.py
+@@ -17,6 +17,7 @@ from metpy.io.gempak import GempakGrid, GempakSounding, GempakSurface
+ logging.getLogger('metpy.io.gempak').setLevel(logging.ERROR)
+
+
++ at 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)
+
+
++ at 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)
+
+
++ at 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)
+
+
++ at 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)
+
+
++ at 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)
+
+
++ at 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)
+
+
++ at 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()])
+
+
++ at 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()])
+
+
++ at 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)
+
+
++ at 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)
+
+
++ at 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'
+
+
++ at 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)
+
+
++ at 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')
+
+
++ at 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
+
+
++ at 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')
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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():
+ )
+
+
++ at 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)
+
+
++ at 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 bb8e3ab..5c6f849 100644
+--- a/tests/io/test_gini.py
++++ b/tests/io/test_gini.py
+@@ -53,6 +53,7 @@ raw_gini_info = [('WEST-CONUS_4km_WV_20151208_2200.gini',
+ ]
+
+
++ at 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)
+
+
++ at 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 @@ gini_dataset_info = [('WEST-CONUS_4km_WV_20151208_2200.gini',
+ ]
+
+
++ at 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']
+
+
++ at 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']
+
+
++ at 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)
+
+
++ at 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
+
+
++ at 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'
+
+
++ at 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
+
+
++ at 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 d3dad56..62a10ea 100644
+--- a/tests/io/test_metar.py
++++ b/tests/io/test_metar.py
+@@ -15,6 +15,7 @@ from metpy.io.metar import Metar, parse_metar
+ from metpy.units import is_quantity, units
+
+
++ at 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
+
+
++ at 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)
+
+
++ at 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
+
+
++ at 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])
+
+
++ at 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)
+
+
++ at 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
+
+
++ at 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)
+
+
++ at 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))")
+
+
++ at 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 71a3dd3..b807c5b 100644
+--- a/tests/io/test_nexrad.py
++++ b/tests/io/test_nexrad.py
+@@ -41,6 +41,7 @@ level2_files = [('KTLX20130520_201643_V06.gz', datetime(2013, 5, 20, 20, 16, 46)
+
+
+ # ids here fixes how things are presented in pycharm
++ at 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
+
+
++ at pytest.mark.network
+ @pytest.mark.parametrize('filename', ['Level2_KFTG_20150430_1419.ar2v',
+ 'TDAL20191021021543V08.raw.gz',
+ 'KTLX20150530_000802_V06.bz2'])
+@@ -83,6 +85,7 @@ def test_level2_fobj(filename, use_seek):
+ Level2File(f)
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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)
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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'))
+@@ -165,6 +173,7 @@ def test_level3_files(fname):
+ assert f.filename == fname
+
+
++ at 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))
+@@ -179,6 +188,7 @@ def test_basic():
+ assert str(f)
+
+
++ at pytest.mark.network
+ def test_new_gsm():
+ """Test parsing recent mods to the GSM product."""
+ f = Level3File(get_test_data('nids/KDDC-gsm.nids'))
+@@ -195,6 +205,7 @@ def test_new_gsm():
+ assert str(f)
+
+
++ at 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)
+@@ -208,23 +219,27 @@ def test_bad_length(caplog):
+ assert 'This product may not parse correctly' in caplog.records[0].message
+
+
++ at 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
+
+
++ at 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)
+
+
++ at 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'))
+
+
++ at 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))
+@@ -232,6 +247,7 @@ def test_level3_pathlib():
+ assert f.filename == str(fname)
+
+
++ at 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'))
+@@ -239,6 +255,7 @@ def test_nids_super_res_width():
+ assert np.nanmax(width) == 15
+
+
++ at pytest.mark.network
+ def test_power_removed_control():
+ """Test decoding new PRC product."""
+ f = Level3File(get_test_data('nids/KGJX_NXF_20200817_0600.nids'))
+@@ -266,6 +283,7 @@ def test31_clear_air():
+ assert not is_precip_mode(31), 'VCP 31 is not precip'
+
+
++ at pytest.mark.network
+ def test_tracks():
+ """Check that tracks are properly decoded."""
+ f = Level3File(get_test_data('nids/KOUN_SDUS34_NSTTLX_201305202016'))
+@@ -276,6 +294,7 @@ def test_tracks():
+ assert len(y)
+
+
++ at pytest.mark.network
+ def test_vector_packet():
+ """Check that vector packets are properly decoded."""
+ f = Level3File(get_test_data('nids/KOUN_SDUS64_NHITLX_201305202016'))
+@@ -289,6 +308,7 @@ def test_vector_packet():
+ assert len(y2)
+
+
++ at 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 48f4209..8110d4e 100644
+--- a/tests/io/test_station_data.py
++++ b/tests/io/test_station_data.py
+@@ -10,6 +10,7 @@ import pytest
+ from metpy.io import add_station_lat_lon, station_info
+
+
++ at 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
+
+
++ at 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)
+
+
++ at 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'
+
+
++ at pytest.mark.network
+ def test_station_lookup_len():
+ """Test that you can get the length of the station data."""
+ assert len(station_info) == 13798
+
+
++ at 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 14b6ee5..752cd4e 100644
+--- a/tests/io/test_text.py
++++ b/tests/io/test_text.py
+@@ -10,7 +10,10 @@ from metpy.cbook import get_test_data
+ from metpy.io import parse_wpc_surface_bulletin
+ from metpy.testing import needs_module
+
++import pytest
+
++
++ at 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 +40,7 @@ def test_parse_wpc_surface_bulletin_highres():
+ assert all(df.valid == datetime(2021, 6, 28, 18, 0, 0))
+
+
++ at 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 93a43c2..177332a 100644
+--- a/tests/plots/test_declarative.py
++++ b/tests/plots/test_declarative.py
+@@ -26,6 +26,7 @@ from metpy.testing import needs_cartopy, version_check
+ from metpy.units import units
+
+
++ at 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
+
+
++ at 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()
+
+
++ at 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()
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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()
+
+
++ at 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)
+
+
++ at 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)
+
+
++ at 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()
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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)
+
+
++ at 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)
+
+
++ at 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)
+
+
++ at 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)
+
+
++ at 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)
+
+
++ at 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)
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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)
+
+
++ at 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)
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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]
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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)
+
+
++ at 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
+
+
++ at 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 96b366c..969e816 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
+
+
++ at 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 5540598..eae290e 100644
+--- a/tests/remote/test_aws.py
++++ b/tests/remote/test_aws.py
+@@ -9,7 +9,10 @@ import tempfile
+ from metpy.remote import GOESArchive, MLWPArchive, NEXRADLevel2Archive, NEXRADLevel3Archive
+ from metpy.testing import needs_aws
+
++import pytest
+
++
++ at 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()
+
+
++ at 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()
+
+
++ at 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'
+
+
++ at 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']
+
+
++ at 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')
+
+
++ at 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
+
+
++ at 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')
+
+
++ at 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 231ced2..a4c62fe 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'])
+
+
++ at 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)
+
+
++ at 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)'
+
+
++ at 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)
+
+
++ at 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)
+
+
++ at 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)
+
+
++ at 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
+
+
++ at pytest.mark.network
+ def test_units(test_var):
+ """Test the units property on the accessor."""
+ assert test_var.metpy.units == units.kelvin
+
+
++ at 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()
+
+
++ at 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
+
+
++ at 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):
+ )
+
+
++ at 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)
+
+
++ at 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
+
+
++ at 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():
+ )
+
+
++ at 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
+
+
++ at 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 test_preprocess_and_wrap_only_preprocessing():
+ assert_array_equal(func(data, b=data2), np.array([1001, 1001, 1001]) * units.m)
+
+
++ at 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)
+
+
++ at 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])
+
+
++ at 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 test_check_matching_coordinates(test_ds_generic):
+ add(test_ds_generic['test'], other)
+
+
++ at 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)
+
+
++ at 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'
+
+
++ at 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'
+
+
++ at 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'
+
+
++ at 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)
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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
+
+
++ at 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)
+
+
++ at 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])
+
+
++ at 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()
+
+
++ at 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, ...])
+
+
++ at 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'])
+
+
++ at 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, :, :, :]
+
+
++ at 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}))
+
+
++ at 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)
+
+
++ at 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)}])
+
+
++ at 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'))
+
+
++ at 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)
+
+
++ at 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)
+
+
++ at 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]
+
+
++ at 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')
+
+
++ at 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)
+
+
++ at 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)
+
+
++ at 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)
+
+
++ at 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):
=====================================
debian/patches/0003-xarray-v2025.12.0-compat.patch
=====================================
@@ -0,0 +1,37 @@
+From: Antonio Valentino <antonio.valentino at tiscali.it>
+Date: Sun, 18 Jan 2026 17:07:50 +0000
+Subject: xarray v2025.12.0 compat
+
+Mark as xfail tests that are expected not to work with
+xarray >= v2025.12.0
+
+Forwarded: https://github.com/Unidata/MetPy/issues/3978
+---
+ tests/calc/test_cross_sections.py | 1 +
+ tests/test_xarray.py | 1 +
+ 2 files changed, 2 insertions(+)
+
+diff --git a/tests/calc/test_cross_sections.py b/tests/calc/test_cross_sections.py
+index dbf65e7..4e3e864 100644
+--- a/tests/calc/test_cross_sections.py
++++ b/tests/calc/test_cross_sections.py
+@@ -304,6 +304,7 @@ def test_absolute_momentum_given_lonlat(test_cross_lonlat):
+ assert_xarray_allclose(momentum, true_momentum)
+
+
++ at pytest.mark.xfail
+ def test_absolute_momentum_given_xy(test_cross_xy):
+ """Test absolute momentum calculation."""
+ momentum = absolute_momentum(test_cross_xy['u_wind'], test_cross_xy['v_wind'])
+diff --git a/tests/test_xarray.py b/tests/test_xarray.py
+index a4c62fe..074fd60 100644
+--- a/tests/test_xarray.py
++++ b/tests/test_xarray.py
+@@ -222,6 +222,7 @@ def test_dequantify():
+ assert result.attrs['standard_name'] == 'air_temperature'
+
+
++ at pytest.mark.xfail
+ def test_dataset_quantify(test_ds_generic):
+ """Test quantify method for converting data to Quantity on Datasets."""
+ result = test_ds_generic.metpy.quantify()
=====================================
debian/patches/series
=====================================
@@ -1 +1,3 @@
0001-Skip-tests-requiring-internet.patch
+0002-No-network.patch
+0003-xarray-v2025.12.0-compat.patch
=====================================
debian/rules
=====================================
@@ -10,85 +10,7 @@ ln -s {dir}/src/${PYBUILD_NAME}/plots/_static {build_dir}/${PYBUILD_NAME}/plots
ln -s {dir}/src/${PYBUILD_NAME}/plots/colortable_files {build_dir}/${PYBUILD_NAME}/plots 2>/dev/null || true; \
ln -s {dir}/src/${PYBUILD_NAME}/plots/nexrad_tables {build_dir}/${PYBUILD_NAME}/plots 2>/dev/null || true
export PYBUILD_AFTER_TEST=rm -f {dir}/tests/conftest.py
-export PYBUILD_TEST_ARGS=\
--W ignore::DeprecationWarning \
--k "not test_zoom_xarray \
-and not test_parse_grid_arguments_xarray \
-and not test_absolute_momentum_xarray_units_attr \
-and not test_precipitable_water \
-and not test_mean_pressure_weighted \
-and not test_weighted_continuous_average \
-and not test_precipitable_water_xarray \
-and not test_bunkers_motion \
-and not test_corfidi_motion \
-and not test_corfidi_motion_override_llj \
-and not test_corfidi_corfidi_llj_unaivalable \
-and not test_corfidi_corfidi_cloudlayer_trimmed \
-and not test_corfidi_motion_with_nans \
-and not test_bulk_shear \
-and not test_critical_angle \
-and not test_interpolate_to_grid \
-and not test_interpolate_to_points \
-and not test_declarative_ \
-and not test_no_field_error\
-and not test_ndim_error_ \
-and not test_projection_object \
-and not test_colorfill \
-and not test_colorbar_kwargs \
-and not test_global \
-and not test_latlon \
-and not test_add_timestamp_xarray \
-and not test_units \
-and not test_time_deltas \
-and not test_advection_4d_vertical \
-and not _4d \
-and not test_natural_neighbor_to_grid \
-and not test_inverse_distance_to_grid \
-and not test_natural_neighbor_to_points \
-and not test_inverse_distance_to_points \
-and not test_plotobs_units_with_formatter \
-and not test_attribute_error_ \
-and not test_projection \
-and not test_pyproj_projection \
-and not test_no_projection \
-and not test_globe \
-and not test_geodetic \
-and not test_unit_array \
-and not test_units_data \
-and not test_magnitude_with_quantity \
-and not test_convert_units \
-and not test_convert_to_base_units \
-and not test_latlon_default_units \
-and not test_missing_grid_mapping_invalid \
-and not test_xy_not_vertical \
-and not test_coordinates_basic_by_method \
-and not test_coordinates_basic_by_property \
-and not test_narr_example_variable_without_grid_mapping \
-and not test_find_axis_name_ \
-and not test_find_axis_number_ \
-and not test_cf_parse_with_grid_mapping \
-and not test_data_array_loc_ \
-and not test_data_array_sel_ \
-and not test_dataset_ \
-and not test_auxilary_lat_lon_ \
-and not test_find_nn_triangles_point \
-and not test_vorticity_grid_pole \
-and not test_find_peaks \
-and not test_find_peaks_minima \
-and not test_nexrad3_single \
-and not test_nexrad3_range \
-and not test_nexrad2_single \
-and not test_nexrad2_range \
-and not test_goes_single \
-and not test_goes_range \
-and not test_mlwp_single \
-and not test_mlwp_range" \
---ignore=tests/io/test_gempak.py \
---ignore=tests/io/test_gini.py \
---ignore=tests/io/test_metar.py \
---ignore=tests/io/test_nexrad.py \
---ignore=tests/io/test_station_data.py \
---ignore=tests/io/test_text.py
+export PYBUILD_TEST_ARGS=-m "not network"
%:
dh $@ --buildsystem=pybuild
View it on GitLab: https://salsa.debian.org/debian-gis-team/metpy/-/compare/5859f1fc05d9793dd32e87d40d956f164c0e5cd4...2fdd0a2f49e734c7a1313b3e6177e4d46ac98ea7
--
View it on GitLab: https://salsa.debian.org/debian-gis-team/metpy/-/compare/5859f1fc05d9793dd32e87d40d956f164c0e5cd4...2fdd0a2f49e734c7a1313b3e6177e4d46ac98ea7
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-grass-devel/attachments/20260118/4e5c2acc/attachment-0001.htm>
More information about the Pkg-grass-devel
mailing list