Skip to content

Commit 1486bea

Browse files
kmuehlbauerdcherianspencerkclarkpre-commit-ci[bot]
authored
split out CFDatetimeCoder, deprecate use_cftime as kwarg (#9901)
* split out CFDatetimeCoder into coders, deprecate use_cftime as keyword argument * add whats-new.rst entry * Apply suggestions from code review Co-authored-by: Deepak Cherian <[email protected]> * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix warning * fix docstrings * try fix typing * Apply suggestions from code review Co-authored-by: Spencer Clark <[email protected]> * Apply suggestions from code review Co-authored-by: Spencer Clark <[email protected]> * Update xarray/conventions.py * Update deprecated directive * fix docstrings/whats-new.rst after merge * fix whats-new.rst * update warnings/errors --------- Co-authored-by: Deepak Cherian <[email protected]> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Spencer Clark <[email protected]>
1 parent ff7d157 commit 1486bea

12 files changed

+190
-52
lines changed

doc/api.rst

+11
Original file line numberDiff line numberDiff line change
@@ -1096,6 +1096,17 @@ DataTree methods
10961096
.. Missing:
10971097
.. ``open_mfdatatree``
10981098
1099+
Encoding/Decoding
1100+
=================
1101+
1102+
Coder objects
1103+
-------------
1104+
1105+
.. autosummary::
1106+
:toctree: generated/
1107+
1108+
coders.CFDatetimeCoder
1109+
10991110
Coordinates objects
11001111
===================
11011112

doc/whats-new.rst

+7-2
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,19 @@ v2025.01.1 (unreleased)
2222

2323
New Features
2424
~~~~~~~~~~~~
25-
25+
- Split out :py:class:`coders.CFDatetimeCoder` as public API in ``xr.coders``, make ``decode_times`` keyword argument
26+
consume :py:class:`coders.CFDatetimeCoder` (:pull:`9901`).
27+
By `Kai Mühlbauer <https://github.com/kmuehlbauer>`_.
2628

2729
Breaking changes
2830
~~~~~~~~~~~~~~~~
2931

3032

3133
Deprecations
3234
~~~~~~~~~~~~
33-
35+
- Time decoding related kwarg ``use_cftime`` is deprecated. Use keyword argument
36+
``decode_times=CFDatetimeCoder(use_cftime=True)`` in :py:func:`~xarray.open_dataset`, :py:func:`~xarray.open_dataarray`, :py:func:`~xarray.open_datatree`, :py:func:`~xarray.open_groups`, :py:func:`~xarray.open_zarr` and :py:func:`~xarray.decode_cf` instead (:pull:`9901`).
37+
By `Kai Mühlbauer <https://github.com/kmuehlbauer>`_.
3438

3539
Bug fixes
3640
~~~~~~~~~
@@ -70,6 +74,7 @@ New Features
7074
iso8601-parser (:pull:`9885`).
7175
By `Kai Mühlbauer <https://github.com/kmuehlbauer>`_.
7276

77+
7378
Breaking changes
7479
~~~~~~~~~~~~~~~~
7580
- Methods including ``dropna``, ``rank``, ``idxmax``, ``idxmin`` require

xarray/__init__.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from importlib.metadata import version as _version
22

3-
from xarray import groupers, testing, tutorial, ufuncs
3+
from xarray import coders, groupers, testing, tutorial, ufuncs
44
from xarray.backends.api import (
55
load_dataarray,
66
load_dataset,
@@ -66,6 +66,7 @@
6666
# `mypy --strict` running in projects that import xarray.
6767
__all__ = ( # noqa: RUF022
6868
# Sub-packages
69+
"coders",
6970
"groupers",
7071
"testing",
7172
"tutorial",

xarray/backends/api.py

+44-12
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
_normalize_path,
3434
)
3535
from xarray.backends.locks import _get_scheduler
36+
from xarray.coders import CFDatetimeCoder
3637
from xarray.core import indexing
3738
from xarray.core.combine import (
3839
_infer_concat_order_from_positions,
@@ -481,7 +482,10 @@ def open_dataset(
481482
cache: bool | None = None,
482483
decode_cf: bool | None = None,
483484
mask_and_scale: bool | Mapping[str, bool] | None = None,
484-
decode_times: bool | Mapping[str, bool] | None = None,
485+
decode_times: bool
486+
| CFDatetimeCoder
487+
| Mapping[str, bool | CFDatetimeCoder]
488+
| None = None,
485489
decode_timedelta: bool | Mapping[str, bool] | None = None,
486490
use_cftime: bool | Mapping[str, bool] | None = None,
487491
concat_characters: bool | Mapping[str, bool] | None = None,
@@ -543,9 +547,10 @@ def open_dataset(
543547
be replaced by NA. Pass a mapping, e.g. ``{"my_variable": False}``,
544548
to toggle this feature per-variable individually.
545549
This keyword may not be supported by all the backends.
546-
decode_times : bool or dict-like, optional
550+
decode_times : bool, CFDatetimeCoder or dict-like, optional
547551
If True, decode times encoded in the standard NetCDF datetime format
548-
into datetime objects. Otherwise, leave them encoded as numbers.
552+
into datetime objects. Otherwise, use :py:class:`coders.CFDatetimeCoder` or leave them
553+
encoded as numbers.
549554
Pass a mapping, e.g. ``{"my_variable": False}``,
550555
to toggle this feature per-variable individually.
551556
This keyword may not be supported by all the backends.
@@ -569,6 +574,10 @@ def open_dataset(
569574
raise an error. Pass a mapping, e.g. ``{"my_variable": False}``,
570575
to toggle this feature per-variable individually.
571576
This keyword may not be supported by all the backends.
577+
578+
.. deprecated:: 2025.01.1
579+
Please pass a :py:class:`coders.CFDatetimeCoder` instance initialized with ``use_cftime`` to the ``decode_times`` kwarg instead.
580+
572581
concat_characters : bool or dict-like, optional
573582
If True, concatenate along the last dimension of character arrays to
574583
form string arrays. Dimensions will only be concatenated over (and
@@ -698,7 +707,10 @@ def open_dataarray(
698707
cache: bool | None = None,
699708
decode_cf: bool | None = None,
700709
mask_and_scale: bool | None = None,
701-
decode_times: bool | None = None,
710+
decode_times: bool
711+
| CFDatetimeCoder
712+
| Mapping[str, bool | CFDatetimeCoder]
713+
| None = None,
702714
decode_timedelta: bool | None = None,
703715
use_cftime: bool | None = None,
704716
concat_characters: bool | None = None,
@@ -761,9 +773,11 @@ def open_dataarray(
761773
`missing_value` attribute contains multiple values a warning will be
762774
issued and all array values matching one of the multiple values will
763775
be replaced by NA. This keyword may not be supported by all the backends.
764-
decode_times : bool, optional
776+
decode_times : bool, CFDatetimeCoder or dict-like, optional
765777
If True, decode times encoded in the standard NetCDF datetime format
766-
into datetime objects. Otherwise, leave them encoded as numbers.
778+
into datetime objects. Otherwise, use :py:class:`coders.CFDatetimeCoder` or leave them encoded as numbers.
779+
Pass a mapping, e.g. ``{"my_variable": False}``,
780+
to toggle this feature per-variable individually.
767781
This keyword may not be supported by all the backends.
768782
decode_timedelta : bool, optional
769783
If True, decode variables and coordinates with time units in
@@ -781,6 +795,10 @@ def open_dataarray(
781795
represented using ``np.datetime64[ns]`` objects. If False, always
782796
decode times to ``np.datetime64[ns]`` objects; if this is not possible
783797
raise an error. This keyword may not be supported by all the backends.
798+
799+
.. deprecated:: 2025.01.1
800+
Please pass a :py:class:`coders.CFDatetimeCoder` instance initialized with ``use_cftime`` to the ``decode_times`` kwarg instead.
801+
784802
concat_characters : bool, optional
785803
If True, concatenate along the last dimension of character arrays to
786804
form string arrays. Dimensions will only be concatenated over (and
@@ -903,7 +921,10 @@ def open_datatree(
903921
cache: bool | None = None,
904922
decode_cf: bool | None = None,
905923
mask_and_scale: bool | Mapping[str, bool] | None = None,
906-
decode_times: bool | Mapping[str, bool] | None = None,
924+
decode_times: bool
925+
| CFDatetimeCoder
926+
| Mapping[str, bool | CFDatetimeCoder]
927+
| None = None,
907928
decode_timedelta: bool | Mapping[str, bool] | None = None,
908929
use_cftime: bool | Mapping[str, bool] | None = None,
909930
concat_characters: bool | Mapping[str, bool] | None = None,
@@ -961,9 +982,9 @@ def open_datatree(
961982
be replaced by NA. Pass a mapping, e.g. ``{"my_variable": False}``,
962983
to toggle this feature per-variable individually.
963984
This keyword may not be supported by all the backends.
964-
decode_times : bool or dict-like, optional
985+
decode_times : bool, CFDatetimeCoder or dict-like, optional
965986
If True, decode times encoded in the standard NetCDF datetime format
966-
into datetime objects. Otherwise, leave them encoded as numbers.
987+
into datetime objects. Otherwise, use :py:class:`coders.CFDatetimeCoder` or leave them encoded as numbers.
967988
Pass a mapping, e.g. ``{"my_variable": False}``,
968989
to toggle this feature per-variable individually.
969990
This keyword may not be supported by all the backends.
@@ -987,6 +1008,10 @@ def open_datatree(
9871008
raise an error. Pass a mapping, e.g. ``{"my_variable": False}``,
9881009
to toggle this feature per-variable individually.
9891010
This keyword may not be supported by all the backends.
1011+
1012+
.. deprecated:: 2025.01.1
1013+
Please pass a :py:class:`coders.CFDatetimeCoder` instance initialized with ``use_cftime`` to the ``decode_times`` kwarg instead.
1014+
9901015
concat_characters : bool or dict-like, optional
9911016
If True, concatenate along the last dimension of character arrays to
9921017
form string arrays. Dimensions will only be concatenated over (and
@@ -1118,7 +1143,10 @@ def open_groups(
11181143
cache: bool | None = None,
11191144
decode_cf: bool | None = None,
11201145
mask_and_scale: bool | Mapping[str, bool] | None = None,
1121-
decode_times: bool | Mapping[str, bool] | None = None,
1146+
decode_times: bool
1147+
| CFDatetimeCoder
1148+
| Mapping[str, bool | CFDatetimeCoder]
1149+
| None = None,
11221150
decode_timedelta: bool | Mapping[str, bool] | None = None,
11231151
use_cftime: bool | Mapping[str, bool] | None = None,
11241152
concat_characters: bool | Mapping[str, bool] | None = None,
@@ -1180,9 +1208,9 @@ def open_groups(
11801208
be replaced by NA. Pass a mapping, e.g. ``{"my_variable": False}``,
11811209
to toggle this feature per-variable individually.
11821210
This keyword may not be supported by all the backends.
1183-
decode_times : bool or dict-like, optional
1211+
decode_times : bool, CFDatetimeCoder or dict-like, optional
11841212
If True, decode times encoded in the standard NetCDF datetime format
1185-
into datetime objects. Otherwise, leave them encoded as numbers.
1213+
into datetime objects. Otherwise, use :py:class:`coders.CFDatetimeCoder` or leave them encoded as numbers.
11861214
Pass a mapping, e.g. ``{"my_variable": False}``,
11871215
to toggle this feature per-variable individually.
11881216
This keyword may not be supported by all the backends.
@@ -1206,6 +1234,10 @@ def open_groups(
12061234
raise an error. Pass a mapping, e.g. ``{"my_variable": False}``,
12071235
to toggle this feature per-variable individually.
12081236
This keyword may not be supported by all the backends.
1237+
1238+
.. deprecated:: 2025.01.1
1239+
Please pass a :py:class:`coders.CFDatetimeCoder` instance initialized with ``use_cftime`` to the ``decode_times`` kwarg instead.
1240+
12091241
concat_characters : bool or dict-like, optional
12101242
If True, concatenate along the last dimension of character arrays to
12111243
form string arrays. Dimensions will only be concatenated over (and

xarray/coders.py

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
"""
2+
This module provides coder objects that encapsulate the
3+
"encoding/decoding" process.
4+
"""
5+
6+
from xarray.coding.times import CFDatetimeCoder
7+
8+
__all__ = [
9+
"CFDatetimeCoder",
10+
]

xarray/coding/times.py

+12-3
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,10 @@ def _unpack_time_unit_and_ref_date(
286286

287287

288288
def _decode_cf_datetime_dtype(
289-
data, units: str, calendar: str | None, use_cftime: bool | None
289+
data,
290+
units: str,
291+
calendar: str | None,
292+
use_cftime: bool | None,
290293
) -> np.dtype:
291294
# Verify that at least the first and last date can be decoded
292295
# successfully. Otherwise, tracebacks end up swallowed by
@@ -421,7 +424,10 @@ def _decode_datetime_with_pandas(
421424

422425

423426
def decode_cf_datetime(
424-
num_dates, units: str, calendar: str | None = None, use_cftime: bool | None = None
427+
num_dates,
428+
units: str,
429+
calendar: str | None = None,
430+
use_cftime: bool | None = None,
425431
) -> np.ndarray:
426432
"""Given an array of numeric dates in netCDF format, convert it into a
427433
numpy array of date time objects.
@@ -1093,7 +1099,10 @@ def _lazily_encode_cf_timedelta(
10931099

10941100

10951101
class CFDatetimeCoder(VariableCoder):
1096-
def __init__(self, use_cftime: bool | None = None) -> None:
1102+
def __init__(
1103+
self,
1104+
use_cftime: bool | None = None,
1105+
) -> None:
10971106
self.use_cftime = use_cftime
10981107

10991108
def encode(self, variable: Variable, name: T_Name = None) -> Variable:

0 commit comments

Comments
 (0)