1"""define generic base classes for pandas objects"""
2
3from __future__ import annotations
4
5from typing import (
6 TYPE_CHECKING,
7 Any,
8 cast,
9)
10
11if TYPE_CHECKING:
12 from pandas import (
13 Categorical,
14 CategoricalIndex,
15 DataFrame,
16 DatetimeIndex,
17 Index,
18 IntervalIndex,
19 MultiIndex,
20 PeriodIndex,
21 RangeIndex,
22 Series,
23 TimedeltaIndex,
24 )
25 from pandas.core.arrays import (
26 DatetimeArray,
27 ExtensionArray,
28 NumpyExtensionArray,
29 PeriodArray,
30 TimedeltaArray,
31 )
32 from pandas.core.generic import NDFrame
33
34
35# define abstract base classes to enable isinstance type checking on our
36# objects
37def create_pandas_abc_type(
38 name: str, attr: str, comp: tuple[str, ...] | set[str]
39) -> type:
40 def _check(inst: Any) -> bool:
41 return getattr(inst, attr, "_typ") in comp
42
43 # https://github.com/python/mypy/issues/1006
44 # error: 'classmethod' used with a non-method
45 @classmethod # type: ignore[misc]
46 def _instancecheck(cls: type, inst: Any) -> bool:
47 return _check(inst) and not isinstance(inst, type)
48
49 @classmethod # type: ignore[misc]
50 def _subclasscheck(cls: type, inst: Any) -> bool:
51 # Raise instead of returning False
52 # This is consistent with default __subclasscheck__ behavior
53 if not isinstance(inst, type):
54 raise TypeError("issubclass() arg 1 must be a class")
55
56 return _check(inst)
57
58 dct = {"__instancecheck__": _instancecheck, "__subclasscheck__": _subclasscheck}
59 meta = type("ABCBase", (type,), dct)
60 return meta(name, (), dct)
61
62
63ABCRangeIndex = cast(
64 "type[RangeIndex]",
65 create_pandas_abc_type("ABCRangeIndex", "_typ", ("rangeindex",)),
66)
67ABCMultiIndex = cast(
68 "type[MultiIndex]",
69 create_pandas_abc_type("ABCMultiIndex", "_typ", ("multiindex",)),
70)
71ABCDatetimeIndex = cast(
72 "type[DatetimeIndex]",
73 create_pandas_abc_type("ABCDatetimeIndex", "_typ", ("datetimeindex",)),
74)
75ABCTimedeltaIndex = cast(
76 "type[TimedeltaIndex]",
77 create_pandas_abc_type("ABCTimedeltaIndex", "_typ", ("timedeltaindex",)),
78)
79ABCPeriodIndex = cast(
80 "type[PeriodIndex]",
81 create_pandas_abc_type("ABCPeriodIndex", "_typ", ("periodindex",)),
82)
83ABCCategoricalIndex = cast(
84 "type[CategoricalIndex]",
85 create_pandas_abc_type("ABCCategoricalIndex", "_typ", ("categoricalindex",)),
86)
87ABCIntervalIndex = cast(
88 "type[IntervalIndex]",
89 create_pandas_abc_type("ABCIntervalIndex", "_typ", ("intervalindex",)),
90)
91ABCIndex = cast(
92 "type[Index]",
93 create_pandas_abc_type(
94 "ABCIndex",
95 "_typ",
96 {
97 "index",
98 "rangeindex",
99 "multiindex",
100 "datetimeindex",
101 "timedeltaindex",
102 "periodindex",
103 "categoricalindex",
104 "intervalindex",
105 },
106 ),
107)
108
109
110ABCNDFrame = cast(
111 "type[NDFrame]",
112 create_pandas_abc_type("ABCNDFrame", "_typ", ("series", "dataframe")),
113)
114ABCSeries = cast(
115 "type[Series]",
116 create_pandas_abc_type("ABCSeries", "_typ", ("series",)),
117)
118ABCDataFrame = cast(
119 "type[DataFrame]", create_pandas_abc_type("ABCDataFrame", "_typ", ("dataframe",))
120)
121
122ABCCategorical = cast(
123 "type[Categorical]",
124 create_pandas_abc_type("ABCCategorical", "_typ", ("categorical",)),
125)
126ABCDatetimeArray = cast(
127 "type[DatetimeArray]",
128 create_pandas_abc_type("ABCDatetimeArray", "_typ", ("datetimearray",)),
129)
130ABCTimedeltaArray = cast(
131 "type[TimedeltaArray]",
132 create_pandas_abc_type("ABCTimedeltaArray", "_typ", ("timedeltaarray",)),
133)
134ABCPeriodArray = cast(
135 "type[PeriodArray]",
136 create_pandas_abc_type("ABCPeriodArray", "_typ", ("periodarray",)),
137)
138ABCExtensionArray = cast(
139 "type[ExtensionArray]",
140 create_pandas_abc_type(
141 "ABCExtensionArray",
142 "_typ",
143 # Note: IntervalArray and SparseArray are included bc they have _typ="extension"
144 {"extension", "categorical", "periodarray", "datetimearray", "timedeltaarray"},
145 ),
146)
147ABCNumpyExtensionArray = cast(
148 "type[NumpyExtensionArray]",
149 create_pandas_abc_type("ABCNumpyExtensionArray", "_typ", ("npy_extension",)),
150)