[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