Skip to content

Commit 3f86d33

Browse files
committed
Add documentation of deprecation
1 parent 11a9e0d commit 3f86d33

File tree

5 files changed

+58
-20
lines changed

5 files changed

+58
-20
lines changed

doc/whats-new.rst

+4-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,10 @@ Breaking changes
3030

3131
Deprecations
3232
~~~~~~~~~~~~
33-
33+
- Following pandas, the `closed` arguments of :py:func:`cftime_range` and
34+
:py:func:`date_range` are deprecated in favor of the `inclusive` arguments,
35+
and will be removed in a future version of xarray (:issue:`6985`:,
36+
:pull:`7373`). By `Spencer Clark <https://github.com/spencerkclark>`_.
3437

3538
Bug fixes
3639
~~~~~~~~~

xarray/coding/cftime_offsets.py

+40-13
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
from datetime import datetime, timedelta
4646
from enum import Enum
4747
from functools import partial
48-
from typing import ClassVar
48+
from typing import TYPE_CHECKING, ClassVar, Literal
4949

5050
import numpy as np
5151
import pandas as pd
@@ -67,6 +67,10 @@
6767
cftime = None
6868

6969

70+
if TYPE_CHECKING:
71+
from xarray.core.types import InclusiveOptions, SideOptions
72+
73+
7074
def get_date_type(calendar, use_cftime=True):
7175
"""Return the cftime date type for a given calendar name."""
7276
if cftime is None:
@@ -852,15 +856,24 @@ def _generate_range(start, end, periods, offset):
852856

853857

854858
class _NoDefault(Enum):
855-
"""Used by pandas to specify a default value for a deprecated argument."""
859+
"""Used by pandas to specify a default value for a deprecated argument.
860+
Copied from pandas._libs.lib._NoDefault.
861+
"""
856862

857863
no_default = "NO_DEFAULT"
858864

859865
def __repr__(self) -> str:
860866
return "<no_default>"
861867

862868

