Skip to content

Commit 3074394

Browse files
Improve warning message and tests for timedelta decoding (#10508)
* Improve warning message and tests for timedelta decoding The new warning message gives years clearer and simpler guidance on how to silence the warning. The new tests verify that `dtype` attributes on disk like `timedelta64[s]` will be decoded properly. * Update xarray/tests/test_coding_times.py Co-authored-by: Spencer Clark <[email protected]> --------- Co-authored-by: Spencer Clark <[email protected]>
1 parent 89c09b6 commit 3074394

File tree

2 files changed

+34
-20
lines changed

2 files changed

+34
-20
lines changed

xarray/coding/times.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1517,20 +1517,20 @@ def decode(self, variable: Variable, name: T_Name = None) -> Variable:
15171517
time_unit = self.time_unit
15181518
else:
15191519
if self._emit_decode_timedelta_future_warning:
1520+
var_string = f"the variable {name!r}" if name else ""
15201521
emit_user_level_warning(
15211522
"In a future version, xarray will not decode "
1522-
"timedelta values based on the presence of a "
1523-
"timedelta-like units attribute by default. Instead "
1524-
"it will rely on the presence of a timedelta64 dtype "
1525-
"attribute, which is now xarray's default way of "
1526-
"encoding timedelta64 values. To continue decoding "
1527-
"timedeltas based on the presence of a timedelta-like "
1528-
"units attribute, users will need to explicitly "
1529-
"opt-in by passing True or "
1530-
"CFTimedeltaCoder(decode_via_units=True) to "
1531-
"decode_timedelta. To silence this warning, set "
1532-
"decode_timedelta to True, False, or a "
1533-
"'CFTimedeltaCoder' instance.",
1523+
f"{var_string} into a timedelta64 dtype based on the "
1524+
"presence of a timedelta-like 'units' attribute by "
1525+
"default. Instead it will rely on the presence of a "
1526+
"timedelta64 'dtype' attribute, which is now xarray's "
1527+
"default way of encoding timedelta64 values.\n"
1528+
"To continue decoding into a timedelta64 dtype, either "
1529+
"set `decode_timedelta=True` when opening this "
1530+
"dataset, or add the attribute "
1531+
"`dtype='timedelta64[ns]'` to this variable on disk.\n"
1532+
"To opt-in to future behavior, set "
1533+
"`decode_timedelta=False`.",
15341534
FutureWarning,
15351535
)
15361536
if self.time_unit is None:

xarray/tests/test_coding_times.py

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1867,7 +1867,10 @@ def test_decode_timedelta_via_units(
18671867
var = Variable(["time"], timedeltas, encoding=attrs)
18681868
encoded = Variable(["time"], np.array([0, 1, 2]), attrs=attrs)
18691869
if warns:
1870-
with pytest.warns(FutureWarning, match="decode_timedelta"):
1870+
with pytest.warns(
1871+
FutureWarning,
1872+
match="xarray will not decode the variable 'foo' into a timedelta64 dtype",
1873+
):
18711874
decoded = conventions.decode_cf_variable(
18721875
"foo",
18731876
encoded,
@@ -1886,45 +1889,56 @@ def test_decode_timedelta_via_units(
18861889

18871890

18881891
_DECODE_TIMEDELTA_VIA_DTYPE_TESTS = {
1889-
"default": (True, None, np.dtype("timedelta64[ns]")),
1890-
"decode_timedelta=False": (True, False, np.dtype("int64")),
1891-
"decode_timedelta=True": (True, True, np.dtype("timedelta64[ns]")),
1892+
"default": (True, None, "ns", np.dtype("timedelta64[ns]")),
1893+
"decode_timedelta=False": (True, False, "ns", np.dtype("int64")),
1894+
"decode_timedelta=True": (True, True, "ns", np.dtype("timedelta64[ns]")),
1895+
"use-original-units": (True, True, "s", np.dtype("timedelta64[s]")),
18921896
"inherit-time_unit-from-decode_times": (
18931897
CFDatetimeCoder(time_unit="s"),
18941898
None,
1899+
"ns",
18951900
np.dtype("timedelta64[s]"),
18961901
),
18971902
"set-time_unit-via-CFTimedeltaCoder-decode_times=True": (
18981903
True,
18991904
CFTimedeltaCoder(time_unit="s"),
1905+
"ns",
19001906
np.dtype("timedelta64[s]"),
19011907
),
19021908
"set-time_unit-via-CFTimedeltaCoder-decode_times=False": (
19031909
False,
19041910
CFTimedeltaCoder(time_unit="s"),
1911+
"ns",
19051912
np.dtype("timedelta64[s]"),
19061913
),
19071914
"override-time_unit-from-decode_times": (
19081915
CFDatetimeCoder(time_unit="ns"),
19091916
CFTimedeltaCoder(time_unit="s"),
1917+
"ns",
19101918
np.dtype("timedelta64[s]"),
19111919
),
1920+
"decode-different-units": (
1921+
True,
1922+
CFTimedeltaCoder(time_unit="us"),
1923+
"s",
1924+
np.dtype("timedelta64[us]"),
1925+
),
19121926
}
19131927

19141928

19151929
@pytest.mark.parametrize(
1916-
("decode_times", "decode_timedelta", "expected_dtype"),
1930+
("decode_times", "decode_timedelta", "original_unit", "expected_dtype"),
19171931
list(_DECODE_TIMEDELTA_VIA_DTYPE_TESTS.values()),
19181932
ids=list(_DECODE_TIMEDELTA_VIA_DTYPE_TESTS.keys()),
19191933
)
19201934
def test_decode_timedelta_via_dtype(
1921-
decode_times, decode_timedelta, expected_dtype
1935+
decode_times, decode_timedelta, original_unit, expected_dtype
19221936
) -> None:
1923-
timedeltas = pd.timedelta_range(0, freq="D", periods=3)
1937+
timedeltas = pd.timedelta_range(0, freq="D", periods=3, unit=original_unit) # type: ignore[call-arg]
19241938
encoding = {"units": "days"}
19251939
var = Variable(["time"], timedeltas, encoding=encoding)
19261940
encoded = conventions.encode_cf_variable(var)
1927-
assert encoded.attrs["dtype"] == "timedelta64[ns]"
1941+
assert encoded.attrs["dtype"] == f"timedelta64[{original_unit}]"
19281942
assert encoded.attrs["units"] == encoding["units"]
19291943
decoded = conventions.decode_cf_variable(
19301944
"foo", encoded, decode_times=decode_times, decode_timedelta=decode_timedelta

0 commit comments

Comments
 (0)