Skip to content

Commit b7e6036

Browse files
authored
finalize deprecation of "closed"-parameter (#9882)
* finalize deprecation of "closed" to "inclusive" in date_range and cftime_range * add whats-new.rst entry * fix tests * fix test * remove stale function
1 parent 49502fc commit b7e6036

File tree

3 files changed

+8
-117
lines changed

3 files changed

+8
-117
lines changed

doc/whats-new.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ Breaking changes
3737

3838
Deprecations
3939
~~~~~~~~~~~~
40-
40+
- Finalize deprecation of ``closed`` parameters of :py:func:`cftime_range` and
41+
:py:func:`date_range` (:pull:`9882`).
42+
By `Kai Mühlbauer <https://github.com/kmuehlbauer>`_.
4143

4244
Bug fixes
4345
~~~~~~~~~

xarray/coding/cftime_offsets.py

Lines changed: 5 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -62,15 +62,13 @@
6262
)
6363
from xarray.core.common import _contains_datetime_like_objects, is_np_datetime_like
6464
from xarray.core.pdcompat import (
65-
NoDefault,
6665
count_not_none,
6766
nanosecond_precision_timestamp,
68-
no_default,
6967
)
7068
from xarray.core.utils import attempt_import, emit_user_level_warning
7169

7270
if TYPE_CHECKING:
73-
from xarray.core.types import InclusiveOptions, Self, SideOptions, TypeAlias
71+
from xarray.core.types import InclusiveOptions, Self, TypeAlias
7472

7573

7674
DayOption: TypeAlias = Literal["start", "end"]
@@ -943,51 +941,14 @@ def _generate_range(start, end, periods, offset):
943941
current = next_date
944942

945943

