Skip to content

Commit d9dd128

Browse files
authored
Revert inclusive default change of IntervalDtype (#47367)
1 parent 37e6239 commit d9dd128

File tree

13 files changed

+217
-96
lines changed

13 files changed

+217
-96
lines changed

doc/source/reference/arrays.rst

+1
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,7 @@ Properties
304304
:toctree: api/
305305

306306
Interval.inclusive
307+
Interval.closed
307308
Interval.closed_left
308309
Interval.closed_right
309310
Interval.is_empty

pandas/_libs/interval.pyi

+2
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ class Interval(IntervalMixin, Generic[_OrderableT]):
6565
def right(self: Interval[_OrderableT]) -> _OrderableT: ...
6666
@property
6767
def inclusive(self) -> IntervalClosedType: ...
68+
@property
69+
def closed(self) -> IntervalClosedType: ...
6870
mid: _MidDescriptor
6971
length: _LengthDescriptor
7072
def __init__(

pandas/_libs/interval.pyx

+19-2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ from cpython.datetime cimport (
99
import_datetime,
1010
)
1111

12+
from pandas.util._exceptions import find_stack_level
13+
1214
import_datetime()
1315

1416
cimport cython
@@ -229,7 +231,7 @@ def _warning_interval(inclusive: str | None = None, closed: None | lib.NoDefault
229231
stacklevel=2,
230232
)
231233
if closed is None:
232-
inclusive = "both"
234+
inclusive = "right"
233235
elif closed in ("both", "neither", "left", "right"):
234236
inclusive = closed
235237
else:
@@ -364,7 +366,7 @@ cdef class Interval(IntervalMixin):
364366
inclusive, closed = _warning_interval(inclusive, closed)
365367

366368
if inclusive is None:
367-
inclusive = "both"
369+
inclusive = "right"
368370

369371
if inclusive not in VALID_CLOSED:
370372
raise ValueError(f"invalid option for 'inclusive': {inclusive}")
@@ -379,6 +381,21 @@ cdef class Interval(IntervalMixin):
379381
self.right = right
380382
self.inclusive = inclusive
381383

384+
@property
385+
def closed(self):
386+
"""
387+
Whether the interval is closed on the left-side, right-side, both or
388+
neither.
389+
390+
.. deprecated:: 1.5.0
391+
"""
392+
warnings.warn(
393+
"Attribute `closed` is deprecated in favor of `inclusive`.",
394+
FutureWarning,
395+
stacklevel=find_stack_level(),
396+
)
397+
return self.inclusive
398+
382399
def _validate_endpoint(self, endpoint):
383400
# GH 23013
384401
if not (is_integer_object(endpoint) or is_float_object(endpoint) or

pandas/_libs/intervaltree.pxi.in

+1-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ cdef class IntervalTree(IntervalMixin):
6969
inclusive, closed = _warning_interval(inclusive, closed)
7070

7171
if inclusive is None:
72-
inclusive = "both"
72+
inclusive = "right"
7373

7474
if inclusive not in ['left', 'right', 'both', 'neither']:
7575
raise ValueError("invalid option for 'inclusive': %s" % inclusive)

pandas/core/arrays/arrow/_arrow_utils.py

+12-9
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,8 @@
66
import numpy as np
77
import pyarrow
88

9-
from pandas._libs import lib
10-
from pandas._libs.interval import _warning_interval
119
from pandas.errors import PerformanceWarning
10+
from pandas.util._decorators import deprecate_kwarg
1211
from pandas.util._exceptions import find_stack_level
1312

1413
from pandas.core.arrays.interval import VALID_CLOSED
@@ -105,15 +104,10 @@ def to_pandas_dtype(self):
105104

106105

107106
class ArrowIntervalType(pyarrow.ExtensionType):
108-
def __init__(
109-
self,
110-
subtype,
111-
inclusive: str | None = None,
112-
closed: None | lib.NoDefault = lib.no_default,
113-
) -> None:
107+
@deprecate_kwarg(old_arg_name="closed", new_arg_name="inclusive")
108+
def __init__(self, subtype, inclusive: str) -> None:
114109
# attributes need to be set first before calling
115110
# super init (as that calls serialize)
116-
inclusive, closed = _warning_interval(inclusive, closed)
117111
assert inclusive in VALID_CLOSED
118112
self._closed = inclusive
119113
if not isinstance(subtype, pyarrow.DataType):
@@ -131,6 +125,15 @@ def subtype(self):
131125
def inclusive(self):
132126
return self._closed
133127

128+
@property
129+
def closed(self):
130+
warnings.warn(
131+
"Attribute `closed` is deprecated in favor of `inclusive`.",
132+
FutureWarning,
133+
stacklevel=find_stack_level(),
134+
)
135+
return self._closed
136+
134137
def __arrow_ext_serialize__(self):
135138
metadata = {"subtype": str(self.subtype), "inclusive": self.inclusive}
136139
return json.dumps(metadata).encode()

pandas/core/arrays/interval.py

+36-10
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
cast,
1616
overload,
1717
)
18+
import warnings
1819

1920
import numpy as np
2021

@@ -25,7 +26,6 @@
2526
VALID_CLOSED,
2627
Interval,
2728
IntervalMixin,
28-
_warning_interval,
2929
intervals_to_interval_bounds,
3030
)
3131
from pandas._libs.missing import NA
@@ -43,8 +43,10 @@
4343
from pandas.errors import IntCastingNaNError
4444
from pandas.util._decorators import (
4545
Appender,
46+
deprecate_kwarg,
4647
deprecate_nonkeyword_arguments,
4748
)
49+
from pandas.util._exceptions import find_stack_level
4850

4951
from pandas.core.dtypes.cast import LossySetitemError
5052
from pandas.core.dtypes.common import (
@@ -220,16 +222,15 @@ def ndim(self) -> Literal[1]:
220222
# ---------------------------------------------------------------------
221223
# Constructors
222224

225+
@deprecate_kwarg(old_arg_name="closed", new_arg_name="inclusive")
223226
def __new__(
224227
cls: type[IntervalArrayT],
225228
data,
226229
inclusive: str | None = None,
227-
closed: None | lib.NoDefault = lib.no_default,
228230
dtype: Dtype | None = None,
229231
copy: bool = False,
230232
verify_integrity: bool = True,
231233
):
232-
inclusive, closed = _warning_interval(inclusive, closed)
233234

234235
data = extract_array(data, extract_numpy=True)
235236

@@ -267,24 +268,22 @@ def __new__(
267268
)
268269

269270
@classmethod
271+
@deprecate_kwarg(old_arg_name="closed", new_arg_name="inclusive")
270272
def _simple_new(
271273
cls: type[IntervalArrayT],
272274
left,
273275
right,
274276
inclusive=None,
275-
closed: None | lib.NoDefault = lib.no_default,
276277
copy: bool = False,
277278
dtype: Dtype | None = None,
278279
verify_integrity: bool = True,
279280
) -> IntervalArrayT:
280281
result = IntervalMixin.__new__(cls)
281282

282-
inclusive, closed = _warning_interval(inclusive, closed)
283-
284283
if inclusive is None and isinstance(dtype, IntervalDtype):
285284
inclusive = dtype.inclusive
286285

287-
inclusive = inclusive or "both"
286+
inclusive = inclusive or "right"
288287

289288
left = ensure_index(left, copy=copy)
290289
right = ensure_index(right, copy=copy)
@@ -424,13 +423,17 @@ def _from_factorized(
424423
),
425424
}
426425
)
426+
@deprecate_kwarg(old_arg_name="closed", new_arg_name="inclusive")
427427
def from_breaks(
428428
cls: type[IntervalArrayT],
429429
breaks,
430-
inclusive="both",
430+
inclusive: IntervalClosedType | None = None,
431431
copy: bool = False,
432432
dtype: Dtype | None = None,
433433
) -> IntervalArrayT:
434+
if inclusive is None:
435+
inclusive = "right"
436+
434437
breaks = _maybe_convert_platform_interval(breaks)
435438