869+
no_default = (
870+
_NoDefault.no_default
871+
) # Sentinel indicating the default value following pandas
872+
NoDefault = Literal[_NoDefault.no_default] # For typing following pandas
873+
874+
863875
def _translate_closed_to_inclusive(closed):
876+
"""Follows code added in pandas #43504."""
864877
emit_user_level_warning(
865878
"Following pandas, the `closed` argument is deprecated in "
866879
"favor of the `inclusive` argument, and will be removed in "
@@ -880,12 +893,13 @@ def _translate_closed_to_inclusive(closed):
880893

881894

882895
def _infer_inclusive(closed, inclusive):
883-
if closed is not _NoDefault and inclusive is not None:
896+
"""Follows code added in pandas #43504."""
897+
if closed is not no_default and inclusive is not None:
884898
raise ValueError(
885-
"Deprecated argument `closed` cannot be passed if "
886-
"argument `inclusive` is not None."
899+
"Following pandas, deprecated argument `closed` cannot be "
900+
"passed if argument `inclusive` is not None."
887901
)
888-
if closed is not _NoDefault:
902+
if closed is not no_default:
889903
inclusive = _translate_closed_to_inclusive(closed)
890904
elif inclusive is None:
891905
inclusive = "both"
@@ -899,8 +913,8 @@ def cftime_range(
899913
freq="D",
900914
normalize=False,
901915
name=None,
902-
closed=_NoDefault,
903-
inclusive=None,
916+
closed: NoDefault | SideOptions = no_default,
917+
inclusive: None | InclusiveOptions = None,
904918
calendar="standard",
905919
):
906920
"""Return a fixed frequency CFTimeIndex.
@@ -922,8 +936,15 @@ def cftime_range(
922936
closed : {"left", "right"} or None, default: _NoDefault
923937
Make the interval closed with respect to the given frequency to the
924938
"left", "right", or both sides (None).
939+
940+
.. deprecated:: 2023.01.1
941+
Following pandas, the `closed` argument is deprecated in favor
942+
of the `inclusive` argument, and will be removed in a future
943+
version of xarray.
925944
inclusive : {None, "both", "neither", "left", "right"}, default None
926-
Include boundaries; Whether to set each bound as closed or open.
945+
Include boundaries; whether to set each bound as closed or open.
946+
947+
.. versionadded:: 2023.01.1
927948
calendar : str, default: "standard"
928949
Calendar type for the datetimes.
929950
@@ -938,7 +959,6 @@ def cftime_range(
938959
features of ``pandas.date_range`` (e.g. specifying how the index is
939960
``closed`` on either side, or whether or not to ``normalize`` the start and
940961
end bounds); however, there are some notable exceptions:
941-
942962
- You cannot specify a ``tz`` (time zone) argument.
943963
- Start or end dates specified as partial-datetime strings must use the
944964
`ISO-8601 format <https://en.wikipedia.org/wiki/ISO_8601>`_.
@@ -1129,8 +1149,8 @@ def date_range(
11291149
tz=None,
11301150
normalize=False,
11311151
name=None,
1132-
closed=_NoDefault,
1133-
inclusive=None,
1152+
closed: NoDefault | SideOptions = no_default,
1153+
inclusive: None | InclusiveOptions = None,
11341154
calendar="standard",
11351155
use_cftime=None,
11361156
):
@@ -1160,8 +1180,15 @@ def date_range(
11601180
closed : {"left", "right"} or None, default: _NoDefault
11611181
Make the interval closed with respect to the given frequency to the
11621182
"left", "right", or both sides (None).
1183+
1184+
.. deprecated:: 2023.01.1
1185+
Following pandas, the `closed` argument is deprecated in favor
1186+
of the `inclusive` argument, and will be removed in a future
1187+
version of xarray.
11631188
inclusive : {None, "both", "neither", "left", "right"}, default None
1164-
Include boundaries; Whether to set each bound as closed or open.
1189+
Include boundaries; whether to set each bound as closed or open.
1190+
1191+
.. versionadded:: 2023.01.1
11651192
calendar : str, default: "standard"
11661193
Calendar type for the datetimes.
11671194
use_cftime : boolean, optional

xarray/core/types.py

+1
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ def dtype(self) -> np.dtype:
172172

173173
CoarsenBoundaryOptions = Literal["exact", "trim", "pad"]
174174
SideOptions = Literal["left", "right"]
175+
InclusiveOptions = Literal["both", "neither", "left", "right"]
175176

176177
ScaleOptions = Literal["linear", "symlog", "log", "logit", None]
177178
HueStyleOptions = Literal["continuous", "discrete", None]

xarray/tests/test_cftime_offsets.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -1411,16 +1411,16 @@ def as_timedelta_not_implemented_error():
14111411

14121412

14131413
@pytest.mark.parametrize("function", [cftime_range, date_range])
1414-
def test_cftime_or_date_range_closed_and_inclusive_error(function):
1414+
def test_cftime_or_date_range_closed_and_inclusive_error(function) -> None:
14151415
if function == cftime_range and not has_cftime:
14161416
pytest.skip("requires cftime")
14171417

1418-
with pytest.raises(ValueError, match="Deprecated argument `closed`"):
1418+
with pytest.raises(ValueError, match="Following pandas, deprecated"):
14191419
function("2000", periods=3, closed=None, inclusive="right")
14201420

14211421

14221422
@pytest.mark.parametrize("function", [cftime_range, date_range])
1423-
def test_cftime_or_date_range_invalid_closed_value(function):
1423+
def test_cftime_or_date_range_invalid_closed_value(function) -> None:
14241424
if function == cftime_range and not has_cftime:
14251425
pytest.skip("requires cftime")
14261426

@@ -1432,7 +1432,7 @@ def test_cftime_or_date_range_invalid_closed_value(function):
14321432
@pytest.mark.parametrize(
14331433
("closed", "inclusive"), [(None, "both"), ("left", "left"), ("right", "right")]
14341434
)
1435-
def test_cftime_or_date_range_closed(function, closed, inclusive):
1435+
def test_cftime_or_date_range_closed(function, closed, inclusive) -> None:
14361436
if function == cftime_range and not has_cftime:
14371437
pytest.skip("requires cftime")
14381438

@@ -1445,7 +1445,7 @@ def test_cftime_or_date_range_closed(function, closed, inclusive):
14451445

14461446

14471447
@pytest.mark.parametrize("function", [cftime_range, date_range])
1448-
def test_cftime_or_date_range_inclusive_None(function):
1448+
def test_cftime_or_date_range_inclusive_None(function) -> None:
14491449
if function == cftime_range and not has_cftime:
14501450
pytest.skip("requires cftime")
14511451

xarray/tests/test_cftimeindex_resample.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import annotations
22

33
import datetime
4+
from typing import TypedDict
45

56
import numpy as np
67
import pandas as pd
@@ -189,6 +190,12 @@ def test_calendars(calendar) -> None:
189190
xr.testing.assert_identical(da_cftime, da_datetime)
190191

191192

193+
class DateRangeKwargs(TypedDict):
194+
start: str
195+
periods: int
196+
freq: str
197+
198+
192199
@pytest.mark.parametrize("closed", ["left", "right"])
193200
@pytest.mark.parametrize(
194201
"origin",
@@ -198,7 +205,7 @@ def test_calendars(calendar) -> None:
198205
def test_origin(closed, origin) -> None:
199206
initial_freq, resample_freq = ("3H", "9H")
200207
start = "1969-12-31T12:07:01"
201-
index_kwargs = dict(start=start, periods=12, freq=initial_freq)
208+
index_kwargs: DateRangeKwargs = dict(start=start, periods=12, freq=initial_freq)
202209
datetime_index = pd.date_range(**index_kwargs)
203210
cftime_index = xr.cftime_range(**index_kwargs)
204211
da_datetimeindex = da(datetime_index)

0 commit comments

Comments
 (0)