946-
def _translate_closed_to_inclusive(closed):
947-
"""Follows code added in pandas #43504."""
948-
emit_user_level_warning(
949-
"Following pandas, the `closed` parameter is deprecated in "
950-
"favor of the `inclusive` parameter, and will be removed in "
951-
"a future version of xarray.",
952-
FutureWarning,
953-
)
954-
if closed is None:
955-
inclusive = "both"
956-
elif closed in ("left", "right"):
957-
inclusive = closed
958-
else:
959-
raise ValueError(
960-
f"Argument `closed` must be either 'left', 'right', or None. "
961-
f"Got {closed!r}."
962-
)
963-
return inclusive
964-
965-
966-
def _infer_inclusive(
967-
closed: NoDefault | SideOptions, inclusive: InclusiveOptions | None
968-
) -> InclusiveOptions:
969-
"""Follows code added in pandas #43504."""
970-
if closed is not no_default and inclusive is not None:
971-
raise ValueError(
972-
"Following pandas, deprecated argument `closed` cannot be "
973-
"passed if argument `inclusive` is not None."
974-
)
975-
if closed is not no_default:
976-
return _translate_closed_to_inclusive(closed)
977-
if inclusive is None:
978-
return "both"
979-
return inclusive
980-
981-
982944
def cftime_range(
983945
start=None,
984946
end=None,
985947
periods=None,
986948
freq=None,
987949
normalize=False,
988950
name=None,
989-
closed: NoDefault | SideOptions = no_default,
990-
inclusive: None | InclusiveOptions = None,
951+
inclusive: InclusiveOptions = "both",
991952
calendar="standard",
992953
) -> CFTimeIndex:
993954
"""Return a fixed frequency CFTimeIndex.
@@ -1006,16 +967,7 @@ def cftime_range(
1006967
Normalize start/end dates to midnight before generating date range.
1007968
name : str, default: None
1008969
Name of the resulting index
1009-
closed : {None, "left", "right"}, default: "NO_DEFAULT"
1010-
Make the interval closed with respect to the given frequency to the
1011-
"left", "right", or both sides (None).
1012-
1013-
.. deprecated:: 2023.02.0
1014-
Following pandas, the ``closed`` parameter is deprecated in favor
1015-
of the ``inclusive`` parameter, and will be removed in a future
1016-
version of xarray.
1017-
1018-
inclusive : {None, "both", "neither", "left", "right"}, default None
970+
inclusive : {"both", "neither", "left", "right"}, default "both"
1019971
Include boundaries; whether to set each bound as closed or open.
1020972
1021973
.. versionadded:: 2023.02.0
@@ -1193,8 +1145,6 @@ def cftime_range(
11931145
offset = to_offset(freq)
11941146
dates = np.array(list(_generate_range(start, end, periods, offset)))
11951147

1196-
inclusive = _infer_inclusive(closed, inclusive)
1197-
11981148
if inclusive == "neither":
11991149
left_closed = False
12001150
right_closed = False
@@ -1229,8 +1179,7 @@ def date_range(
12291179
tz=None,
12301180
normalize=False,
12311181
name=None,
1232-
closed: NoDefault | SideOptions = no_default,
1233-
inclusive: None | InclusiveOptions = None,
1182+
inclusive: InclusiveOptions = "both",
12341183
calendar="standard",
12351184
use_cftime=None,
12361185
):
@@ -1257,20 +1206,10 @@ def date_range(
12571206
Normalize start/end dates to midnight before generating date range.
12581207
name : str, default: None
12591208
Name of the resulting index
1260-
closed : {None, "left", "right"}, default: "NO_DEFAULT"
1261-
Make the interval closed with respect to the given frequency to the
1262-
"left", "right", or both sides (None).
1263-
1264-
.. deprecated:: 2023.02.0
1265-
Following pandas, the `closed` parameter is deprecated in favor
1266-
of the `inclusive` parameter, and will be removed in a future
1267-
version of xarray.
1268-
1269-
inclusive : {None, "both", "neither", "left", "right"}, default: None
1209+
inclusive : {"both", "neither", "left", "right"}, default: "both"
12701210
Include boundaries; whether to set each bound as closed or open.
12711211
12721212
.. versionadded:: 2023.02.0
1273-
12741213
calendar : str, default: "standard"
12751214
Calendar type for the datetimes.
12761215
use_cftime : boolean, optional
@@ -1294,8 +1233,6 @@ def date_range(
12941233
if tz is not None:
12951234
use_cftime = False
12961235

1297-
inclusive = _infer_inclusive(closed, inclusive)
1298-
12991236
if _is_standard_calendar(calendar) and use_cftime is not True:
13001237
try:
13011238
return pd.date_range(

xarray/tests/test_cftime_offsets.py

Lines changed: 0 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1057,15 +1057,6 @@ def test_rollback(calendar, offset, initial_date_args, partial_expected_date_arg
10571057
False,
10581058
[(1, 1, 2), (1, 1, 3)],
10591059
),
1060-
(
1061-
"0001-01-01",
1062-
"0001-01-04",
1063-
None,
1064-
"D",
1065-
None,
1066-
False,
1067-
[(1, 1, 1), (1, 1, 2), (1, 1, 3), (1, 1, 4)],
1068-
),
10691060
(
10701061
"0001-01-01",
10711062
"0001-01-04",
@@ -1294,13 +1285,6 @@ def test_invalid_cftime_range_inputs(
12941285
cftime_range(start, end, periods, freq, inclusive=inclusive) # type: ignore[arg-type]
12951286

12961287

1297-
def test_invalid_cftime_arg() -> None:
1298-
with pytest.warns(
1299-
FutureWarning, match="Following pandas, the `closed` parameter is deprecated"
1300-
):
1301-
cftime_range("2000", "2001", None, "YE", closed="left")
1302-
1303-
13041288
_CALENDAR_SPECIFIC_MONTH_END_TESTS = [
13051289
("noleap", [(2, 28), (4, 30), (6, 30), (8, 31), (10, 31), (12, 31)]),
13061290
("all_leap", [(2, 29), (4, 30), (6, 30), (8, 31), (10, 31), (12, 31)]),
@@ -1534,15 +1518,6 @@ def as_timedelta_not_implemented_error():
15341518
tick.as_timedelta()
15351519

15361520

1537-
@pytest.mark.parametrize("function", [cftime_range, date_range])
1538-
def test_cftime_or_date_range_closed_and_inclusive_error(function: Callable) -> None:
1539-
if function == cftime_range and not has_cftime:
1540-
pytest.skip("requires cftime")
1541-
1542-
with pytest.raises(ValueError, match="Following pandas, deprecated"):
1543-
function("2000", periods=3, closed=None, inclusive="right")
1544-
1545-
15461521
@pytest.mark.parametrize("function", [cftime_range, date_range])
15471522
def test_cftime_or_date_range_invalid_inclusive_value(function: Callable) -> None:
15481523
if function == cftime_range and not has_cftime:
@@ -1552,29 +1527,6 @@ def test_cftime_or_date_range_invalid_inclusive_value(function: Callable) -> Non
15521527
function("2000", periods=3, inclusive="foo")
15531528

15541529

1555-
@pytest.mark.parametrize(
1556-
"function",
1557-
[
1558-
pytest.param(cftime_range, id="cftime", marks=requires_cftime),
1559-
pytest.param(date_range, id="date"),
1560-
],
1561-
)
1562-
@pytest.mark.parametrize(
1563-
("closed", "inclusive"), [(None, "both"), ("left", "left"), ("right", "right")]
1564-
)
1565-
def test_cftime_or_date_range_closed(
1566-
function: Callable,
1567-
closed: Literal["left", "right", None],
1568-
inclusive: Literal["left", "right", "both"],
1569-
) -> None:
1570-
with pytest.warns(FutureWarning, match="Following pandas"):
1571-
result_closed = function("2000-01-01", "2000-01-04", freq="D", closed=closed)
1572-
result_inclusive = function(
1573-
"2000-01-01", "2000-01-04", freq="D", inclusive=inclusive
1574-
)
1575-
np.testing.assert_equal(result_closed.values, result_inclusive.values)
1576-
1577-
15781530
@pytest.mark.parametrize("function", [cftime_range, date_range])
15791531
def test_cftime_or_date_range_inclusive_None(function) -> None:
15801532
if function == cftime_range and not has_cftime:

0 commit comments

Comments
 (0)