Skip to content

Commit 161f3f7

Browse files
meeseeksmachinejbrockmendel
authored andcommitted
Backport PR #30860: REF: gradually move ExtensionIndex delegation to use inherit_names (#31375)
Co-authored-by: jbrockmendel <[email protected]>
1 parent d80045e commit 161f3f7

File tree

4 files changed

+56
-58
lines changed

4 files changed

+56
-58
lines changed

pandas/core/indexes/category.py

+16-14
Original file line numberDiff line numberDiff line change
@@ -27,19 +27,29 @@
2727
import pandas.core.common as com
2828
import pandas.core.indexes.base as ibase
2929
from pandas.core.indexes.base import Index, _index_shared_docs, maybe_extract_name
30-
from pandas.core.indexes.extension import ExtensionIndex
30+
from pandas.core.indexes.extension import ExtensionIndex, inherit_names
3131
import pandas.core.missing as missing
3232
from pandas.core.ops import get_op_result_name
3333

3434
_index_doc_kwargs = dict(ibase._index_doc_kwargs)
3535
_index_doc_kwargs.update(dict(target_klass="CategoricalIndex"))
3636

3737

38-
@accessor.delegate_names(
39-
delegate=Categorical,
40-
accessors=["codes", "categories", "ordered"],
41-
typ="property",
42-
overwrite=True,
38+
@inherit_names(
39+
[
40+
"argsort",
41+
"_internal_get_values",
42+
"tolist",
43+
"codes",
44+
"categories",
45+
"ordered",
46+
"_reverse_indexer",
47+
"searchsorted",
48+
"is_dtype_equal",
49+
"min",
50+
"max",
51+
],
52+
Categorical,
4353
)
4454
@accessor.delegate_names(
4555
delegate=Categorical,
@@ -52,14 +62,6 @@
5262
"set_categories",
5363
"as_ordered",
5464
"as_unordered",
55-
"min",
56-
"max",
57-
"is_dtype_equal",
58-
"tolist",
59-
"_internal_get_values",
60-
"_reverse_indexer",
61-
"searchsorted",
62-
"argsort",
6365
],
6466
typ="method",
6567
overwrite=True,

pandas/core/indexes/extension.py

+24-5
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from pandas.core.ops import get_op_result_name
1717

1818

19-
def inherit_from_data(name: str, delegate, cache: bool = False):
19+
def inherit_from_data(name: str, delegate, cache: bool = False, wrap: bool = False):
2020
"""
2121
Make an alias for a method of the underlying ExtensionArray.
2222
@@ -27,6 +27,8 @@ def inherit_from_data(name: str, delegate, cache: bool = False):
2727
delegate : class
2828
cache : bool, default False
2929
Whether to convert wrapped properties into cache_readonly
30+
wrap : bool, default False
31+
Whether to wrap the inherited result in an Index.
3032
3133
Returns
3234
-------
@@ -37,12 +39,23 @@ def inherit_from_data(name: str, delegate, cache: bool = False):
3739

3840
if isinstance(attr, property):
3941
if cache:
40-
method = cache_readonly(attr.fget)
42+
43+
def cached(self):
44+
return getattr(self._data, name)
45+
46+
cached.__name__ = name
47+
cached.__doc__ = attr.__doc__
48+
method = cache_readonly(cached)
4149

4250
else:
4351

4452
def fget(self):
45-
return getattr(self._data, name)
53+
result = getattr(self._data, name)
54+
if wrap:
55+
if isinstance(result, type(self._data)):
56+
return type(self)._simple_new(result, name=self.name)
57+
return Index(result, name=self.name)
58+
return result
4659

4760
def fset(self, value):
4861
setattr(self._data, name, value)
@@ -60,14 +73,18 @@ def fset(self, value):
6073

6174
def method(self, *args, **kwargs):
6275
result = attr(self._data, *args, **kwargs)
76+
if wrap:
77+
if isinstance(result, type(self._data)):
78+
return type(self)._simple_new(result, name=self.name)
79+
return Index(result, name=self.name)
6380
return result
6481

6582
method.__name__ = name
6683
method.__doc__ = attr.__doc__
6784
return method
6885

6986

70-
def inherit_names(names: List[str], delegate, cache: bool = False):
87+
def inherit_names(names: List[str], delegate, cache: bool = False, wrap: bool = False):
7188
"""
7289
Class decorator to pin attributes from an ExtensionArray to a Index subclass.
7390
@@ -76,11 +93,13 @@ def inherit_names(names: List[str], delegate, cache: bool = False):
7693
names : List[str]
7794
delegate : class
7895
cache : bool, default False
96+
wrap : bool, default False
97+
Whether to wrap the inherited result in an Index.
7998
"""
8099

81100
def wrapper(cls):
82101
for name in names:
83-
meth = inherit_from_data(name, delegate, cache=cache)
102+
meth = inherit_from_data(name, delegate, cache=cache, wrap=wrap)
84103
setattr(cls, name, meth)
85104

86105
return cls

pandas/core/indexes/interval.py

+12-34
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@
3737
from pandas.core.dtypes.generic import ABCSeries
3838
from pandas.core.dtypes.missing import isna
3939

40-
from pandas.core import accessor
4140
from pandas.core.algorithms import take_1d
4241
from pandas.core.arrays.interval import IntervalArray, _interval_shared_docs
4342
import pandas.core.common as com
@@ -183,31 +182,27 @@ def func(intvidx_self, other, sort=False):
183182
),
184183
)
185184
)
186-
@accessor.delegate_names(
187-
delegate=IntervalArray,
188-
accessors=["length", "size", "left", "right", "mid", "closed", "dtype"],
189-
typ="property",
190-
overwrite=True,
191-
)
192-
@accessor.delegate_names(
193-
delegate=IntervalArray,
194-
accessors=[
185+
@inherit_names(["set_closed", "to_tuples"], IntervalArray, wrap=True)
186+
@inherit_names(
187+
[
188+
"__len__",
195189
"__array__",
196190
"overlaps",
197191
"contains",
198-
"__len__",
199-
"set_closed",
200-
"to_tuples",
192+
"size",
193+
"dtype",
194+
"left",
195+
"right",
196+
"length",
201197
],
202-
typ="method",
203-
overwrite=True,
198+
IntervalArray,
204199
)
205200
@inherit_names(
206-
["is_non_overlapping_monotonic", "mid", "_ndarray_values"],
201+
["is_non_overlapping_monotonic", "mid", "_ndarray_values", "closed"],
207202
IntervalArray,
208203
cache=True,
209204
)
210-
class IntervalIndex(IntervalMixin, ExtensionIndex, accessor.PandasDelegate):
205+
class IntervalIndex(IntervalMixin, ExtensionIndex):
211206
_typ = "intervalindex"
212207
_comparables = ["name"]
213208
_attributes = ["name", "closed"]
@@ -218,8 +213,6 @@ class IntervalIndex(IntervalMixin, ExtensionIndex, accessor.PandasDelegate):
218213
# Immutable, so we are able to cache computations like isna in '_mask'
219214
_mask = None
220215

221-
_raw_inherit = {"__array__", "overlaps", "contains"}
222-
223216
# --------------------------------------------------------------------
224217
# Constructors
225218

@@ -1176,21 +1169,6 @@ def is_all_dates(self) -> bool:
11761169

11771170
# TODO: arithmetic operations
11781171

1179-
def _delegate_property_get(self, name, *args, **kwargs):
1180-
""" method delegation to the ._values """
1181-
prop = getattr(self._data, name)
1182-
return prop # no wrapping for now
1183-
1184-
def _delegate_method(self, name, *args, **kwargs):
1185-
""" method delegation to the ._data """
1186-
method = getattr(self._data, name)
1187-
res = method(*args, **kwargs)
1188-
if is_scalar(res) or name in self._raw_inherit:
1189-
return res
1190-
if isinstance(res, IntervalArray):
1191-
return type(self)._simple_new(res, name=self.name)
1192-
return Index(res)
1193-
11941172
# GH#30817 until IntervalArray implements inequalities, get them from Index
11951173
def __lt__(self, other):
11961174
return Index.__lt__(self, other)

pandas/core/indexes/timedeltas.py

+4-5
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,12 @@ class TimedeltaDelegateMixin(DatetimelikeDelegateMixin):
4343
_raw_methods = {"to_pytimedelta", "sum", "std", "median", "_format_native_types"}
4444

4545
_delegated_properties = TimedeltaArray._datetimelike_ops + list(_raw_properties)
46-
_delegated_methods = (
47-
TimedeltaArray._datetimelike_methods
48-
+ list(_raw_methods)
49-
+ ["_box_values", "__neg__", "__pos__", "__abs__"]
50-
)
46+
_delegated_methods = TimedeltaArray._datetimelike_methods + list(_raw_methods)
5147

5248

49+
@inherit_names(
50+
["_box_values", "__neg__", "__pos__", "__abs__"], TimedeltaArray, wrap=True
51+
)
5352
@inherit_names(
5453
[
5554
"_bool_ops",

0 commit comments

Comments
 (0)