Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor calendar fixtures #10150

Merged
merged 7 commits into from
Mar 20, 2025
Merged
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
32 changes: 28 additions & 4 deletions xarray/tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import xarray.testing
from xarray import Dataset
from xarray.coding.times import _STANDARD_CALENDARS as _STANDARD_CALENDARS_UNSORTED
from xarray.core.duck_array_ops import allclose_or_equiv # noqa: F401
from xarray.core.extension_array import PandasExtensionArray
from xarray.core.options import set_options
Expand Down Expand Up @@ -355,13 +356,36 @@ def create_test_data(
return obj


_CFTIME_CALENDARS = [
_STANDARD_CALENDAR_NAMES = sorted(_STANDARD_CALENDARS_UNSORTED)
_NON_STANDARD_CALENDAR_NAMES = {
"noleap",
"365_day",
"360_day",
"julian",
"all_leap",
"366_day",
"gregorian",
"proleptic_gregorian",
"standard",
}
_NON_STANDARD_CALENDARS = [
pytest.param(cal, marks=requires_cftime)
for cal in sorted(_NON_STANDARD_CALENDAR_NAMES)
]
_STANDARD_CALENDARS = [pytest.param(cal) for cal in _STANDARD_CALENDAR_NAMES]
_ALL_CALENDARS = sorted(_STANDARD_CALENDARS + _NON_STANDARD_CALENDARS)
_CFTIME_CALENDARS = [
pytest.param(*p.values, marks=requires_cftime) for p in _ALL_CALENDARS
]


def _all_cftime_date_types():
import cftime

return {
"noleap": cftime.DatetimeNoLeap,
"365_day": cftime.DatetimeNoLeap,
"360_day": cftime.Datetime360Day,
"julian": cftime.DatetimeJulian,
"all_leap": cftime.DatetimeAllLeap,
"366_day": cftime.DatetimeAllLeap,
"gregorian": cftime.DatetimeGregorian,
"proleptic_gregorian": cftime.DatetimeProlepticGregorian,
}
25 changes: 9 additions & 16 deletions xarray/tests/test_accessor_dt.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

import xarray as xr
from xarray.tests import (
_CFTIME_CALENDARS,
_all_cftime_date_types,
assert_allclose,
assert_array_equal,
assert_chunks_equal,
Expand Down Expand Up @@ -390,15 +392,6 @@ def test_dask_accessor_method(self, method, parameters) -> None:
assert_equal(actual.compute(), expected.compute())


_CFTIME_CALENDARS = [
"365_day",
"360_day",
"julian",
"all_leap",
"366_day",
"gregorian",
"proleptic_gregorian",
]
_NT = 100


Expand All @@ -407,6 +400,13 @@ def calendar(request):
return request.param


@pytest.fixture()
def cftime_date_type(calendar):
if calendar == "standard":
calendar = "proleptic_gregorian"
return _all_cftime_date_types()[calendar]


@pytest.fixture()
def times(calendar):
import cftime
Expand Down Expand Up @@ -573,13 +573,6 @@ def test_dask_field_access(times_3d, data, field) -> None:
assert_equal(result.compute(), expected)


@pytest.fixture()
def cftime_date_type(calendar):
from xarray.tests.test_coding_times import _all_cftime_date_types

return _all_cftime_date_types()[calendar]


@requires_cftime
def test_seasons(cftime_date_type) -> None:
dates = xr.DataArray(
Expand Down
10 changes: 4 additions & 6 deletions xarray/tests/test_cftimeindex.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,14 @@
)
from xarray.core.types import PDDatetimeUnitOptions
from xarray.tests import (
_ALL_CALENDARS,
_NON_STANDARD_CALENDAR_NAMES,
_all_cftime_date_types,
assert_array_equal,
assert_identical,
has_cftime,
requires_cftime,
)
from xarray.tests.test_coding_times import (
_ALL_CALENDARS,
_NON_STANDARD_CALENDARS,
_all_cftime_date_types,
)

# cftime 1.5.2 renames "gregorian" to "standard"
standard_or_gregorian = ""
Expand Down Expand Up @@ -1228,7 +1226,7 @@ def test_to_datetimeindex(calendar, unsafe) -> None:
index = xr.date_range("2000", periods=5, calendar=calendar, use_cftime=True)
expected = pd.date_range("2000", periods=5, unit="ns")

if calendar in _NON_STANDARD_CALENDARS and not unsafe:
if calendar in _NON_STANDARD_CALENDAR_NAMES and not unsafe:
with pytest.warns(RuntimeWarning, match="non-standard"):
result = index.to_datetimeindex(time_unit="ns")
else:
Expand Down
42 changes: 10 additions & 32 deletions xarray/tests/test_coding_times.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
decode_cf,
)
from xarray.coders import CFDatetimeCoder, CFTimedeltaCoder
from xarray.coding.times import _STANDARD_CALENDARS as _STANDARD_CALENDARS_UNSORTED
from xarray.coding.times import (
_encode_datetime_with_cftime,
_netcdf_to_numpy_timeunit,
Expand All @@ -41,8 +40,13 @@
from xarray.core.utils import is_duck_dask_array
from xarray.testing import assert_equal, assert_identical
from xarray.tests import (
_ALL_CALENDARS,
_NON_STANDARD_CALENDARS,
_STANDARD_CALENDAR_NAMES,
_STANDARD_CALENDARS,
DuckArrayWrapper,
FirstElementAccessibleArray,
_all_cftime_date_types,
arm_xfail,
assert_array_equal,
assert_duckarray_allclose,
Expand All @@ -53,17 +57,6 @@
requires_dask,
)

_NON_STANDARD_CALENDARS_SET = {
"noleap",
"365_day",
"360_day",
"julian",
"all_leap",
"366_day",
}
_STANDARD_CALENDARS = sorted(_STANDARD_CALENDARS_UNSORTED)
_ALL_CALENDARS = sorted(_NON_STANDARD_CALENDARS_SET.union(_STANDARD_CALENDARS))
_NON_STANDARD_CALENDARS = sorted(_NON_STANDARD_CALENDARS_SET)
_CF_DATETIME_NUM_DATES_UNITS = [
(np.arange(10), "days since 2000-01-01", "s"),
(np.arange(10).astype("float64"), "days since 2000-01-01", "s"),
Expand Down Expand Up @@ -99,26 +92,11 @@
_CF_DATETIME_TESTS = [
num_dates_units + (calendar,)
for num_dates_units, calendar in product(
_CF_DATETIME_NUM_DATES_UNITS, _STANDARD_CALENDARS
_CF_DATETIME_NUM_DATES_UNITS, _STANDARD_CALENDAR_NAMES
)
]


def _all_cftime_date_types():
import cftime

return {
"noleap": cftime.DatetimeNoLeap,
"365_day": cftime.DatetimeNoLeap,
"360_day": cftime.Datetime360Day,
"julian": cftime.DatetimeJulian,
"all_leap": cftime.DatetimeAllLeap,
"366_day": cftime.DatetimeAllLeap,
"gregorian": cftime.DatetimeGregorian,
"proleptic_gregorian": cftime.DatetimeProlepticGregorian,
}


@requires_cftime
@pytest.mark.filterwarnings("ignore:Ambiguous reference date string")
@pytest.mark.filterwarnings("ignore:Times can't be serialized faithfully")
Expand Down Expand Up @@ -734,13 +712,13 @@ def test_decode_cf(calendar, time_unit: PDDatetimeUnitOptions) -> None:
ds[v].attrs["units"] = "days since 2001-01-01"
ds[v].attrs["calendar"] = calendar

if not has_cftime and calendar not in _STANDARD_CALENDARS:
if not has_cftime and calendar not in _STANDARD_CALENDAR_NAMES:
with pytest.raises(ValueError):
ds = decode_cf(ds)
else:
ds = decode_cf(ds, decode_times=CFDatetimeCoder(time_unit=time_unit))

if calendar not in _STANDARD_CALENDARS:
if calendar not in _STANDARD_CALENDAR_NAMES:
assert ds.test.dtype == np.dtype("O")
else:
assert ds.test.dtype == np.dtype(f"=M8[{time_unit}]")
Expand Down Expand Up @@ -1085,7 +1063,7 @@ def test_decode_ambiguous_time_warns(calendar) -> None:

# we don't decode non-standard calendards with
# pandas so expect no warning will be emitted
is_standard_calendar = calendar in _STANDARD_CALENDARS
is_standard_calendar = calendar in _STANDARD_CALENDAR_NAMES

dates = [1, 2, 3]
units = "days since 1-1-1"
Expand Down Expand Up @@ -1954,7 +1932,7 @@ def test_duck_array_decode_times(calendar) -> None:
decoded = conventions.decode_cf_variable(
"foo", var, decode_times=CFDatetimeCoder(use_cftime=None)
)
if calendar not in _STANDARD_CALENDARS:
if calendar not in _STANDARD_CALENDAR_NAMES:
assert decoded.dtype == np.dtype("O")
else:
assert decoded.dtype == np.dtype("=M8[ns]")
Expand Down
Loading