Skip to content

Commit c73ec4c

Browse files
authored
feat(arithmetic): ✖️ multiplication (#1397)
* feat(arithmetic): multiplication * feat: mul for timedelta indexes * doc and refactor * complex * bool * chore(poetry): update pyright * fix(comment): #1397 (comment) * fix(comment): #1397 (review) * chore(pyrefly): remove-unused-ignores * chore(comment): #1397 (comment) * fix(comment): #1397 (comment) * fix(nightly): pytest * chore: naming * fix(comment): pandas-dev/pandas#62595 #1397 (comment) * fix: mypy
1 parent 986a7f1 commit c73ec4c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+2382
-571
lines changed

pandas-stubs/_libs/tslibs/period.pyi

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ from pandas import (
1313
Timedelta,
1414
TimedeltaIndex,
1515
)
16+
from typing_extensions import Self
1617

1718
from pandas._libs.tslibs import NaTType
1819
from pandas._libs.tslibs.offsets import BaseOffset
@@ -87,15 +88,23 @@ class Period(PeriodMixin):
8788
@overload
8889
def __sub__(self, other: TimedeltaIndex) -> PeriodIndex: ...
8990
@overload
90-
def __add__(self, other: _PeriodAddSub) -> Period: ...
91+
def __add__(self, other: _PeriodAddSub) -> Self: ...
9192
@overload
9293
def __add__(self, other: NaTType) -> NaTType: ...
9394
@overload
9495
def __add__(self, other: Index) -> PeriodIndex: ...
96+
# Ignored due to indecipherable error from mypy:
97+
# Forward operator "__add__" is not callable [misc]
9598
@overload
96-
def __add__(
97-
self, other: Series[BaseOffset] | Series[Timedelta]
98-
) -> Series[Period]: ... # pyrefly: ignore[bad-specialization]
99+
def __radd__(self, other: _PeriodAddSub) -> Self: ... # type: ignore[misc]
100+
@overload
101+
def __radd__(self, other: NaTType) -> NaTType: ...
102+
# Real signature is -> PeriodIndex, but conflicts with Index.__add__
103+
# Changing Index is very hard due to Index inheritance
104+
# Signatures of "__radd__" of "Period" and "__add__" of "Index"
105+
# are unsafely overlapping
106+
@overload
107+
def __radd__(self, other: Index) -> PeriodIndex: ...
99108
# ignore[misc] here because we know all other comparisons
100109
# are False, so we use Literal[False]
101110
@overload
@@ -168,18 +177,6 @@ class Period(PeriodMixin):
168177
def __ne__(self, other: np_ndarray[ShapeT, np.object_]) -> np_ndarray[ShapeT, np.bool]: ... # type: ignore[overload-overlap]
169178
@overload
170179
def __ne__(self, other: object) -> Literal[True]: ...
171-
# Ignored due to indecipherable error from mypy:
172-
# Forward operator "__add__" is not callable [misc]
173-
@overload
174-
def __radd__(self, other: _PeriodAddSub) -> Period: ... # type: ignore[misc]
175-
# Real signature is -> PeriodIndex, but conflicts with Index.__add__
176-
# Changing Index is very hard due to Index inheritance
177-
# Signatures of "__radd__" of "Period" and "__add__" of "Index"
178-
# are unsafely overlapping
179-
@overload
180-
def __radd__(self, other: Index) -> Index: ...
181-
@overload
182-
def __radd__(self, other: NaTType) -> NaTType: ...
183180
@property
184181
def day(self) -> int: ...
185182
@property

pandas-stubs/_libs/tslibs/timedeltas.pyi

Lines changed: 38 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
# pyright: strict
2-
import datetime as dt
3-
from datetime import timedelta
2+
from datetime import (
3+
date,
4+
datetime,
5+
timedelta,
6+
)
47
from typing import (
58
ClassVar,
69
Literal,
@@ -133,17 +136,17 @@ class Timedelta(timedelta):
133136
def ceil(self, freq: str | BaseOffset) -> Self: ...
134137
@property
135138
def resolution_string(self) -> str: ...
136-
# Override due to more types supported than dt.timedelta
139+
# Override due to more types supported than timedelta
137140
@overload # type: ignore[override]
138-
def __add__(self, other: dt.datetime | np.datetime64) -> Timestamp: ...
141+
def __add__(self, other: datetime | np.datetime64) -> Timestamp: ...
139142
@overload
140143
def __add__(self, other: timedelta | np.timedelta64) -> Self: ...
141144
@overload
142145
def __add__(self, other: NaTType) -> NaTType: ...
143146
@overload
144147
def __add__(self, other: Period) -> Period: ...
145148
@overload
146-
def __add__(self, other: dt.date) -> dt.date: ...
149+
def __add__(self, other: date) -> date: ...
147150
@overload
148151
def __add__(
149152
self, other: np_ndarray[ShapeT, np.timedelta64]
@@ -153,13 +156,13 @@ class Timedelta(timedelta):
153156
self, other: np_ndarray[ShapeT, np.datetime64]
154157
) -> np_ndarray[ShapeT, np.datetime64]: ...
155158
@overload
156-
def __radd__(self, other: dt.datetime | np.datetime64) -> Timestamp: ... # type: ignore[misc]
159+
def __radd__(self, other: datetime | np.datetime64) -> Timestamp: ... # type: ignore[misc]
157160
@overload
158161
def __radd__(self, other: timedelta | np.timedelta64) -> Self: ...
159162
@overload
160163
def __radd__(self, other: NaTType) -> NaTType: ...
161164
@overload
162-
def __radd__(self, other: dt.date) -> dt.date: ...
165+
def __radd__(self, other: date) -> date: ...
163166
@overload
164167
def __radd__(
165168
self, other: np_ndarray[ShapeT, np.timedelta64]
@@ -168,9 +171,9 @@ class Timedelta(timedelta):
168171
def __radd__(
169172
self, other: np_ndarray[ShapeT, np.datetime64]
170173
) -> np_ndarray[ShapeT, np.datetime64]: ...
171-
# Override due to more types supported than dt.timedelta
174+
# Override due to more types supported than timedelta
172175
@overload # type: ignore[override]
173-
def __sub__(self, other: timedelta | Timedelta | np.timedelta64) -> Timedelta: ...
176+
def __sub__(self, other: timedelta | Timedelta | np.timedelta64) -> Self: ...
174177
@overload
175178
def __sub__(self, other: NaTType) -> NaTType: ...
176179
@overload
@@ -180,11 +183,9 @@ class Timedelta(timedelta):
180183
@overload
181184
def __sub__(self, other: pd.TimedeltaIndex) -> TimedeltaIndex: ...
182185
@overload
183-
def __sub__(self, other: Series[pd.Timedelta]) -> Series[pd.Timedelta]: ...
184-
@overload
185-
def __rsub__(self, other: timedelta | Timedelta | np.timedelta64) -> Timedelta: ...
186+
def __rsub__(self, other: timedelta | Timedelta | np.timedelta64) -> Self: ...
186187
@overload
187-
def __rsub__(self, other: dt.datetime | Timestamp | np.datetime64) -> Timestamp: ... # type: ignore[misc]
188+
def __rsub__(self, other: datetime | Timestamp | np.datetime64) -> Timestamp: ... # type: ignore[misc]
188189
@overload
189190
def __rsub__(self, other: NaTType) -> NaTType: ...
190191
@overload
@@ -203,44 +204,31 @@ class Timedelta(timedelta):
203204
) -> np_ndarray[ShapeT, np.timedelta64]: ...
204205
@overload
205206
def __rsub__(self, other: pd.TimedeltaIndex) -> pd.TimedeltaIndex: ...
206-
def __neg__(self) -> Timedelta: ...
207-
def __pos__(self) -> Timedelta: ...
208-
def __abs__(self) -> Timedelta: ...
209-
# Override due to more types supported than dt.timedelta
207+
def __neg__(self) -> Self: ...
208+
def __pos__(self) -> Self: ...
209+
def __abs__(self) -> Self: ...
210+
# Override due to more types supported than timedelta
210211
@overload # type: ignore[override]
211-
def __mul__(self, other: float) -> Timedelta: ...
212+
def __mul__(self, other: float) -> Self: ...
212213
@overload
213214
def __mul__(
214-
self, other: np_ndarray[ShapeT, np.integer] | np_ndarray[ShapeT, np.floating]
215+
self, other: np_ndarray[ShapeT, np.bool_ | np.integer | np.floating]
215216
) -> np_ndarray[ShapeT, np.timedelta64]: ...
216217
@overload
217-
def __mul__(self, other: Series[int]) -> Series[Timedelta]: ...
218-
@overload
219-
def __mul__(self, other: Series[float]) -> Series[Timedelta]: ...
220-
@overload
221-
def __mul__(self, other: Index[int] | Index[float]) -> TimedeltaIndex: ...
222-
@overload
223-
def __rmul__(self, other: float) -> Timedelta: ...
218+
def __rmul__(self, other: float) -> Self: ...
224219
@overload
225220
def __rmul__(
226-
self, other: np_ndarray[ShapeT, np.floating] | np_ndarray[ShapeT, np.integer]
221+
self, other: np_ndarray[ShapeT, np.bool_ | np.integer | np.floating]
227222
) -> np_ndarray[ShapeT, np.timedelta64]: ...
228-
@overload
229-
def __rmul__(self, other: Series[int]) -> Series[Timedelta]: ...
230-
@overload
231-
def __rmul__(self, other: Series[float]) -> Series[Timedelta]: ...
232-
# maybe related to https://github.com/python/mypy/issues/10755
233-
@overload
234-
def __rmul__(self, other: Index[int] | Index[float]) -> TimedeltaIndex: ...
235-
# Override due to more types supported than dt.timedelta
223+
# Override due to more types supported than timedelta
236224
# error: Signature of "__floordiv__" incompatible with supertype "timedelta"
237225
@overload # type: ignore[override]
238226
def __floordiv__(self, other: timedelta | Timedelta | np.timedelta64) -> int: ...
239227
@overload
240-
def __floordiv__(self, other: float) -> Timedelta: ...
228+
def __floordiv__(self, other: float) -> Self: ...
241229
@overload
242230
def __floordiv__(
243-
self, other: np_ndarray[ShapeT, np.integer] | np_ndarray[ShapeT, np.floating]
231+
self, other: np_ndarray[ShapeT, np.integer | np.floating]
244232
) -> np_ndarray[ShapeT, np.timedelta64]: ...
245233
@overload
246234
def __floordiv__(
@@ -264,14 +252,14 @@ class Timedelta(timedelta):
264252
def __rfloordiv__(
265253
self, other: np_ndarray[ShapeT, np.timedelta64]
266254
) -> np_ndarray[ShapeT, np.int_]: ...
267-
# Override due to more types supported than dt.timedelta
255+
# Override due to more types supported than timedelta
268256
@overload # type: ignore[override]
269257
def __truediv__(self, other: timedelta | Timedelta | NaTType) -> float: ...
270258
@overload
271-
def __truediv__(self, other: float) -> Timedelta: ...
259+
def __truediv__(self, other: float) -> Self: ...
272260
@overload
273261
def __truediv__(
274-
self, other: np_ndarray[ShapeT, np.integer] | np_ndarray[ShapeT, np.floating]
262+
self, other: np_ndarray[ShapeT, np.integer | np.floating]
275263
) -> np_ndarray[ShapeT, np.timedelta64]: ...
276264
@overload
277265
def __truediv__(self, other: Series[Timedelta]) -> Series[float]: ...
@@ -282,7 +270,7 @@ class Timedelta(timedelta):
282270
@overload
283271
def __truediv__(self, other: Index[int] | Index[float]) -> TimedeltaIndex: ...
284272
def __rtruediv__(self, other: timedelta | Timedelta | NaTType) -> float: ...
285-
# Override due to more types supported than dt.timedelta
273+
# Override due to more types supported than timedelta
286274
@overload
287275
def __eq__(self, other: timedelta | Timedelta | np.timedelta64) -> bool: ... # type: ignore[overload-overlap] # pyright: ignore[reportOverlappingOverload]
288276
@overload
@@ -295,7 +283,7 @@ class Timedelta(timedelta):
295283
) -> np_ndarray[ShapeT, np.bool_]: ...
296284
@overload
297285
def __eq__(self, other: object) -> Literal[False]: ...
298-
# Override due to more types supported than dt.timedelta
286+
# Override due to more types supported than timedelta
299287
@overload
300288
def __ne__(self, other: timedelta | Timedelta | np.timedelta64) -> bool: ... # type: ignore[overload-overlap] # pyright: ignore[reportOverlappingOverload]
301289
@overload
@@ -308,18 +296,18 @@ class Timedelta(timedelta):
308296
) -> np_ndarray[ShapeT, np.bool_]: ...
309297
@overload
310298
def __ne__(self, other: object) -> Literal[True]: ...
311-
# Override due to more types supported than dt.timedelta
299+
# Override due to more types supported than timedelta
312300
@overload # type: ignore[override]
313-
def __mod__(self, other: timedelta) -> Timedelta: ...
301+
def __mod__(self, other: timedelta) -> Self: ...
314302
@overload
315-
def __mod__(self, other: float) -> Timedelta: ...
303+
def __mod__(self, other: float) -> Self: ...
316304
@overload
317305
def __mod__(self, other: Series[int] | Series[float]) -> Series[Timedelta]: ...
318306
@overload
319307
def __mod__(self, other: Index[int] | Index[float]) -> TimedeltaIndex: ...
320308
@overload
321309
def __mod__(
322-
self, other: np_ndarray[ShapeT, np.integer] | np_ndarray[ShapeT, np.floating]
310+
self, other: np_ndarray[ShapeT, np.integer | np.floating]
323311
) -> np_ndarray[ShapeT, np.timedelta64]: ...
324312
@overload
325313
def __mod__(
@@ -328,7 +316,7 @@ class Timedelta(timedelta):
328316
def __divmod__(self, other: timedelta) -> tuple[int, Timedelta]: ...
329317
# Mypy complains Forward operator "<inequality op>" is not callable, so ignore misc
330318
# for le, lt ge and gt
331-
# Override due to more types supported than dt.timedelta
319+
# Override due to more types supported than timedelta
332320
@overload # type: ignore[override]
333321
def __le__(self, other: timedelta | Timedelta | np.timedelta64) -> bool: ... # type: ignore[misc]
334322
@overload
@@ -339,7 +327,7 @@ class Timedelta(timedelta):
339327
) -> np_ndarray[ShapeT, np.bool_]: ...
340328
@overload
341329
def __le__(self, other: Series[pd.Timedelta]) -> Series[bool]: ...
342-
# Override due to more types supported than dt.timedelta
330+
# Override due to more types supported than timedelta
343331
@overload # type: ignore[override]
344332
def __lt__(self, other: timedelta | Timedelta | np.timedelta64) -> bool: ... # type: ignore[misc]
345333
@overload
@@ -350,7 +338,7 @@ class Timedelta(timedelta):
350338
) -> np_ndarray[ShapeT, np.bool_]: ...
351339
@overload
352340
def __lt__(self, other: Series[pd.Timedelta]) -> Series[bool]: ...
353-
# Override due to more types supported than dt.timedelta
341+
# Override due to more types supported than timedelta
354342
@overload # type: ignore[override]
355343
def __ge__(self, other: timedelta | Timedelta | np.timedelta64) -> bool: ... # type: ignore[misc]
356344
@overload
@@ -361,7 +349,7 @@ class Timedelta(timedelta):
361349
) -> np_ndarray[ShapeT, np.bool_]: ...
362350
@overload
363351
def __ge__(self, other: Series[pd.Timedelta]) -> Series[bool]: ...
364-
# Override due to more types supported than dt.timedelta
352+
# Override due to more types supported than timedelta
365353
@overload # type: ignore[override]
366354
def __gt__(self, other: timedelta | Timedelta | np.timedelta64) -> bool: ... # type: ignore[misc]
367355
@overload