436439
return cls.from_arrays(
@@ -501,14 +504,19 @@ def from_breaks(
501504
),
502505
}
503506
)
507+
@deprecate_kwarg(old_arg_name="closed", new_arg_name="inclusive")
504508
def from_arrays(
505509
cls: type[IntervalArrayT],
506510
left,
507511
right,
508-
inclusive="both",
512+
inclusive: IntervalClosedType | None = None,
509513
copy: bool = False,
510514
dtype: Dtype | None = None,
511515
) -> IntervalArrayT:
516+
517+
if inclusive is None:
518+
inclusive = "right"
519+
512520
left = _maybe_convert_platform_interval(left)
513521
right = _maybe_convert_platform_interval(right)
514522

@@ -570,13 +578,17 @@ def from_arrays(
570578
),
571579
}
572580
)
581+
@deprecate_kwarg(old_arg_name="closed", new_arg_name="inclusive")
573582
def from_tuples(
574583
cls: type[IntervalArrayT],
575584
data,
576-
inclusive="both",
585+
inclusive=None,
577586
copy: bool = False,
578587
dtype: Dtype | None = None,
579588
) -> IntervalArrayT:
589+
if inclusive is None:
590+
inclusive = "right"
591+
580592
if len(data):
581593
left, right = [], []
582594
else:
@@ -1355,6 +1367,19 @@ def inclusive(self) -> IntervalClosedType:
13551367
"""
13561368
return self.dtype.inclusive
13571369

1370+
@property
1371+
def closed(self) -> IntervalClosedType:
1372+
"""
1373+
Whether the intervals are closed on the left-side, right-side, both or
1374+
neither.
1375+
"""
1376+
warnings.warn(
1377+
"Attribute `closed` is deprecated in favor of `inclusive`.",
1378+
FutureWarning,
1379+
stacklevel=find_stack_level(),
1380+
)
1381+
return self.dtype.inclusive
1382+
13581383
_interval_shared_docs["set_closed"] = textwrap.dedent(
13591384
"""
13601385
Return an %(klass)s identical to the current one, but closed on the
@@ -1395,6 +1420,7 @@ def inclusive(self) -> IntervalClosedType:
13951420
),
13961421
}
13971422
)
1423+
@deprecate_kwarg(old_arg_name="closed", new_arg_name="inclusive")
13981424
def set_closed(
13991425
self: IntervalArrayT, inclusive: IntervalClosedType
14001426
) -> IntervalArrayT:

pandas/core/dtypes/dtypes.py

+11
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
MutableMapping,
1111
cast,
1212
)
13+
import warnings
1314

1415
import numpy as np
1516
import pytz
@@ -40,6 +41,7 @@
4041
npt,
4142
type_t,
4243
)
44+
from pandas.util._exceptions import find_stack_level
4345

4446
from pandas.core.dtypes.base import (
4547
ExtensionDtype,
@@ -1176,6 +1178,15 @@ def _can_hold_na(self) -> bool:
11761178
def inclusive(self):
11771179
return self._closed
11781180

1181+
@property
1182+
def closed(self):
1183+
warnings.warn(
1184+
"Attribute `closed` is deprecated in favor of `inclusive`.",
1185+
FutureWarning,
1186+
stacklevel=find_stack_level(),
1187+
)
1188+
return self._closed
1189+
11791190
@property
11801191
def subtype(self):
11811192
"""

0 commit comments

Comments
 (0)