Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/actions/run-tests/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ inputs:
pytest-args:
description: 'Additional arguments to pass to pytest'
required: false
default: '--mpl -W error::metpy.deprecation.MetpyDeprecationWarning'
default: '--mpl --record-mode=none -W error::metpy.deprecation.MetpyDeprecationWarning'
runs:
using: composite
steps:
Expand Down
1 change: 1 addition & 0 deletions ci-dev/test_requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
packaging==25.0
pytest==8.4.0
pytest-mpl==0.17.0
pytest-recording==0.13.2
coverage==7.9.1
vcrpy==7.0.0
17 changes: 17 additions & 0 deletions conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,3 +183,20 @@ def geog_data(request):
geod=crs.get_geod())[0][0],
metpy.calc.lat_lon_grid_deltas(numpy.zeros_like(lats.m), lats.m,
geod=crs.get_geod())[1][:, 0])


@pytest.fixture(scope='module')
def vcr_cassette_dir(request):
"""Modify default cassette path for vcr mark."""
return str(request.path.parent / 'fixtures')


@pytest.fixture(scope='package')
def vcr_config():
"""Pass default config to vcr mark."""
return {
# Record new cassettes if empty and replay existing cassettes by default;
# we can use 'none' in CI to refuse new recordings and replay old only.
# Use pytest --record-mode=rewrite to delete existing cassettes and re-record.
'record_mode': 'once'
}
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ test = [
"packaging>=21.0",
"pytest>=7.0",
"pytest-mpl",
"pytest-recording",
"vcrpy>=4.3.1"
]
extras = [
Expand Down
29 changes: 0 additions & 29 deletions src/metpy/testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@
import contextlib
import functools
from importlib.metadata import PackageNotFoundError, requires, version
import inspect
import operator as op
from pathlib import Path
import re

import matplotlib.pyplot as plt
Expand Down Expand Up @@ -134,33 +132,6 @@ def wrapped(*args, **kwargs):
needs_cartopy = needs_module('cartopy')


def needs_aws(test_func):
"""Decorate a test function that needs AWS functionality.

This both sets up recording using VCRPy as well as ensures that the the appropriate
AWS libraries are installed, otherwise the test is skipped.
"""
# Get the vcr module this way so we can skip tests if it's not present
vcr = pytest.importorskip('vcr')

# Set up the fixtures relative to the test file
func_path = inspect.getfile(test_func)
fixture_path = Path(func_path).with_name('fixtures') / f'{test_func.__name__}.yaml'

# Set the cassette to use
# also wrap to skip the test if no boto3
# and filter s3 resource unclosed SSL warnings
return (
vcr.use_cassette(fixture_path)(
needs_module('boto3')(
pytest.mark.filterwarnings('default:unclosed:ResourceWarning')(
test_func
)
)
)
)


@contextlib.contextmanager
def autoclose_figure(*args, **kwargs):
"""Create a figure that is automatically closed when exiting a block.
Expand Down
35 changes: 26 additions & 9 deletions tests/remote/test_aws.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,30 @@
from pathlib import Path
import tempfile

import pytest

from metpy.remote import GOESArchive, MLWPArchive, NEXRADLevel2Archive, NEXRADLevel3Archive
from metpy.testing import needs_aws
from metpy.testing import needs_module

# Add pytest marks for all tests in module
pytestmark = [
# Reset warning filter to default ignore for all
# ResourceWarning: unclosed SSL from boto3 resource
pytest.mark.filterwarnings('default:unclosed:ResourceWarning')
]


@needs_aws
@pytest.mark.vcr
@needs_module('boto3')
def test_nexrad3_single():
"""Test getting a single product from the NEXRAD level 3 archive."""
l3 = NEXRADLevel3Archive().get_product('FTG', 'N0Q', datetime(2020, 4, 1, 12, 30))
assert l3.path == 'FTG_N0Q_2020_04_01_12_30_12'
assert l3.access()


@needs_aws
@pytest.mark.vcr
@needs_module('boto3')
def test_nexrad3_range():
"""Test getting a range of products from the NEXRAD level 3 archive."""
prods = list(NEXRADLevel3Archive().get_range('FTG', 'N0B', datetime(2024, 12, 31, 23, 45),
Expand All @@ -40,14 +51,16 @@ def test_nexrad3_range():
assert (Path(tmpdir) / 'tempprod').exists()


@needs_aws
@pytest.mark.vcr
@needs_module('boto3')
def test_nexrad2_single():
"""Test getting a single volume from the NEXRAD level 2 archive."""
l2 = NEXRADLevel2Archive().get_product('KTLX', datetime(2013, 5, 20, 20, 15))
assert l2.name == 'KTLX20130520_201643_V06.gz'


@needs_aws
@pytest.mark.vcr
@needs_module('boto3')
def test_nexrad2_range():
"""Test getting a range of products from the NEXRAD level 2 archive."""
vols = list(NEXRADLevel2Archive().get_range('KFTG', datetime(2024, 12, 14, 15, 15),
Expand All @@ -59,7 +72,8 @@ def test_nexrad2_range():
'KFTG20241214_161349_V06', 'KFTG20241214_162248_V06']


@needs_aws
@pytest.mark.vcr
@needs_module('boto3')
def test_goes_single():
"""Test getting a single product from the GOES archive."""
prod = GOESArchive(18).get_product('ABI-L1b-RadM1', datetime(2025, 1, 9, 23, 56), band=2)
Expand All @@ -70,7 +84,8 @@ def test_goes_single():
'_e20250092356311_c20250092356338.nc')


@needs_aws
@pytest.mark.vcr
@needs_module('boto3')
def test_goes_range():
"""Test getting a range of products from the GOES archive."""
prods = list(GOESArchive(16).get_range('ABI-L1b-RadC', datetime(2024, 12, 10, 1, 0),
Expand All @@ -94,15 +109,17 @@ def test_goes_range():
assert names == truth


@needs_aws
@pytest.mark.vcr
@needs_module('boto3')
def test_mlwp_single():
"""Test getting a single product from the MLWP archive."""
prod = MLWPArchive().get_product('graphcast', datetime(2025, 1, 30, 10))
assert prod.url == ('https://noaa-oar-mlwp-data.s3.amazonaws.com/GRAP_v100_GFS/'
'2025/0130/GRAP_v100_GFS_2025013012_f000_f240_06.nc')


@needs_aws
@pytest.mark.vcr
@needs_module('boto3')
def test_mlwp_range():
"""Test getting a single product from the MLWP archive."""
prods = MLWPArchive().get_range('fourcastnet', datetime(2025, 2, 3), datetime(2025, 2, 6))
Expand Down