pandas-stubs/_typing.pyi

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,8 @@ RandomState: TypeAlias = (
173173
)
174174

175175
# dtypes
176-
NpDtype: TypeAlias = str | np.dtype[np.generic] | type[str | complex | bool | object]
176+
NpDtypeNoStr: TypeAlias = np.dtype[np.generic] | type[complex | bool | object]
177+
NpDtype: TypeAlias = str | NpDtypeNoStr | type[str]
177178
Dtype: TypeAlias = ExtensionDtype | NpDtype
178179

179180
# AstypeArg is more carefully defined here as compared to pandas
@@ -847,19 +848,21 @@ MaskType: TypeAlias = Series[bool] | np_ndarray_bool | list[bool]
847848

848849
T_INT = TypeVar("T_INT", bound=int)
849850
T_COMPLEX = TypeVar("T_COMPLEX", bound=complex)
850-
SeriesDTypeNoDateTime: TypeAlias = (
851-
str
852-
| bytes
851+
SeriesDTypeNoStrDateTime: TypeAlias = (
852+
bytes
853853
| bool
854854
| int
855855
| float
856856
| complex
857-
| Dtype
857+
| NpDtypeNoStr
858+
| ExtensionDtype
858859
| Period
859860
| Interval
860861
| CategoricalDtype
861862
| BaseOffset
862-
| list[str]
863+
)
864+
SeriesDTypeNoDateTime: TypeAlias = (
865+
str | SeriesDTypeNoStrDateTime | type[str] | list[str]
863866
)
864867
SeriesDType: TypeAlias = (
865868
SeriesDTypeNoDateTime
@@ -869,6 +872,9 @@ SeriesDType: TypeAlias = (
869872
| datetime.timedelta # includes pd.Timedelta
870873
)
871874
S1 = TypeVar("S1", bound=SeriesDType, default=Any)
875+
S1_CO_NSDT = TypeVar(
876+
"S1_CO_NSDT", bound=SeriesDTypeNoStrDateTime, default=Any, covariant=True
877+
)
872878
S1_CT_NDT = TypeVar(
873879
"S1_CT_NDT", bound=SeriesDTypeNoDateTime, default=Any, contravariant=True
874880
)

pandas-stubs/core/arrays/datetimelike.pyi

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ class DatetimeLikeArrayMixin(ExtensionOpsMixin, ExtensionArray):
6060
@property
6161
def shape(self): ...
6262
def reshape(self, *args: Any, **kwargs: Any): ...
63-
def ravel(self, *args: Any, **kwargs: Any): ... # pyrefly: ignore
63+
def ravel(self, *args: Any, **kwargs: Any): ...
6464
def __iter__(self): ...
6565
@property
6666
def asi8(self) -> np.ndarray: ...
@@ -85,7 +85,7 @@ class DatetimeLikeArrayMixin(ExtensionOpsMixin, ExtensionArray):
8585
def unique(self): ...
8686
def copy(self): ...
8787
def shift(self, periods: int = 1, fill_value=..., axis: int = ...): ...
88-
def repeat(self, repeats, *args: Any, **kwargs: Any): ... # pyrefly: ignore
88+
def repeat(self, repeats, *args: Any, **kwargs: Any): ...
8989
def value_counts(self, dropna: bool = True): ...
9090
def map(self, mapper): ...
9191
def isna(self): ...

pandas-stubs/core/frame.pyi

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,7 @@ from pandas.core.reshape.pivot import (
6060
_PivotTableIndexTypes,
6161
_PivotTableValuesTypes,
6262
)
63-
from pandas.core.series import (
64-
Series,
65-
)
63+
from pandas.core.series import Series
6664
from pandas.core.window import (
6765
Expanding,
6866
ExponentialMovingWindow,

pandas-stubs/core/generic.pyi

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,7 @@ import numpy as np
2424
from pandas import Index
2525
import pandas.core.indexing as indexing
2626
from pandas.core.resample import DatetimeIndexResampler
27-
from pandas.core.series import (
28-
Series,
29-
)
27+
from pandas.core.series import Series
3028
import sqlalchemy.engine
3129
from typing_extensions import (
3230
Never,

pandas-stubs/core/groupby/generic.pyi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ class NamedAgg(NamedTuple):
5757

5858
class SeriesGroupBy(GroupBy[Series[S2]], Generic[S2, ByT]):
5959
@overload
60-
def aggregate( # pyrefly: ignore
60+
def aggregate(
6161
self,
6262
func: Callable[Concatenate[Series[S2], P], S3],
6363
/,

pandas-stubs/core/groupby/groupby.pyi

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,7 @@ from typing import (
2020
import numpy as np
2121
from pandas.core.base import SelectionMixin
2222
from pandas.core.frame import DataFrame
23-
from pandas.core.groupby import (
24-
generic,
25-
)
23+
from pandas.core.groupby import generic
2624
from pandas.core.groupby.indexing import (
2725
GroupByIndexingMixin,
2826
GroupByNthSelector,

0 commit comments

Comments
 (0)