diff --git a/pandas-stubs/_libs/tslibs/period.pyi b/pandas-stubs/_libs/tslibs/period.pyi index 897153c2e..55b1a2bfb 100644 --- a/pandas-stubs/_libs/tslibs/period.pyi +++ b/pandas-stubs/_libs/tslibs/period.pyi @@ -12,7 +12,10 @@ from pandas import ( Timedelta, TimedeltaIndex, ) -from typing_extensions import TypeAlias +from typing_extensions import ( + Self, + TypeAlias, +) from pandas._libs.tslibs import NaTType from pandas._libs.tslibs.offsets import BaseOffset @@ -87,15 +90,23 @@ class Period(PeriodMixin): @overload def __sub__(self, other: TimedeltaIndex) -> PeriodIndex: ... @overload - def __add__(self, other: _PeriodAddSub) -> Period: ... + def __add__(self, other: _PeriodAddSub) -> Self: ... @overload def __add__(self, other: NaTType) -> NaTType: ... @overload def __add__(self, other: Index) -> PeriodIndex: ... + # Ignored due to indecipherable error from mypy: + # Forward operator "__add__" is not callable [misc] @overload - def __add__( - self, other: Series[BaseOffset] | Series[Timedelta] - ) -> Series[Period]: ... # pyrefly: ignore[bad-specialization] + def __radd__(self, other: _PeriodAddSub) -> Self: ... # type: ignore[misc] + @overload + def __radd__(self, other: NaTType) -> NaTType: ... + # Real signature is -> PeriodIndex, but conflicts with Index.__add__ + # Changing Index is very hard due to Index inheritance + # Signatures of "__radd__" of "Period" and "__add__" of "Index" + # are unsafely overlapping + @overload + def __radd__(self, other: Index) -> PeriodIndex: ... # ignore[misc] here because we know all other comparisons # are False, so we use Literal[False] @overload @@ -168,18 +179,6 @@ class Period(PeriodMixin): def __ne__(self, other: np_ndarray[ShapeT, np.object_]) -> np_ndarray[ShapeT, np.bool]: ... # type: ignore[overload-overlap] @overload def __ne__(self, other: object) -> Literal[True]: ... - # Ignored due to indecipherable error from mypy: - # Forward operator "__add__" is not callable [misc] - @overload - def __radd__(self, other: _PeriodAddSub) -> Period: ... # type: ignore[misc] - # Real signature is -> PeriodIndex, but conflicts with Index.__add__ - # Changing Index is very hard due to Index inheritance - # Signatures of "__radd__" of "Period" and "__add__" of "Index" - # are unsafely overlapping - @overload - def __radd__(self, other: Index) -> Index: ... - @overload - def __radd__(self, other: NaTType) -> NaTType: ... @property def day(self) -> int: ... @property diff --git a/pandas-stubs/_libs/tslibs/timedeltas.pyi b/pandas-stubs/_libs/tslibs/timedeltas.pyi index 9ad5e0e68..a8adc91f3 100644 --- a/pandas-stubs/_libs/tslibs/timedeltas.pyi +++ b/pandas-stubs/_libs/tslibs/timedeltas.pyi @@ -1,6 +1,9 @@ # pyright: strict -import datetime as dt -from datetime import timedelta +from datetime import ( + date, + datetime, + timedelta, +) from typing import ( ClassVar, Literal, @@ -135,9 +138,9 @@ class Timedelta(timedelta): def ceil(self, freq: str | BaseOffset) -> Self: ... @property def resolution_string(self) -> str: ... - # Override due to more types supported than dt.timedelta + # Override due to more types supported than timedelta @overload # type: ignore[override] - def __add__(self, other: dt.datetime | np.datetime64) -> Timestamp: ... + def __add__(self, other: datetime | np.datetime64) -> Timestamp: ... @overload def __add__(self, other: timedelta | np.timedelta64) -> Self: ... @overload @@ -145,7 +148,7 @@ class Timedelta(timedelta): @overload def __add__(self, other: Period) -> Period: ... @overload - def __add__(self, other: dt.date) -> dt.date: ... + def __add__(self, other: date) -> date: ... @overload def __add__( self, other: np_ndarray[ShapeT, np.timedelta64] @@ -155,13 +158,13 @@ class Timedelta(timedelta): self, other: np_ndarray[ShapeT, np.datetime64] ) -> np_ndarray[ShapeT, np.datetime64]: ... @overload - def __radd__(self, other: dt.datetime | np.datetime64) -> Timestamp: ... # type: ignore[misc] + def __radd__(self, other: datetime | np.datetime64) -> Timestamp: ... # type: ignore[misc] @overload def __radd__(self, other: timedelta | np.timedelta64) -> Self: ... @overload def __radd__(self, other: NaTType) -> NaTType: ... @overload - def __radd__(self, other: dt.date) -> dt.date: ... + def __radd__(self, other: date) -> date: ... @overload def __radd__( self, other: np_ndarray[ShapeT, np.timedelta64] @@ -170,9 +173,9 @@ class Timedelta(timedelta): def __radd__( self, other: np_ndarray[ShapeT, np.datetime64] ) -> np_ndarray[ShapeT, np.datetime64]: ... - # Override due to more types supported than dt.timedelta + # Override due to more types supported than timedelta @overload # type: ignore[override] - def __sub__(self, other: timedelta | Timedelta | np.timedelta64) -> Timedelta: ... + def __sub__(self, other: timedelta | Timedelta | np.timedelta64) -> Self: ... @overload def __sub__(self, other: NaTType) -> NaTType: ... @overload @@ -182,11 +185,9 @@ class Timedelta(timedelta): @overload def __sub__(self, other: pd.TimedeltaIndex) -> TimedeltaIndex: ... @overload - def __sub__(self, other: Series[pd.Timedelta]) -> Series[pd.Timedelta]: ... - @overload - def __rsub__(self, other: timedelta | Timedelta | np.timedelta64) -> Timedelta: ... + def __rsub__(self, other: timedelta | Timedelta | np.timedelta64) -> Self: ... @overload - def __rsub__(self, other: dt.datetime | Timestamp | np.datetime64) -> Timestamp: ... # type: ignore[misc] + def __rsub__(self, other: datetime | Timestamp | np.datetime64) -> Timestamp: ... # type: ignore[misc] @overload def __rsub__(self, other: NaTType) -> NaTType: ... @overload @@ -205,44 +206,31 @@ class Timedelta(timedelta): ) -> np_ndarray[ShapeT, np.timedelta64]: ... @overload def __rsub__(self, other: pd.TimedeltaIndex) -> pd.TimedeltaIndex: ... - def __neg__(self) -> Timedelta: ... - def __pos__(self) -> Timedelta: ... - def __abs__(self) -> Timedelta: ... - # Override due to more types supported than dt.timedelta + def __neg__(self) -> Self: ... + def __pos__(self) -> Self: ... + def __abs__(self) -> Self: ... + # Override due to more types supported than timedelta @overload # type: ignore[override] - def __mul__(self, other: float) -> Timedelta: ... + def __mul__(self, other: float) -> Self: ... @overload def __mul__( - self, other: np_ndarray[ShapeT, np.integer] | np_ndarray[ShapeT, np.floating] + self, other: np_ndarray[ShapeT, np.bool_ | np.integer | np.floating] ) -> np_ndarray[ShapeT, np.timedelta64]: ... @overload - def __mul__(self, other: Series[int]) -> Series[Timedelta]: ... - @overload - def __mul__(self, other: Series[float]) -> Series[Timedelta]: ... - @overload - def __mul__(self, other: Index[int] | Index[float]) -> TimedeltaIndex: ... - @overload - def __rmul__(self, other: float) -> Timedelta: ... + def __rmul__(self, other: float) -> Self: ... @overload def __rmul__( - self, other: np_ndarray[ShapeT, np.floating] | np_ndarray[ShapeT, np.integer] + self, other: np_ndarray[ShapeT, np.bool_ | np.integer | np.floating] ) -> np_ndarray[ShapeT, np.timedelta64]: ... - @overload - def __rmul__(self, other: Series[int]) -> Series[Timedelta]: ... - @overload - def __rmul__(self, other: Series[float]) -> Series[Timedelta]: ... - # maybe related to https://github.com/python/mypy/issues/10755 - @overload - def __rmul__(self, other: Index[int] | Index[float]) -> TimedeltaIndex: ... - # Override due to more types supported than dt.timedelta + # Override due to more types supported than timedelta # error: Signature of "__floordiv__" incompatible with supertype "timedelta" @overload # type: ignore[override] def __floordiv__(self, other: timedelta | Timedelta | np.timedelta64) -> int: ... @overload - def __floordiv__(self, other: float) -> Timedelta: ... + def __floordiv__(self, other: float) -> Self: ... @overload def __floordiv__( - self, other: np_ndarray[ShapeT, np.integer] | np_ndarray[ShapeT, np.floating] + self, other: np_ndarray[ShapeT, np.integer | np.floating] ) -> np_ndarray[ShapeT, np.timedelta64]: ... @overload def __floordiv__( @@ -266,14 +254,14 @@ class Timedelta(timedelta): def __rfloordiv__( self, other: np_ndarray[ShapeT, np.timedelta64] ) -> np_ndarray[ShapeT, np.int_]: ... - # Override due to more types supported than dt.timedelta + # Override due to more types supported than timedelta @overload # type: ignore[override] def __truediv__(self, other: timedelta | Timedelta | NaTType) -> float: ... @overload - def __truediv__(self, other: float) -> Timedelta: ... + def __truediv__(self, other: float) -> Self: ... @overload def __truediv__( - self, other: np_ndarray[ShapeT, np.integer] | np_ndarray[ShapeT, np.floating] + self, other: np_ndarray[ShapeT, np.integer | np.floating] ) -> np_ndarray[ShapeT, np.timedelta64]: ... @overload def __truediv__(self, other: Series[Timedelta]) -> Series[float]: ... @@ -284,7 +272,7 @@ class Timedelta(timedelta): @overload def __truediv__(self, other: Index[int] | Index[float]) -> TimedeltaIndex: ... def __rtruediv__(self, other: timedelta | Timedelta | NaTType) -> float: ... - # Override due to more types supported than dt.timedelta + # Override due to more types supported than timedelta @overload def __eq__(self, other: timedelta | Timedelta | np.timedelta64) -> bool: ... # type: ignore[overload-overlap] # pyright: ignore[reportOverlappingOverload] @overload @@ -297,7 +285,7 @@ class Timedelta(timedelta): ) -> np_ndarray[ShapeT, np.bool_]: ... @overload def __eq__(self, other: object) -> Literal[False]: ... - # Override due to more types supported than dt.timedelta + # Override due to more types supported than timedelta @overload def __ne__(self, other: timedelta | Timedelta | np.timedelta64) -> bool: ... # type: ignore[overload-overlap] # pyright: ignore[reportOverlappingOverload] @overload @@ -310,18 +298,18 @@ class Timedelta(timedelta): ) -> np_ndarray[ShapeT, np.bool_]: ... @overload def __ne__(self, other: object) -> Literal[True]: ... - # Override due to more types supported than dt.timedelta + # Override due to more types supported than timedelta @overload # type: ignore[override] - def __mod__(self, other: timedelta) -> Timedelta: ... + def __mod__(self, other: timedelta) -> Self: ... @overload - def __mod__(self, other: float) -> Timedelta: ... + def __mod__(self, other: float) -> Self: ... @overload def __mod__(self, other: Series[int] | Series[float]) -> Series[Timedelta]: ... @overload def __mod__(self, other: Index[int] | Index[float]) -> TimedeltaIndex: ... @overload def __mod__( - self, other: np_ndarray[ShapeT, np.integer] | np_ndarray[ShapeT, np.floating] + self, other: np_ndarray[ShapeT, np.integer | np.floating] ) -> np_ndarray[ShapeT, np.timedelta64]: ... @overload def __mod__( @@ -330,7 +318,7 @@ class Timedelta(timedelta): def __divmod__(self, other: timedelta) -> tuple[int, Timedelta]: ... # Mypy complains Forward operator "" is not callable, so ignore misc # for le, lt ge and gt - # Override due to more types supported than dt.timedelta + # Override due to more types supported than timedelta @overload # type: ignore[override] def __le__(self, other: timedelta | Timedelta | np.timedelta64) -> bool: ... # type: ignore[misc] @overload @@ -341,7 +329,7 @@ class Timedelta(timedelta): ) -> np_ndarray[ShapeT, np.bool_]: ... @overload def __le__(self, other: Series[pd.Timedelta]) -> Series[bool]: ... - # Override due to more types supported than dt.timedelta + # Override due to more types supported than timedelta @overload # type: ignore[override] def __lt__(self, other: timedelta | Timedelta | np.timedelta64) -> bool: ... # type: ignore[misc] @overload @@ -352,7 +340,7 @@ class Timedelta(timedelta): ) -> np_ndarray[ShapeT, np.bool_]: ... @overload def __lt__(self, other: Series[pd.Timedelta]) -> Series[bool]: ... - # Override due to more types supported than dt.timedelta + # Override due to more types supported than timedelta @overload # type: ignore[override] def __ge__(self, other: timedelta | Timedelta | np.timedelta64) -> bool: ... # type: ignore[misc] @overload @@ -363,7 +351,7 @@ class Timedelta(timedelta): ) -> np_ndarray[ShapeT, np.bool_]: ... @overload def __ge__(self, other: Series[pd.Timedelta]) -> Series[bool]: ... - # Override due to more types supported than dt.timedelta + # Override due to more types supported than timedelta @overload # type: ignore[override] def __gt__(self, other: timedelta | Timedelta | np.timedelta64) -> bool: ... # type: ignore[misc] @overload diff --git a/pandas-stubs/_typing.pyi b/pandas-stubs/_typing.pyi index b852fcc65..9e24a97a5 100644 --- a/pandas-stubs/_typing.pyi +++ b/pandas-stubs/_typing.pyi @@ -173,7 +173,8 @@ RandomState: TypeAlias = ( ) # dtypes -NpDtype: TypeAlias = str | np.dtype[np.generic] | type[str | complex | bool | object] +NpDtypeNoStr: TypeAlias = np.dtype[np.generic] | type[complex | bool | object] +NpDtype: TypeAlias = str | NpDtypeNoStr | type[str] Dtype: TypeAlias = ExtensionDtype | NpDtype # AstypeArg is more carefully defined here as compared to pandas @@ -847,19 +848,21 @@ MaskType: TypeAlias = Series[bool] | np_ndarray_bool | list[bool] T_INT = TypeVar("T_INT", bound=int) T_COMPLEX = TypeVar("T_COMPLEX", bound=complex) -SeriesDTypeNoDateTime: TypeAlias = ( - str - | bytes +SeriesDTypeNoStrDateTime: TypeAlias = ( + bytes | bool | int | float | complex - | Dtype + | NpDtypeNoStr + | ExtensionDtype | Period | Interval | CategoricalDtype | BaseOffset - | list[str] +) +SeriesDTypeNoDateTime: TypeAlias = ( + str | SeriesDTypeNoStrDateTime | type[str] | list[str] ) SeriesDType: TypeAlias = ( SeriesDTypeNoDateTime @@ -869,6 +872,9 @@ SeriesDType: TypeAlias = ( | datetime.timedelta # includes pd.Timedelta ) S1 = TypeVar("S1", bound=SeriesDType, default=Any) +S1_CO_NSDT = TypeVar( + "S1_CO_NSDT", bound=SeriesDTypeNoStrDateTime, default=Any, covariant=True +) S1_CT_NDT = TypeVar( "S1_CT_NDT", bound=SeriesDTypeNoDateTime, default=Any, contravariant=True ) diff --git a/pandas-stubs/core/indexes/base.pyi b/pandas-stubs/core/indexes/base.pyi index 32f2f6297..112cb7dff 100644 --- a/pandas-stubs/core/indexes/base.pyi +++ b/pandas-stubs/core/indexes/base.pyi @@ -21,7 +21,9 @@ from typing import ( from _typeshed import ( SupportsAdd, + SupportsMul, SupportsRAdd, + SupportsRMul, ) import numpy as np from pandas import ( @@ -48,12 +50,15 @@ from typing_extensions import ( ) from pandas._libs.interval import _OrderableT +from pandas._libs.tslibs.timedeltas import Timedelta from pandas._typing import ( C2, S1, S1_CO, + S1_CO_NSDT, S1_CT, T_COMPLEX, + T_INT, AnyAll, ArrayLike, AxesData, @@ -82,8 +87,10 @@ from pandas._typing import ( np_ndarray_anyint, np_ndarray_bool, np_ndarray_complex, + np_ndarray_dt, np_ndarray_float, np_ndarray_str, + np_ndarray_td, type_t, ) @@ -728,16 +735,115 @@ class Index(IndexOpsMixin[S1]): ), ) -> Index[complex]: ... @overload + def __mul__(self: Index[Never], other: complex | _ListLike | Index) -> Index: ... + @overload + def __mul__(self, other: Index[Never]) -> Index: ... + @overload + def __mul__(self, other: np_ndarray_dt) -> Never: ... + # pandas-dev/pandas#62524 + @overload + def __mul__( # type: ignore[overload-overlap] + self: Index[bool] | Index[int] | Index[float], other: Sequence[timedelta] + ) -> Index[Timedelta]: ... + @overload def __mul__( - self: Index[int] | Index[float], other: timedelta + self: Index[bool] | Index[int] | Index[float], + other: timedelta | Sequence[Timedelta] | np.timedelta64 | np_ndarray_td, ) -> TimedeltaIndex: ... @overload + def __mul__(self: Index[T_INT], other: bool | Sequence[bool]) -> Index[T_INT]: ... + @overload + def __mul__(self: Index[float], other: int | Sequence[int]) -> Index[float]: ... + @overload def __mul__( - self, other: float | Sequence[float] | Index[int] | Index[float] - ) -> Self: ... + self: Index[complex], other: float | Sequence[float] + ) -> Index[complex]: ... + @overload + def __mul__( + self: Index[S1_CT], + other: ( + SupportsRMul[S1_CT, S1_CO_NSDT] | Sequence[SupportsRMul[S1_CT, S1_CO_NSDT]] + ), + ) -> Index[S1_CO_NSDT]: ... + @overload + def __mul__( + self: Index[T_COMPLEX], other: np_ndarray_bool | Index[bool] + ) -> Index[T_COMPLEX]: ... + @overload + def __mul__( + self: Index[bool], other: np_ndarray_anyint | Index[int] + ) -> Index[int]: ... + @overload + def __mul__( + self: Index[T_COMPLEX], other: np_ndarray_anyint | Index[int] + ) -> Index[T_COMPLEX]: ... + @overload + def __mul__( + self: Index[bool] | Index[int], other: np_ndarray_float | Index[float] + ) -> Index[float]: ... + @overload + def __mul__( + self: Index[T_COMPLEX], other: np_ndarray_float | Index[float] + ) -> Index[T_COMPLEX]: ... + @overload + def __mul__( + self: Index[T_COMPLEX], other: np_ndarray_complex | Index[complex] + ) -> Index[complex]: ... + @overload + def __rmul__(self: Index[Never], other: complex | _ListLike | Index) -> Index: ... + @overload + def __rmul__(self, other: Index[Never]) -> Index: ... + @overload + def __rmul__(self, other: np_ndarray_dt) -> Never: ... + # pandas-dev/pandas#62524 + @overload + def __rmul__( # type: ignore[overload-overlap] + self: Index[bool] | Index[int] | Index[float], other: Sequence[timedelta] + ) -> Index[Timedelta]: ... + @overload def __rmul__( - self, other: float | Sequence[float] | Index[int] | Index[float] - ) -> Self: ... + self: Index[bool] | Index[int] | Index[float], + other: timedelta | Sequence[Timedelta] | np.timedelta64 | np_ndarray_td, + ) -> TimedeltaIndex: ... + @overload + def __rmul__(self: Index[T_INT], other: bool | Sequence[bool]) -> Index[T_INT]: ... + @overload + def __rmul__(self: Index[float], other: int | Sequence[int]) -> Index[float]: ... + @overload + def __rmul__( + self: Index[complex], other: float | Sequence[float] + ) -> Index[complex]: ... + @overload + def __rmul__( + self: Index[S1_CT], + other: ( + SupportsMul[S1_CT, S1_CO_NSDT] | Sequence[SupportsMul[S1_CT, S1_CO_NSDT]] + ), + ) -> Index[S1_CO_NSDT]: ... + @overload + def __rmul__( + self: Index[T_COMPLEX], other: np_ndarray_bool | Index[bool] + ) -> Index[T_COMPLEX]: ... + @overload + def __rmul__( + self: Index[bool], other: np_ndarray_anyint | Index[int] + ) -> Index[int]: ... + @overload + def __rmul__( + self: Index[T_COMPLEX], other: np_ndarray_anyint | Index[int] + ) -> Index[T_COMPLEX]: ... + @overload + def __rmul__( + self: Index[bool] | Index[int], other: np_ndarray_float | Index[float] + ) -> Index[float]: ... + @overload + def __rmul__( + self: Index[T_COMPLEX], other: np_ndarray_float | Index[float] + ) -> Index[T_COMPLEX]: ... + @overload + def __rmul__( + self: Index[T_COMPLEX], other: np_ndarray_complex | Index[complex] + ) -> Index[complex]: ... def __floordiv__( self, other: float | Sequence[float] | Index[int] | Index[float] ) -> Self: ... diff --git a/pandas-stubs/core/indexes/datetimelike.pyi b/pandas-stubs/core/indexes/datetimelike.pyi index bf5556595..0c0087e6b 100644 --- a/pandas-stubs/core/indexes/datetimelike.pyi +++ b/pandas-stubs/core/indexes/datetimelike.pyi @@ -1,7 +1,9 @@ import numpy as np from pandas.core.indexes.extension import ExtensionIndex -from pandas.core.indexes.timedeltas import TimedeltaIndex -from typing_extensions import Self +from typing_extensions import ( + Never, + Self, +) from pandas._libs.tslibs import BaseOffset from pandas._typing import ( @@ -9,6 +11,7 @@ from pandas._typing import ( AxisIndex, GenericT_co, TimeUnit, + np_ndarray_complex, ) class DatetimeIndexOpsMixin(ExtensionIndex[S1, GenericT_co]): @@ -30,9 +33,12 @@ class DatetimeIndexOpsMixin(ExtensionIndex[S1, GenericT_co]): def argmax( self, axis: AxisIndex | None = None, skipna: bool = True, *args, **kwargs ) -> np.int64: ... - def __rsub__( # type: ignore[misc,override] # pyright: ignore[reportIncompatibleMethodOverride] - self, other: DatetimeIndexOpsMixin - ) -> TimedeltaIndex: ... + def __mul__( # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] + self, other: np_ndarray_complex + ) -> Never: ... + def __rmul__( # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] + self, other: np_ndarray_complex + ) -> Never: ... class DatetimeTimedeltaMixin(DatetimeIndexOpsMixin[S1, GenericT_co]): @property diff --git a/pandas-stubs/core/indexes/datetimes.pyi b/pandas-stubs/core/indexes/datetimes.pyi index 12af992f7..16e1a8c22 100644 --- a/pandas-stubs/core/indexes/datetimes.pyi +++ b/pandas-stubs/core/indexes/datetimes.pyi @@ -62,20 +62,19 @@ class DatetimeIndex( # various ignores needed for mypy, as we do want to restrict what can be used in # arithmetic for these types def __add__( # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] - self, other: timedelta | TimedeltaIndex | BaseOffset - ) -> DatetimeIndex: ... - def __radd__( # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] - self, other: timedelta | TimedeltaIndex | BaseOffset - ) -> DatetimeIndex: ... + self, other: timedelta | BaseOffset + ) -> Self: ... + def __radd__( # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] + self, other: timedelta | BaseOffset + ) -> Self: ... @overload # type: ignore[override] def __sub__( - self, - other: timedelta | np.timedelta64 | np_ndarray_td | TimedeltaIndex | BaseOffset, - ) -> DatetimeIndex: ... + self, other: datetime | np.datetime64 | np_ndarray_dt | Self + ) -> TimedeltaIndex: ... @overload def __sub__( # pyright: ignore[reportIncompatibleMethodOverride] - self, other: datetime | np.datetime64 | np_ndarray_dt | DatetimeIndex - ) -> TimedeltaIndex: ... + self, other: timedelta | np.timedelta64 | np_ndarray_td | BaseOffset + ) -> Self: ... @final def to_series( self, index: Index | None = None, name: Hashable | None = None diff --git a/pandas-stubs/core/indexes/timedeltas.pyi b/pandas-stubs/core/indexes/timedeltas.pyi index e87592dc8..b15b9afe1 100644 --- a/pandas-stubs/core/indexes/timedeltas.pyi +++ b/pandas-stubs/core/indexes/timedeltas.pyi @@ -27,6 +27,10 @@ from pandas._libs.tslibs import BaseOffset from pandas._typing import ( AxesData, TimedeltaConvertibleTypes, + np_ndarray_anyint, + np_ndarray_bool, + np_ndarray_dt, + np_ndarray_float, np_ndarray_td, num, ) @@ -64,9 +68,42 @@ class TimedeltaIndex( self, other: dt.timedelta | Self ) -> Self: ... def __sub__( # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] - self, other: dt.timedelta | np.timedelta64 | np_ndarray_td | Self + self, other: dt.timedelta | np.timedelta64 | np_ndarray_td | BaseOffset | Self + ) -> Self: ... + @overload # type: ignore[override] + def __rsub__( + self, other: dt.timedelta | np.timedelta64 | np_ndarray_td | BaseOffset | Self + ) -> Self: ... + @overload + def __rsub__( # pyright: ignore[reportIncompatibleMethodOverride] + self, other: dt.datetime | np.datetime64 | np_ndarray_dt | DatetimeIndex + ) -> DatetimeIndex: ... + def __mul__( # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] + self, + other: ( # type: ignore[override] + float + | Sequence[float] + | np_ndarray_bool + | np_ndarray_anyint + | np_ndarray_float + | Index[bool] + | Index[int] + | Index[float] + ), + ) -> Self: ... + def __rmul__( # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] + self, + other: ( # type: ignore[override] + float + | Sequence[float] + | np_ndarray_bool + | np_ndarray_anyint + | np_ndarray_float + | Index[bool] + | Index[int] + | Index[float] + ), ) -> Self: ... - def __mul__(self, other: float) -> Self: ... # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] @overload # type: ignore[override] def __truediv__(self, other: float | Sequence[float]) -> Self: ... @overload diff --git a/pandas-stubs/core/series.pyi b/pandas-stubs/core/series.pyi index 04b7a712f..519ad56e9 100644 --- a/pandas-stubs/core/series.pyi +++ b/pandas-stubs/core/series.pyi @@ -36,7 +36,9 @@ from typing import ( from _typeshed import ( SupportsAdd, SupportsGetItem, + SupportsMul, SupportsRAdd, + SupportsRMul, ) from matplotlib.axes import ( Axes as PlotAxes, @@ -112,6 +114,7 @@ from pandas._libs.tslibs.offsets import DateOffset from pandas._typing import ( S1, S1_CO, + S1_CO_NSDT, S1_CT, S1_CT_NDT, S2, @@ -2533,85 +2536,114 @@ class Series(IndexOpsMixin[S1], NDFrame): @overload def __mul__(self, other: Index[Never] | Series[Never]) -> Series: ... @overload - def __mul__( - self: Series[bool], - other: T_COMPLEX | Sequence[T_COMPLEX] | Index[T_COMPLEX] | Series[T_COMPLEX], - ) -> Series[T_COMPLEX]: ... + def __mul__(self, other: np_ndarray_dt) -> Never: ... @overload - def __mul__(self: Series[bool], other: np_ndarray_bool) -> Series[bool]: ... + def __mul__( + self: Series[bool] | Series[int] | Series[float], + other: ( + timedelta + | Sequence[timedelta] + | np.timedelta64 + | np_ndarray_td + | TimedeltaIndex + | Series[Timedelta] + ), + ) -> Series[Timedelta]: ... @overload - def __mul__(self: Series[bool], other: np_ndarray_anyint) -> Series[int]: ... + def __mul__(self: Series[Timestamp], other: np_ndarray) -> Never: ... @overload - def __mul__(self: Series[bool], other: np_ndarray_float) -> Series[float]: ... + def __mul__(self: Series[Timedelta], other: np_ndarray_complex) -> Never: ... @overload def __mul__( - self: Series[int], + self: Series[Timedelta], other: ( - bool - | Sequence[bool] + float + | Sequence[float] | np_ndarray_bool | np_ndarray_anyint + | np_ndarray_float | Index[bool] + | Index[int] + | Index[float] | Series[bool] + | Series[int] + | Series[float] ), - ) -> Series[int]: ... + ) -> Series[Timedelta]: ... @overload - def __mul__( - self: Series[int], - other: T_COMPLEX | Sequence[T_COMPLEX] | Index[T_COMPLEX] | Series[T_COMPLEX], - ) -> Series[T_COMPLEX]: ... + def __mul__(self: Series[T_INT], other: bool | Sequence[bool]) -> Series[T_INT]: ... @overload - def __mul__(self: Series[int], other: np_ndarray_float) -> Series[float]: ... + def __mul__(self: Series[float], other: int | Sequence[int]) -> Series[float]: ... @overload def __mul__( - self: Series[float], + self: Series[complex], other: float | Sequence[float] + ) -> Series[complex]: ... + @overload + def __mul__( + self: Series[S1_CT], other: ( - int - | Sequence[int] - | np_ndarray_bool - | np_ndarray_anyint - | np_ndarray_float - | Index[T_INT] - | Series[T_INT] + SupportsRMul[S1_CT, S1_CO_NSDT] | Sequence[SupportsRMul[S1_CT, S1_CO_NSDT]] ), - ) -> Series[float]: ... + ) -> Series[S1_CO_NSDT]: ... @overload def __mul__( - self: Series[float], - other: T_COMPLEX | Sequence[T_COMPLEX] | Index[T_COMPLEX] | Series[T_COMPLEX], + self: Series[T_COMPLEX], other: np_ndarray_bool | Index[bool] | Series[bool] ) -> Series[T_COMPLEX]: ... @overload def __mul__( - self: Series[complex], - other: ( - T_COMPLEX - | Sequence[T_COMPLEX] - | np_ndarray_bool - | np_ndarray_anyint - | np_ndarray_float - | Index[T_COMPLEX] - | Series[T_COMPLEX] - ), - ) -> Series[complex]: ... + self: Series[bool], other: np_ndarray_anyint | Index[int] | Series[int] + ) -> Series[int]: ... @overload def __mul__( - self: Series[T_COMPLEX], other: np_ndarray_complex - ) -> Series[complex]: ... + self: Series[T_COMPLEX], other: np_ndarray_anyint | Index[int] | Series[int] + ) -> Series[T_COMPLEX]: ... + @overload + def __mul__( + self: Series[bool] | Series[int], + other: np_ndarray_float | Index[float] | Series[float], + ) -> Series[float]: ... @overload def __mul__( + self: Series[T_COMPLEX], other: np_ndarray_float | Index[float] | Series[float] + ) -> Series[T_COMPLEX]: ... + @overload + def __mul__( + self: Series[T_COMPLEX], + other: np_ndarray_complex | Index[complex] | Series[complex], + ) -> Series[complex]: ... + @overload + def mul( + self: Series[Never], + other: complex | _ListLike | Index | Series, + level: Level | None = None, + fill_value: float | None = None, + axis: int = 0, + ) -> Series: ... + @overload + def mul( + self, + other: Index[Never] | Series[Never], + level: Level | None = None, + fill_value: float | None = None, + axis: int = 0, + ) -> Series: ... + @overload + def mul( self: Series[bool] | Series[int] | Series[float], other: ( timedelta + | Sequence[timedelta] | np.timedelta64 | np_ndarray_td | TimedeltaIndex | Series[Timedelta] ), + level: Level | None = ..., + fill_value: float | None = None, + axis: AxisIndex | None = 0, ) -> Series[Timedelta]: ... @overload - def __mul__(self: Series[Timedelta], other: np_ndarray_complex) -> Never: ... - @overload - def __mul__( + def mul( self: Series[Timedelta], other: ( float @@ -2626,136 +2658,102 @@ class Series(IndexOpsMixin[S1], NDFrame): | Series[int] | Series[float] ), + level: Level | None = ..., + fill_value: float | None = None, + axis: AxisIndex | None = 0, ) -> Series[Timedelta]: ... @overload def mul( - self: Series[Never], - other: complex | _ListLike | Index | Series, + self: Series[T_INT], + other: bool | Sequence[bool], level: Level | None = None, fill_value: float | None = None, axis: int = 0, - ) -> Series: ... + ) -> Series[T_INT]: ... @overload def mul( - self, - other: Index[Never] | Series[Never], + self: Series[float], + other: int | Sequence[int], level: Level | None = None, fill_value: float | None = None, axis: int = 0, - ) -> Series: ... + ) -> Series[float]: ... @overload def mul( - self: Series[bool], - other: T_COMPLEX | Sequence[T_COMPLEX] | Index[T_COMPLEX] | Series[T_COMPLEX], + self: Series[complex], + other: float | Sequence[float], level: Level | None = None, fill_value: float | None = None, axis: int = 0, - ) -> Series[T_COMPLEX]: ... + ) -> Series[complex]: ... @overload def mul( - self: Series[bool], - other: np_ndarray_bool, + self: Series[S1_CT], + other: ( + SupportsRMul[S1_CT, S1_CO_NSDT] | Sequence[SupportsRMul[S1_CT, S1_CO_NSDT]] + ), level: Level | None = None, fill_value: float | None = None, axis: int = 0, - ) -> Series[bool]: ... + ) -> Series[S1_CO_NSDT]: ... @overload def mul( - self: Series[bool], - other: np_ndarray_anyint, + self: Series[T_COMPLEX], + other: np_ndarray_bool | Index[bool] | Series[bool], level: Level | None = None, fill_value: float | None = None, axis: int = 0, - ) -> Series[int]: ... + ) -> Series[T_COMPLEX]: ... @overload def mul( self: Series[bool], - other: np_ndarray_float, - level: Level | None = None, - fill_value: float | None = None, - axis: int = 0, - ) -> Series[float]: ... - @overload - def mul( - self: Series[int], - other: ( - bool - | Sequence[bool] - | np_ndarray_bool - | np_ndarray_anyint - | Index[bool] - | Series[bool] - ), + other: np_ndarray_anyint | Index[int] | Series[int], level: Level | None = None, fill_value: float | None = None, axis: int = 0, ) -> Series[int]: ... @overload def mul( - self: Series[int], - other: T_COMPLEX | Sequence[T_COMPLEX] | Index[T_COMPLEX] | Series[T_COMPLEX], + self: Series[T_COMPLEX], + other: np_ndarray_anyint | Index[int] | Series[int], level: Level | None = None, fill_value: float | None = None, axis: int = 0, ) -> Series[T_COMPLEX]: ... @overload def mul( - self: Series[int], - other: np_ndarray_float, - level: Level | None = None, - fill_value: float | None = None, - axis: int = 0, - ) -> Series[float]: ... - @overload - def mul( - self: Series[float], - other: ( - int - | Sequence[int] - | np_ndarray_bool - | np_ndarray_anyint - | np_ndarray_float - | Index[T_INT] - | Series[T_INT] - ), + self: Series[bool] | Series[int], + other: np_ndarray_float | Index[float] | Series[float], level: Level | None = None, fill_value: float | None = None, axis: int = 0, ) -> Series[float]: ... @overload def mul( - self: Series[float], - other: T_COMPLEX | Sequence[T_COMPLEX] | Index[T_COMPLEX] | Series[T_COMPLEX], + self: Series[T_COMPLEX], + other: np_ndarray_float | Index[float] | Series[float], level: Level | None = None, fill_value: float | None = None, axis: int = 0, ) -> Series[T_COMPLEX]: ... @overload - def mul( - self: Series[complex], - other: ( - T_COMPLEX - | Sequence[T_COMPLEX] - | np_ndarray_bool - | np_ndarray_anyint - | np_ndarray_float - | Index[T_COMPLEX] - | Series[T_COMPLEX] - ), - level: Level | None = None, - fill_value: float | None = None, - axis: int = 0, - ) -> Series[complex]: ... - @overload def mul( self: Series[T_COMPLEX], - other: np_ndarray_complex, + other: np_ndarray_complex | Index[complex] | Series[complex], level: Level | None = None, fill_value: float | None = None, axis: int = 0, ) -> Series[complex]: ... @overload - def mul( + def __rmul__( # type: ignore[overload-overlap] + self: Series[Never], other: complex | NumListLike | Index | Series + ) -> Series: ... + @overload + def __rmul__(self, other: Index[Never] | Series[Never]) -> Series: ... # type: ignore[misc] + @overload + def __rmul__(self, other: np_ndarray_dt) -> Never: ... + @overload + def __rmul__( self: Series[bool] | Series[int] | Series[float], other: ( timedelta @@ -2765,12 +2763,13 @@ class Series(IndexOpsMixin[S1], NDFrame): | TimedeltaIndex | Series[Timedelta] ), - level: Level | None = ..., - fill_value: float | None = None, - axis: AxisIndex | None = 0, ) -> Series[Timedelta]: ... @overload - def mul( + def __rmul__(self: Series[Timestamp], other: np_ndarray) -> Never: ... + @overload + def __rmul__(self: Series[Timedelta], other: np_ndarray_complex) -> Never: ... + @overload + def __rmul__( self: Series[Timedelta], other: ( float @@ -2785,96 +2784,83 @@ class Series(IndexOpsMixin[S1], NDFrame): | Series[int] | Series[float] ), - level: Level | None = ..., - fill_value: float | None = None, - axis: AxisIndex | None = 0, ) -> Series[Timedelta]: ... @overload - def __rmul__( # type: ignore[overload-overlap] - self: Series[Never], other: complex | NumListLike | Index | Series - ) -> Series: ... - @overload - def __rmul__(self, other: Index[Never] | Series[Never]) -> Series: ... - @overload def __rmul__( - self: Series[bool], - other: T_COMPLEX | Sequence[T_COMPLEX] | Index[T_COMPLEX] | Series[T_COMPLEX], - ) -> Series[T_COMPLEX]: ... - @overload - def __rmul__(self: Series[bool], other: np_ndarray_bool) -> Series[bool]: ... + self: Series[T_INT], other: bool | Sequence[bool] + ) -> Series[T_INT]: ... @overload - def __rmul__(self: Series[bool], other: np_ndarray_anyint) -> Series[int]: ... + def __rmul__(self: Series[float], other: int | Sequence[int]) -> Series[float]: ... @overload - def __rmul__(self: Series[bool], other: np_ndarray_float) -> Series[float]: ... + def __rmul__( + self: Series[complex], other: float | Sequence[float] + ) -> Series[complex]: ... @overload def __rmul__( - self: Series[int], + self: Series[S1_CT], other: ( - bool - | Sequence[bool] - | np_ndarray_bool - | np_ndarray_anyint - | Index[bool] - | Series[bool] + SupportsMul[S1_CT, S1_CO_NSDT] | Sequence[SupportsMul[S1_CT, S1_CO_NSDT]] ), - ) -> Series[int]: ... + ) -> Series[S1_CO_NSDT]: ... @overload def __rmul__( - self: Series[int], - other: T_COMPLEX | Sequence[T_COMPLEX] | Index[T_COMPLEX] | Series[T_COMPLEX], + self: Series[T_COMPLEX], other: np_ndarray_bool | Index[bool] | Series[bool] ) -> Series[T_COMPLEX]: ... @overload - def __rmul__(self: Series[int], other: np_ndarray_float) -> Series[float]: ... + def __rmul__( + self: Series[bool], other: np_ndarray_anyint | Index[int] | Series[int] + ) -> Series[int]: ... + @overload + def __rmul__( + self: Series[T_COMPLEX], other: np_ndarray_anyint | Index[int] | Series[int] + ) -> Series[T_COMPLEX]: ... @overload def __rmul__( - self: Series[float], - other: ( - int - | Sequence[int] - | np_ndarray_bool - | np_ndarray_anyint - | np_ndarray_float - | Index[T_INT] - | Series[T_INT] - ), + self: Series[bool] | Series[int], + other: np_ndarray_float | Index[float] | Series[float], ) -> Series[float]: ... @overload def __rmul__( - self: Series[float], - other: T_COMPLEX | Sequence[T_COMPLEX] | Index[T_COMPLEX] | Series[T_COMPLEX], + self: Series[T_COMPLEX], other: np_ndarray_float | Index[float] | Series[float] ) -> Series[T_COMPLEX]: ... @overload def __rmul__( - self: Series[complex], - other: ( - T_COMPLEX - | Sequence[T_COMPLEX] - | np_ndarray_bool - | np_ndarray_anyint - | np_ndarray_float - | Index[T_COMPLEX] - | Series[T_COMPLEX] - ), + self: Series[T_COMPLEX], + other: np_ndarray_complex | Index[complex] | Series[complex], ) -> Series[complex]: ... @overload - def __rmul__( - self: Series[T_COMPLEX], other: np_ndarray_complex - ) -> Series[complex]: ... + def rmul( + self: Series[Never], + other: complex | _ListLike | Index | Series, + level: Level | None = None, + fill_value: float | None = None, + axis: int = 0, + ) -> Series: ... @overload - def __rmul__( + def rmul( + self, + other: Index[Never] | Series[Never], + level: Level | None = None, + fill_value: float | None = None, + axis: int = 0, + ) -> Series: ... + @overload + def rmul( self: Series[bool] | Series[int] | Series[float], other: ( timedelta + | Sequence[timedelta] | np.timedelta64 | np_ndarray_td | TimedeltaIndex | Series[Timedelta] ), + level: Level | None = ..., + fill_value: float | None = None, + axis: AxisIndex | None = 0, ) -> Series[Timedelta]: ... @overload - def __rmul__(self: Series[Timedelta], other: np_ndarray_complex) -> Never: ... - @overload - def __rmul__( + def rmul( self: Series[Timedelta], other: ( float @@ -2889,169 +2875,92 @@ class Series(IndexOpsMixin[S1], NDFrame): | Series[int] | Series[float] ), + level: Level | None = ..., + fill_value: float | None = None, + axis: AxisIndex | None = 0, ) -> Series[Timedelta]: ... @overload def rmul( - self: Series[Never], - other: complex | _ListLike | Index | Series, + self: Series[T_INT], + other: bool | Sequence[bool], level: Level | None = None, fill_value: float | None = None, axis: int = 0, - ) -> Series: ... + ) -> Series[T_INT]: ... @overload def rmul( - self, - other: Index[Never] | Series[Never], + self: Series[float], + other: int | Sequence[int], level: Level | None = None, fill_value: float | None = None, axis: int = 0, - ) -> Series: ... + ) -> Series[float]: ... @overload def rmul( - self: Series[bool], - other: T_COMPLEX | Sequence[T_COMPLEX] | Index[T_COMPLEX] | Series[T_COMPLEX], + self: Series[complex], + other: float | Sequence[float], level: Level | None = None, fill_value: float | None = None, axis: int = 0, - ) -> Series[T_COMPLEX]: ... + ) -> Series[complex]: ... @overload def rmul( - self: Series[bool], - other: np_ndarray_bool, + self: Series[S1_CT], + other: ( + SupportsMul[S1_CT, S1_CO_NSDT] | Sequence[SupportsMul[S1_CT, S1_CO_NSDT]] + ), level: Level | None = None, fill_value: float | None = None, axis: int = 0, - ) -> Series[bool]: ... + ) -> Series[S1_CO_NSDT]: ... @overload def rmul( - self: Series[bool], - other: np_ndarray_anyint, + self: Series[T_COMPLEX], + other: np_ndarray_bool | Index[bool] | Series[bool], level: Level | None = None, fill_value: float | None = None, axis: int = 0, - ) -> Series[int]: ... + ) -> Series[T_COMPLEX]: ... @overload def rmul( self: Series[bool], - other: np_ndarray_float, - level: Level | None = None, - fill_value: float | None = None, - axis: int = 0, - ) -> Series[float]: ... - @overload - def rmul( - self: Series[int], - other: ( - bool - | Sequence[bool] - | np_ndarray_bool - | np_ndarray_anyint - | Index[bool] - | Series[bool] - ), + other: np_ndarray_anyint | Index[int] | Series[int], level: Level | None = None, fill_value: float | None = None, axis: int = 0, ) -> Series[int]: ... @overload def rmul( - self: Series[int], - other: T_COMPLEX | Sequence[T_COMPLEX] | Index[T_COMPLEX] | Series[T_COMPLEX], + self: Series[T_COMPLEX], + other: np_ndarray_anyint | Index[int] | Series[int], level: Level | None = None, fill_value: float | None = None, axis: int = 0, ) -> Series[T_COMPLEX]: ... @overload def rmul( - self: Series[int], - other: np_ndarray_float, - level: Level | None = None, - fill_value: float | None = None, - axis: int = 0, - ) -> Series[float]: ... - @overload - def rmul( - self: Series[float], - other: ( - int - | Sequence[int] - | np_ndarray_bool - | np_ndarray_anyint - | np_ndarray_float - | Index[T_INT] - | Series[T_INT] - ), + self: Series[bool] | Series[int], + other: np_ndarray_float | Index[float] | Series[float], level: Level | None = None, fill_value: float | None = None, axis: int = 0, ) -> Series[float]: ... @overload def rmul( - self: Series[float], - other: T_COMPLEX | Sequence[T_COMPLEX] | Index[T_COMPLEX] | Series[T_COMPLEX], + self: Series[T_COMPLEX], + other: np_ndarray_float | Index[float] | Series[float], level: Level | None = None, fill_value: float | None = None, axis: int = 0, ) -> Series[T_COMPLEX]: ... @overload - def rmul( - self: Series[complex], - other: ( - T_COMPLEX - | Sequence[T_COMPLEX] - | np_ndarray_bool - | np_ndarray_anyint - | np_ndarray_float - | Index[T_COMPLEX] - | Series[T_COMPLEX] - ), - level: Level | None = None, - fill_value: float | None = None, - axis: int = 0, - ) -> Series[complex]: ... - @overload def rmul( self: Series[T_COMPLEX], - other: np_ndarray_complex, + other: np_ndarray_complex | Index[complex] | Series[complex], level: Level | None = None, fill_value: float | None = None, axis: int = 0, ) -> Series[complex]: ... - @overload - def rmul( - self: Series[bool] | Series[int] | Series[float], - other: ( - timedelta - | Sequence[timedelta] - | np.timedelta64 - | np_ndarray_td - | TimedeltaIndex - | Series[Timedelta] - ), - level: Level | None = ..., - fill_value: float | None = None, - axis: AxisIndex | None = 0, - ) -> Series[Timedelta]: ... - @overload - def rmul( - self: Series[Timedelta], - other: ( - float - | Sequence[float] - | np_ndarray_bool - | np_ndarray_anyint - | np_ndarray_float - | Index[bool] - | Index[int] - | Index[float] - | Series[bool] - | Series[int] - | Series[float] - ), - level: Level | None = ..., - fill_value: float | None = None, - axis: AxisIndex | None = 0, - ) -> Series[Timedelta]: ... def __mod__(self, other: num | _ListLike | Series[S1]) -> Series[S1]: ... def __ne__(self, other: object) -> Series[_bool]: ... # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] def __pow__(self, other: num | _ListLike | Series[S1]) -> Series[S1]: ... diff --git a/tests/indexes/arithmetic/bool/test_mul.py b/tests/indexes/arithmetic/bool/test_mul.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/indexes/arithmetic/complex/test_mul.py b/tests/indexes/arithmetic/complex/test_mul.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/indexes/arithmetic/datetime/__init__.py b/tests/indexes/arithmetic/datetime/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/indexes/arithmetic/float/test_mul.py b/tests/indexes/arithmetic/float/test_mul.py new file mode 100644 index 000000000..e3b4fa4e0 --- /dev/null +++ b/tests/indexes/arithmetic/float/test_mul.py @@ -0,0 +1,128 @@ +from datetime import ( + datetime, + timedelta, +) +from typing import Any + +import numpy as np +from numpy import typing as npt # noqa: F401 +import pandas as pd +from typing_extensions import ( + Never, + assert_type, +) + +from tests import ( + TYPE_CHECKING_INVALID_USAGE, + check, +) + +left = pd.Index([1.0, 2.0, 3.0]) # left operand + + +def test_mul_py_scalar() -> None: + """Test pd.Index[float] * Python native scalars""" + b, i, f, c = True, 1, 1.0, 1j + s, d = datetime(2025, 9, 30), timedelta(seconds=1) + + check(assert_type(left * b, "pd.Index[float]"), pd.Index, np.floating) + check(assert_type(left * i, "pd.Index[float]"), pd.Index, np.floating) + check(assert_type(left * f, "pd.Index[float]"), pd.Index, np.floating) + check(assert_type(left * c, "pd.Index[complex]"), pd.Index, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _05 = left * s # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + check(assert_type(left * d, "pd.TimedeltaIndex"), pd.TimedeltaIndex, timedelta) + + check(assert_type(b * left, "pd.Index[float]"), pd.Index, np.floating) + check(assert_type(i * left, "pd.Index[float]"), pd.Index, np.floating) + check(assert_type(f * left, "pd.Index[float]"), pd.Index, np.floating) + check(assert_type(c * left, "pd.Index[complex]"), pd.Index, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _15 = s * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + check(assert_type(d * left, "pd.TimedeltaIndex"), pd.TimedeltaIndex, timedelta) + + +def test_mul_py_sequence() -> None: + """Test pd.Index[float] * Python native sequences""" + b, i, f, c = [True, False, True], [2, 3, 5], [1.0, 2.0, 3.0], [1j, 1j, 4j] + s = [datetime(2025, 9, d) for d in (27, 28, 29)] + d = [timedelta(seconds=s + 1) for s in range(3)] + + check(assert_type(left * b, "pd.Index[float]"), pd.Index, np.floating) + check(assert_type(left * i, "pd.Index[float]"), pd.Index, np.floating) + check(assert_type(left * f, "pd.Index[float]"), pd.Index, np.floating) + check(assert_type(left * c, "pd.Index[complex]"), pd.Index, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _05 = left * s # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + check(assert_type(left * d, "pd.Index[pd.Timedelta]"), pd.Index, timedelta) + + check(assert_type(b * left, "pd.Index[float]"), pd.Index, np.floating) + check(assert_type(i * left, "pd.Index[float]"), pd.Index, np.floating) + check(assert_type(f * left, "pd.Index[float]"), pd.Index, np.floating) + check(assert_type(c * left, "pd.Index[complex]"), pd.Index, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _15 = s * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + check(assert_type(d * left, "pd.Index[pd.Timedelta]"), pd.Index, timedelta) + + +def test_mul_numpy_array() -> None: + """Test pd.Index[float] * numpy arrays""" + b = np.array([True, False, True], np.bool_) + i = np.array([2, 3, 5], np.int64) + f = np.array([1.0, 2.0, 3.0], np.float64) + c = np.array([1.1j, 2.2j, 4.1j], np.complex128) + s = np.array([np.datetime64(f"2025-09-{d}") for d in (27, 28, 29)], np.datetime64) + d = np.array([np.timedelta64(s + 1, "s") for s in range(3)], np.timedelta64) + + check(assert_type(left * b, "pd.Index[float]"), pd.Index, np.floating) + check(assert_type(left * i, "pd.Index[float]"), pd.Index, np.floating) + check(assert_type(left * f, "pd.Index[float]"), pd.Index, np.floating) + check(assert_type(left * c, "pd.Index[complex]"), pd.Index, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + assert_type(left * s, Never) + check(assert_type(left * d, pd.TimedeltaIndex), pd.TimedeltaIndex, pd.Timedelta) + + # `numpy` typing gives the corresponding `ndarray`s in the static type + # checking, where our `__rmul__` cannot override. At runtime, they return + # `Series` with the correct element type. + check(assert_type(b * left, "npt.NDArray[np.bool_]"), pd.Index, np.floating) + check(assert_type(i * left, "npt.NDArray[np.int64]"), pd.Index, np.floating) + check(assert_type(f * left, "npt.NDArray[np.float64]"), pd.Index, np.floating) + check( + assert_type(c * left, "npt.NDArray[np.complex128]"), + pd.Index, + np.complexfloating, + ) + if TYPE_CHECKING_INVALID_USAGE: + assert_type(s * left, Any) + check( + assert_type(d * left, "npt.NDArray[np.timedelta64]"), + pd.TimedeltaIndex, + pd.Timedelta, + ) + + +def test_mul_pd_index() -> None: + """Test pd.Index[float] * pandas Indexes""" + b = pd.Index([True, False, True]) + i = pd.Index([2, 3, 5]) + f = pd.Index([1.0, 2.0, 3.0]) + c = pd.Index([1.1j, 2.2j, 4.1j]) + s = pd.Index([datetime(2025, 9, d) for d in (27, 28, 29)]) + d = pd.Index([timedelta(seconds=s + 1) for s in range(3)]) + + check(assert_type(left * b, "pd.Index[float]"), pd.Index, np.floating) + check(assert_type(left * i, "pd.Index[float]"), pd.Index, np.floating) + check(assert_type(left * f, "pd.Index[float]"), pd.Index, np.floating) + check(assert_type(left * c, "pd.Index[complex]"), pd.Index, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _05 = left * s # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + check(assert_type(left * d, pd.TimedeltaIndex), pd.TimedeltaIndex, pd.Timedelta) + + check(assert_type(b * left, "pd.Index[float]"), pd.Index, np.floating) + check(assert_type(i * left, "pd.Index[float]"), pd.Index, np.floating) + check(assert_type(f * left, "pd.Index[float]"), pd.Index, np.floating) + check(assert_type(c * left, "pd.Index[complex]"), pd.Index, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _15 = s * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + check(assert_type(d * left, pd.TimedeltaIndex), pd.TimedeltaIndex, pd.Timedelta) diff --git a/tests/indexes/arithmetic/int/test_mul.py b/tests/indexes/arithmetic/int/test_mul.py new file mode 100644 index 000000000..1f0a395c7 --- /dev/null +++ b/tests/indexes/arithmetic/int/test_mul.py @@ -0,0 +1,126 @@ +from datetime import ( + datetime, + timedelta, +) +from typing import Any + +import numpy as np +from numpy import typing as npt # noqa: F401 +import pandas as pd +from typing_extensions import ( + Never, + assert_type, +) + +from tests import ( + TYPE_CHECKING_INVALID_USAGE, + check, +) + +left = pd.Index([1, 2, 3]) # left operand + + +def test_mul_py_scalar() -> None: + """Test pd.Index[int] * Python native scalars""" + b, i, f, c = True, 1, 1.0, 1j + s, d = datetime(2025, 9, 27), timedelta(seconds=1) + + check(assert_type(left * b, "pd.Index[int]"), pd.Index, np.integer) + check(assert_type(left * i, "pd.Index[int]"), pd.Index, np.integer) + check(assert_type(left * f, "pd.Index[float]"), pd.Index, np.floating) + check(assert_type(left * c, "pd.Index[complex]"), pd.Index, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _05 = left * s # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + check(assert_type(left * d, pd.TimedeltaIndex), pd.Index, pd.Timedelta) + + check(assert_type(b * left, "pd.Index[int]"), pd.Index, np.integer) + check(assert_type(i * left, "pd.Index[int]"), pd.Index, np.integer) + check(assert_type(f * left, "pd.Index[float]"), pd.Index, np.floating) + check(assert_type(c * left, "pd.Index[complex]"), pd.Index, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _15 = s * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + check(assert_type(d * left, pd.TimedeltaIndex), pd.Index, pd.Timedelta) + + +def test_mul_py_sequence() -> None: + """Test pd.Index[int] * Python native sequences""" + b, i, f, c = [True, False, True], [2, 3, 5], [1.0, 2.0, 3.0], [1j, 1j, 4j] + s = [datetime(2025, 9, d) for d in (27, 28, 29)] + d = [timedelta(seconds=s + 1) for s in range(3)] + + check(assert_type(left * b, "pd.Index[int]"), pd.Index, np.integer) + check(assert_type(left * i, "pd.Index[int]"), pd.Index, np.integer) + check(assert_type(left * f, "pd.Index[float]"), pd.Index, np.floating) + check(assert_type(left * c, "pd.Index[complex]"), pd.Index, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _05 = left * s # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + # pandas-dev/pandas#62524 + check(assert_type(left * d, "pd.Index[pd.Timedelta]"), pd.Index, timedelta) + + check(assert_type(b * left, "pd.Index[int]"), pd.Index, np.integer) + check(assert_type(i * left, "pd.Index[int]"), pd.Index, np.integer) + check(assert_type(f * left, "pd.Index[float]"), pd.Index, np.floating) + check(assert_type(c * left, "pd.Index[complex]"), pd.Index, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _15 = s * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + # pandas-dev/pandas#62524 + check(assert_type(d * left, "pd.Index[pd.Timedelta]"), pd.Index, timedelta) + + +def test_mul_numpy_array() -> None: + """Test pd.Index[int] * numpy arrays""" + b = np.array([True, False, True], np.bool_) + i = np.array([2, 3, 5], np.int64) + f = np.array([1.0, 2.0, 3.0], np.float64) + c = np.array([1.1j, 2.2j, 4.1j], np.complex128) + s = np.array([np.datetime64(f"2025-09-{d}") for d in (27, 28, 29)], np.datetime64) + d = np.array([np.timedelta64(s + 1, "s") for s in range(3)], np.timedelta64) + + check(assert_type(left * b, "pd.Index[int]"), pd.Index, np.integer) + check(assert_type(left * i, "pd.Index[int]"), pd.Index, np.integer) + check(assert_type(left * f, "pd.Index[float]"), pd.Index, np.floating) + check(assert_type(left * c, "pd.Index[complex]"), pd.Index, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + assert_type(left * s, Never) + check(assert_type(left * d, pd.TimedeltaIndex), pd.Index, pd.Timedelta) + + # `numpy` typing gives the corresponding `ndarray`s in the static type + # checking, where our `__rmul__` cannot override. At runtime, they return + # `Series` with the correct element type. + check(assert_type(b * left, "npt.NDArray[np.bool_]"), pd.Index, np.integer) + check(assert_type(i * left, "npt.NDArray[np.int64]"), pd.Index, np.integer) + check(assert_type(f * left, "npt.NDArray[np.float64]"), pd.Index, np.floating) + check( + assert_type(c * left, "npt.NDArray[np.complex128]"), + pd.Index, + np.complexfloating, + ) + if TYPE_CHECKING_INVALID_USAGE: + assert_type(s * left, Any) + check(assert_type(d * left, "npt.NDArray[np.timedelta64]"), pd.Index, pd.Timedelta) + + +def test_mul_pd_index() -> None: + """Test pd.Index[int] * pandas Indexes""" + b = pd.Index([True, False, True]) + i = pd.Index([2, 3, 5]) + f = pd.Index([1.0, 2.0, 3.0]) + c = pd.Index([1.1j, 2.2j, 4.1j]) + s = pd.Index([datetime(2025, 9, d) for d in (27, 28, 29)]) + d = pd.Index([timedelta(seconds=s + 1) for s in range(3)]) + + check(assert_type(left * b, "pd.Index[int]"), pd.Index, np.integer) + check(assert_type(left * i, "pd.Index[int]"), pd.Index, np.integer) + check(assert_type(left * f, "pd.Index[float]"), pd.Index, np.floating) + check(assert_type(left * c, "pd.Index[complex]"), pd.Index, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _05 = left * s # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + check(assert_type(left * d, pd.TimedeltaIndex), pd.Index, pd.Timedelta) + + check(assert_type(b * left, "pd.Index[int]"), pd.Index, np.integer) + check(assert_type(i * left, "pd.Index[int]"), pd.Index, np.integer) + check(assert_type(f * left, "pd.Index[float]"), pd.Index, np.floating) + check(assert_type(c * left, "pd.Index[complex]"), pd.Index, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _15 = s * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + check(assert_type(d * left, pd.TimedeltaIndex), pd.Index, pd.Timedelta) diff --git a/tests/indexes/arithmetic/test_mul.py b/tests/indexes/arithmetic/test_mul.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/indexes/arithmetic/timedelta/__init__.py b/tests/indexes/arithmetic/timedelta/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/indexes/arithmetic/timedelta/test_mul.py b/tests/indexes/arithmetic/timedelta/test_mul.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/scalars/test_scalars.py b/tests/scalars/test_scalars.py index 54fcb08c5..75332fdb0 100644 --- a/tests/scalars/test_scalars.py +++ b/tests/scalars/test_scalars.py @@ -519,7 +519,6 @@ def test_timedelta_add_sub() -> None: as_dt_timedelta = dt.timedelta(days=1) as_timedelta64 = np.timedelta64(1, "D") as_timedelta_index = pd.TimedeltaIndex([td]) - as_timedelta_series = pd.Series(as_timedelta_index) as_period_index = pd.period_range("2012-01-01", periods=3, freq="D") as_datetime_index = pd.date_range("2012-01-01", periods=3) as_ndarray_td64 = ndarray_td64 @@ -535,11 +534,6 @@ def test_timedelta_add_sub() -> None: check(assert_type(td + as_dt_timedelta, pd.Timedelta), pd.Timedelta) check(assert_type(td + as_timedelta64, pd.Timedelta), pd.Timedelta) check(assert_type(td + as_timedelta_index, pd.TimedeltaIndex), pd.TimedeltaIndex) - check( - assert_type(td + as_timedelta_series, "pd.Series[pd.Timedelta]"), - pd.Series, - pd.Timedelta, - ) check(assert_type(td + as_period_index, pd.PeriodIndex), pd.PeriodIndex) check(assert_type(td + as_datetime_index, pd.DatetimeIndex), pd.DatetimeIndex) check( @@ -577,11 +571,6 @@ def test_timedelta_add_sub() -> None: pd.Timedelta, ) check(assert_type(as_timedelta_index + td, pd.TimedeltaIndex), pd.TimedeltaIndex) - check( - assert_type(as_timedelta_series + td, "pd.Series[pd.Timedelta]"), - pd.Series, - pd.Timedelta, - ) check(assert_type(as_period_index + td, pd.PeriodIndex), pd.PeriodIndex) check(assert_type(as_datetime_index + td, pd.DatetimeIndex), pd.DatetimeIndex) check( @@ -612,11 +601,6 @@ def test_timedelta_add_sub() -> None: check(assert_type(td - as_dt_timedelta, pd.Timedelta), pd.Timedelta) check(assert_type(td - as_timedelta64, pd.Timedelta), pd.Timedelta) check(assert_type(td - as_timedelta_index, pd.TimedeltaIndex), pd.TimedeltaIndex) - check( - assert_type(td - as_timedelta_series, "pd.Series[pd.Timedelta]"), - pd.Series, - pd.Timedelta, - ) check( assert_type(td - as_ndarray_td64, npt.NDArray[np.timedelta64]), np.ndarray, @@ -646,11 +630,6 @@ def test_timedelta_add_sub() -> None: pd.Timedelta, ) check(assert_type(as_timedelta_index - td, pd.TimedeltaIndex), pd.TimedeltaIndex) - check( - assert_type(as_timedelta_series - td, "pd.Series[pd.Timedelta]"), - pd.Series, - pd.Timedelta, - ) check(assert_type(as_period_index - td, pd.PeriodIndex), pd.PeriodIndex) check(assert_type(as_datetime_index - td, pd.DatetimeIndex), pd.DatetimeIndex) check( @@ -703,18 +682,6 @@ def test_timedelta_mul_div() -> None: np.ndarray, np.timedelta64, ) - check( - assert_type(td * mp_series_int, "pd.Series[pd.Timedelta]"), - pd.Series, - pd.Timedelta, - ) - check( - assert_type(td * md_series_float, "pd.Series[pd.Timedelta]"), - pd.Series, - pd.Timedelta, - ) - check(assert_type(td * md_int64_index, pd.TimedeltaIndex), pd.TimedeltaIndex) - check(assert_type(td * md_float_index, pd.TimedeltaIndex), pd.TimedeltaIndex) check(assert_type(md_int * td, pd.Timedelta), pd.Timedelta) check(assert_type(md_float * td, pd.Timedelta), pd.Timedelta) @@ -728,18 +695,6 @@ def test_timedelta_mul_div() -> None: np.ndarray, np.timedelta64, ) - check( - assert_type(mp_series_int * td, "pd.Series[pd.Timedelta]"), - pd.Series, - pd.Timedelta, - ) - check( - assert_type(md_series_float * td, "pd.Series[pd.Timedelta]"), - pd.Series, - pd.Timedelta, - ) - check(assert_type(md_int64_index * td, pd.TimedeltaIndex), pd.TimedeltaIndex) - check(assert_type(md_float_index * td, pd.TimedeltaIndex), pd.TimedeltaIndex) check(assert_type(td // td, int), int) check(assert_type(td // pd.NaT, float), float) diff --git a/tests/series/arithmetic/float/test_mul.py b/tests/series/arithmetic/float/test_mul.py index f4afe08d2..d9d3c71f4 100644 --- a/tests/series/arithmetic/float/test_mul.py +++ b/tests/series/arithmetic/float/test_mul.py @@ -1,9 +1,21 @@ +from datetime import ( + datetime, + timedelta, +) +from typing import Any + import numpy as np from numpy import typing as npt # noqa: F401 import pandas as pd -from typing_extensions import assert_type +from typing_extensions import ( + Never, + assert_type, +) -from tests import check +from tests import ( + TYPE_CHECKING_INVALID_USAGE, + check, +) left = pd.Series([1.0, 2.0, 3.0]) # left operand @@ -11,21 +23,31 @@ def test_mul_py_scalar() -> None: """Test pd.Series[float] * Python native scalars""" b, i, f, c = True, 1, 1.0, 1j + s, d = datetime(2025, 9, 30), timedelta(seconds=1) check(assert_type(left * b, "pd.Series[float]"), pd.Series, np.floating) check(assert_type(left * i, "pd.Series[float]"), pd.Series, np.floating) check(assert_type(left * f, "pd.Series[float]"), pd.Series, np.floating) check(assert_type(left * c, "pd.Series[complex]"), pd.Series, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _05 = left * s # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + check(assert_type(left * d, "pd.Series[pd.Timedelta]"), pd.Series, timedelta) check(assert_type(b * left, "pd.Series[float]"), pd.Series, np.floating) check(assert_type(i * left, "pd.Series[float]"), pd.Series, np.floating) check(assert_type(f * left, "pd.Series[float]"), pd.Series, np.floating) check(assert_type(c * left, "pd.Series[complex]"), pd.Series, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _15 = s * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + check(assert_type(d * left, "pd.Series[pd.Timedelta]"), pd.Series, timedelta) check(assert_type(left.mul(b), "pd.Series[float]"), pd.Series, np.floating) check(assert_type(left.mul(i), "pd.Series[float]"), pd.Series, np.floating) check(assert_type(left.mul(f), "pd.Series[float]"), pd.Series, np.floating) check(assert_type(left.mul(c), "pd.Series[complex]"), pd.Series, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _25 = left.mul(s) # type: ignore[call-overload] # pyright: ignore[reportArgumentType,reportCallIssue] + check(assert_type(left.mul(d), "pd.Series[pd.Timedelta]"), pd.Series, timedelta) check(assert_type(left.rmul(b), "pd.Series[float]"), pd.Series, np.floating) check(assert_type(left.rmul(i), "pd.Series[float]"), pd.Series, np.floating) @@ -33,26 +55,40 @@ def test_mul_py_scalar() -> None: check( assert_type(left.rmul(c), "pd.Series[complex]"), pd.Series, np.complexfloating ) + if TYPE_CHECKING_INVALID_USAGE: + _35 = left.rmul(s) # type: ignore[call-overload] # pyright: ignore[reportArgumentType,reportCallIssue] + check(assert_type(left.rmul(d), "pd.Series[pd.Timedelta]"), pd.Series, timedelta) def test_mul_py_sequence() -> None: """Test pd.Series[float] * Python native sequences""" b, i, f, c = [True, False, True], [2, 3, 5], [1.0, 2.0, 3.0], [1j, 1j, 4j] + s = [datetime(2025, 9, d) for d in (27, 28, 29)] + d = [timedelta(seconds=s + 1) for s in range(3)] check(assert_type(left * b, "pd.Series[float]"), pd.Series, np.floating) check(assert_type(left * i, "pd.Series[float]"), pd.Series, np.floating) check(assert_type(left * f, "pd.Series[float]"), pd.Series, np.floating) check(assert_type(left * c, "pd.Series[complex]"), pd.Series, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _05 = left * s # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + check(assert_type(left * d, "pd.Series[pd.Timedelta]"), pd.Series, timedelta) check(assert_type(b * left, "pd.Series[float]"), pd.Series, np.floating) check(assert_type(i * left, "pd.Series[float]"), pd.Series, np.floating) check(assert_type(f * left, "pd.Series[float]"), pd.Series, np.floating) check(assert_type(c * left, "pd.Series[complex]"), pd.Series, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _15 = s * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + check(assert_type(d * left, "pd.Series[pd.Timedelta]"), pd.Series, timedelta) check(assert_type(left.mul(b), "pd.Series[float]"), pd.Series, np.floating) check(assert_type(left.mul(i), "pd.Series[float]"), pd.Series, np.floating) check(assert_type(left.mul(f), "pd.Series[float]"), pd.Series, np.floating) check(assert_type(left.mul(c), "pd.Series[complex]"), pd.Series, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _25 = left.mul(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + check(assert_type(left.mul(d), "pd.Series[pd.Timedelta]"), pd.Series, timedelta) check(assert_type(left.rmul(b), "pd.Series[float]"), pd.Series, np.floating) check(assert_type(left.rmul(i), "pd.Series[float]"), pd.Series, np.floating) @@ -60,6 +96,9 @@ def test_mul_py_sequence() -> None: check( assert_type(left.rmul(c), "pd.Series[complex]"), pd.Series, np.complexfloating ) + if TYPE_CHECKING_INVALID_USAGE: + _35 = left.rmul(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + check(assert_type(left.rmul(d), "pd.Series[pd.Timedelta]"), pd.Series, timedelta) def test_mul_numpy_array() -> None: @@ -68,11 +107,16 @@ def test_mul_numpy_array() -> None: i = np.array([2, 3, 5], np.int64) f = np.array([1.0, 2.0, 3.0], np.float64) c = np.array([1.1j, 2.2j, 4.1j], np.complex128) + s = np.array([np.datetime64(f"2025-09-{d}") for d in (27, 28, 29)], np.datetime64) + d = np.array([np.timedelta64(s + 1, "s") for s in range(3)], np.timedelta64) check(assert_type(left * b, "pd.Series[float]"), pd.Series, np.floating) check(assert_type(left * i, "pd.Series[float]"), pd.Series, np.floating) check(assert_type(left * f, "pd.Series[float]"), pd.Series, np.floating) check(assert_type(left * c, "pd.Series[complex]"), pd.Series, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + assert_type(left * s, Never) + check(assert_type(left * d, "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) # `numpy` typing gives the corresponding `ndarray`s in the static type # checking, where our `__rmul__` cannot override. At runtime, they return @@ -85,11 +129,17 @@ def test_mul_numpy_array() -> None: pd.Series, np.complexfloating, ) + if TYPE_CHECKING_INVALID_USAGE: + assert_type(s * left, Any) + check(assert_type(d * left, "npt.NDArray[np.timedelta64]"), pd.Series, pd.Timedelta) check(assert_type(left.mul(b), "pd.Series[float]"), pd.Series, np.floating) check(assert_type(left.mul(i), "pd.Series[float]"), pd.Series, np.floating) check(assert_type(left.mul(f), "pd.Series[float]"), pd.Series, np.floating) check(assert_type(left.mul(c), "pd.Series[complex]"), pd.Series, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _25 = left.mul(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + check(assert_type(left.mul(d), "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) check(assert_type(left.rmul(b), "pd.Series[float]"), pd.Series, np.floating) check(assert_type(left.rmul(i), "pd.Series[float]"), pd.Series, np.floating) @@ -97,6 +147,9 @@ def test_mul_numpy_array() -> None: check( assert_type(left.rmul(c), "pd.Series[complex]"), pd.Series, np.complexfloating ) + if TYPE_CHECKING_INVALID_USAGE: + _35 = left.rmul(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + check(assert_type(left.rmul(d), "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) def test_mul_pd_index() -> None: @@ -105,21 +158,32 @@ def test_mul_pd_index() -> None: i = pd.Index([2, 3, 5]) f = pd.Index([1.0, 2.0, 3.0]) c = pd.Index([1.1j, 2.2j, 4.1j]) + s = pd.Index([datetime(2025, 9, d) for d in (27, 28, 29)]) + d = pd.Index([timedelta(seconds=s + 1) for s in range(3)]) check(assert_type(left * b, "pd.Series[float]"), pd.Series, np.floating) check(assert_type(left * i, "pd.Series[float]"), pd.Series, np.floating) check(assert_type(left * f, "pd.Series[float]"), pd.Series, np.floating) check(assert_type(left * c, "pd.Series[complex]"), pd.Series, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _05 = left * s # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + check(assert_type(left * d, "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) check(assert_type(b * left, "pd.Series[float]"), pd.Series, np.floating) check(assert_type(i * left, "pd.Series[float]"), pd.Series, np.floating) check(assert_type(f * left, "pd.Series[float]"), pd.Series, np.floating) check(assert_type(c * left, "pd.Series[complex]"), pd.Series, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _15 = s * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + check(assert_type(d * left, "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) check(assert_type(left.mul(b), "pd.Series[float]"), pd.Series, np.floating) check(assert_type(left.mul(i), "pd.Series[float]"), pd.Series, np.floating) check(assert_type(left.mul(f), "pd.Series[float]"), pd.Series, np.floating) check(assert_type(left.mul(c), "pd.Series[complex]"), pd.Series, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _25 = left.mul(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + check(assert_type(left.mul(d), "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) check(assert_type(left.rmul(b), "pd.Series[float]"), pd.Series, np.floating) check(assert_type(left.rmul(i), "pd.Series[float]"), pd.Series, np.floating) @@ -127,6 +191,9 @@ def test_mul_pd_index() -> None: check( assert_type(left.rmul(c), "pd.Series[complex]"), pd.Series, np.complexfloating ) + if TYPE_CHECKING_INVALID_USAGE: + _35 = left.rmul(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + check(assert_type(left.rmul(d), "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) def test_mul_pd_series() -> None: @@ -135,21 +202,32 @@ def test_mul_pd_series() -> None: i = pd.Series([2, 3, 5]) f = pd.Series([1.0, 2.0, 3.0]) c = pd.Series([1.1j, 2.2j, 4.1j]) + s = pd.Series([datetime(2025, 9, d) for d in (27, 28, 29)]) + d = pd.Series([timedelta(seconds=s + 1) for s in range(3)]) check(assert_type(left * b, "pd.Series[float]"), pd.Series, np.floating) check(assert_type(left * i, "pd.Series[float]"), pd.Series, np.floating) check(assert_type(left * f, "pd.Series[float]"), pd.Series, np.floating) check(assert_type(left * c, "pd.Series[complex]"), pd.Series, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _05 = left * s # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + check(assert_type(left * d, "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) check(assert_type(b * left, "pd.Series[float]"), pd.Series, np.floating) check(assert_type(i * left, "pd.Series[float]"), pd.Series, np.floating) check(assert_type(f * left, "pd.Series[float]"), pd.Series, np.floating) check(assert_type(c * left, "pd.Series[complex]"), pd.Series, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _15 = s * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + check(assert_type(d * left, "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) check(assert_type(left.mul(b), "pd.Series[float]"), pd.Series, np.floating) check(assert_type(left.mul(i), "pd.Series[float]"), pd.Series, np.floating) check(assert_type(left.mul(f), "pd.Series[float]"), pd.Series, np.floating) check(assert_type(left.mul(c), "pd.Series[complex]"), pd.Series, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _25 = left.mul(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + check(assert_type(left.mul(d), "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) check(assert_type(left.rmul(b), "pd.Series[float]"), pd.Series, np.floating) check(assert_type(left.rmul(i), "pd.Series[float]"), pd.Series, np.floating) @@ -157,3 +235,6 @@ def test_mul_pd_series() -> None: check( assert_type(left.rmul(c), "pd.Series[complex]"), pd.Series, np.complexfloating ) + if TYPE_CHECKING_INVALID_USAGE: + _35 = left.rmul(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + check(assert_type(left.rmul(d), "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) diff --git a/tests/series/arithmetic/int/test_mul.py b/tests/series/arithmetic/int/test_mul.py index b15d91256..548b9ade1 100644 --- a/tests/series/arithmetic/int/test_mul.py +++ b/tests/series/arithmetic/int/test_mul.py @@ -1,9 +1,21 @@ +from datetime import ( + datetime, + timedelta, +) +from typing import Any + import numpy as np from numpy import typing as npt # noqa: F401 import pandas as pd -from typing_extensions import assert_type +from typing_extensions import ( + Never, + assert_type, +) -from tests import check +from tests import ( + TYPE_CHECKING_INVALID_USAGE, + check, +) left = pd.Series([1, 2, 3]) # left operand @@ -11,21 +23,31 @@ def test_mul_py_scalar() -> None: """Test pd.Series[int] * Python native scalars""" b, i, f, c = True, 1, 1.0, 1j + s, d = datetime(2025, 9, 27), timedelta(seconds=1) check(assert_type(left * b, "pd.Series[int]"), pd.Series, np.integer) check(assert_type(left * i, "pd.Series[int]"), pd.Series, np.integer) check(assert_type(left * f, "pd.Series[float]"), pd.Series, np.floating) check(assert_type(left * c, "pd.Series[complex]"), pd.Series, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _05 = left * s # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + check(assert_type(left * d, "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) check(assert_type(b * left, "pd.Series[int]"), pd.Series, np.integer) check(assert_type(i * left, "pd.Series[int]"), pd.Series, np.integer) check(assert_type(f * left, "pd.Series[float]"), pd.Series, np.floating) check(assert_type(c * left, "pd.Series[complex]"), pd.Series, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _15 = s * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + check(assert_type(d * left, "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) check(assert_type(left.mul(b), "pd.Series[int]"), pd.Series, np.integer) check(assert_type(left.mul(i), "pd.Series[int]"), pd.Series, np.integer) check(assert_type(left.mul(f), "pd.Series[float]"), pd.Series, np.floating) check(assert_type(left.mul(c), "pd.Series[complex]"), pd.Series, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _25 = left.mul(s) # type: ignore[call-overload] # pyright: ignore[reportArgumentType,reportCallIssue] + check(assert_type(left.mul(d), "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) check(assert_type(left.rmul(b), "pd.Series[int]"), pd.Series, np.integer) check(assert_type(left.rmul(i), "pd.Series[int]"), pd.Series, np.integer) @@ -33,26 +55,40 @@ def test_mul_py_scalar() -> None: check( assert_type(left.rmul(c), "pd.Series[complex]"), pd.Series, np.complexfloating ) + if TYPE_CHECKING_INVALID_USAGE: + _35 = left.rmul(s) # type: ignore[call-overload] # pyright: ignore[reportArgumentType,reportCallIssue] + check(assert_type(left.rmul(d), "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) def test_mul_py_sequence() -> None: """Test pd.Series[int] * Python native sequences""" b, i, f, c = [True, False, True], [2, 3, 5], [1.0, 2.0, 3.0], [1j, 1j, 4j] + s = [datetime(2025, 9, d) for d in (27, 28, 29)] + d = [timedelta(seconds=s + 1) for s in range(3)] check(assert_type(left * b, "pd.Series[int]"), pd.Series, np.integer) check(assert_type(left * i, "pd.Series[int]"), pd.Series, np.integer) check(assert_type(left * f, "pd.Series[float]"), pd.Series, np.floating) check(assert_type(left * c, "pd.Series[complex]"), pd.Series, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _05 = left * s # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + check(assert_type(left * d, "pd.Series[pd.Timedelta]"), pd.Series, timedelta) check(assert_type(b * left, "pd.Series[int]"), pd.Series, np.integer) check(assert_type(i * left, "pd.Series[int]"), pd.Series, np.integer) check(assert_type(f * left, "pd.Series[float]"), pd.Series, np.floating) check(assert_type(c * left, "pd.Series[complex]"), pd.Series, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _15 = s * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + check(assert_type(d * left, "pd.Series[pd.Timedelta]"), pd.Series, timedelta) check(assert_type(left.mul(b), "pd.Series[int]"), pd.Series, np.integer) check(assert_type(left.mul(i), "pd.Series[int]"), pd.Series, np.integer) check(assert_type(left.mul(f), "pd.Series[float]"), pd.Series, np.floating) check(assert_type(left.mul(c), "pd.Series[complex]"), pd.Series, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _25 = left.mul(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + check(assert_type(left.mul(d), "pd.Series[pd.Timedelta]"), pd.Series, timedelta) check(assert_type(left.rmul(b), "pd.Series[int]"), pd.Series, np.integer) check(assert_type(left.rmul(i), "pd.Series[int]"), pd.Series, np.integer) @@ -60,6 +96,9 @@ def test_mul_py_sequence() -> None: check( assert_type(left.rmul(c), "pd.Series[complex]"), pd.Series, np.complexfloating ) + if TYPE_CHECKING_INVALID_USAGE: + _35 = left.rmul(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + check(assert_type(left.rmul(d), "pd.Series[pd.Timedelta]"), pd.Series, timedelta) def test_mul_numpy_array() -> None: @@ -68,11 +107,16 @@ def test_mul_numpy_array() -> None: i = np.array([2, 3, 5], np.int64) f = np.array([1.0, 2.0, 3.0], np.float64) c = np.array([1.1j, 2.2j, 4.1j], np.complex128) + s = np.array([np.datetime64(f"2025-09-{d}") for d in (27, 28, 29)], np.datetime64) + d = np.array([np.timedelta64(s + 1, "s") for s in range(3)], np.timedelta64) check(assert_type(left * b, "pd.Series[int]"), pd.Series, np.integer) check(assert_type(left * i, "pd.Series[int]"), pd.Series, np.integer) check(assert_type(left * f, "pd.Series[float]"), pd.Series, np.floating) check(assert_type(left * c, "pd.Series[complex]"), pd.Series, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + assert_type(left * s, Never) + check(assert_type(left * d, "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) # `numpy` typing gives the corresponding `ndarray`s in the static type # checking, where our `__rmul__` cannot override. At runtime, they return @@ -85,11 +129,17 @@ def test_mul_numpy_array() -> None: pd.Series, np.complexfloating, ) + if TYPE_CHECKING_INVALID_USAGE: + assert_type(s * left, Any) + check(assert_type(d * left, "npt.NDArray[np.timedelta64]"), pd.Series, pd.Timedelta) check(assert_type(left.mul(b), "pd.Series[int]"), pd.Series, np.integer) check(assert_type(left.mul(i), "pd.Series[int]"), pd.Series, np.integer) check(assert_type(left.mul(f), "pd.Series[float]"), pd.Series, np.floating) check(assert_type(left.mul(c), "pd.Series[complex]"), pd.Series, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _25 = left.mul(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + check(assert_type(left.mul(d), "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) check(assert_type(left.rmul(b), "pd.Series[int]"), pd.Series, np.integer) check(assert_type(left.rmul(i), "pd.Series[int]"), pd.Series, np.integer) @@ -97,6 +147,9 @@ def test_mul_numpy_array() -> None: check( assert_type(left.rmul(c), "pd.Series[complex]"), pd.Series, np.complexfloating ) + if TYPE_CHECKING_INVALID_USAGE: + _35 = left.rmul(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + check(assert_type(left.rmul(d), "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) def test_mul_pd_index() -> None: @@ -105,21 +158,32 @@ def test_mul_pd_index() -> None: i = pd.Index([2, 3, 5]) f = pd.Index([1.0, 2.0, 3.0]) c = pd.Index([1.1j, 2.2j, 4.1j]) + s = pd.Index([datetime(2025, 9, d) for d in (27, 28, 29)]) + d = pd.Index([timedelta(seconds=s + 1) for s in range(3)]) check(assert_type(left * b, "pd.Series[int]"), pd.Series, np.integer) check(assert_type(left * i, "pd.Series[int]"), pd.Series, np.integer) check(assert_type(left * f, "pd.Series[float]"), pd.Series, np.floating) check(assert_type(left * c, "pd.Series[complex]"), pd.Series, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _05 = left * s # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + check(assert_type(left * d, "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) check(assert_type(b * left, "pd.Series[int]"), pd.Series, np.integer) check(assert_type(i * left, "pd.Series[int]"), pd.Series, np.integer) check(assert_type(f * left, "pd.Series[float]"), pd.Series, np.floating) check(assert_type(c * left, "pd.Series[complex]"), pd.Series, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _15 = s * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + check(assert_type(d * left, "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) check(assert_type(left.mul(b), "pd.Series[int]"), pd.Series, np.integer) check(assert_type(left.mul(i), "pd.Series[int]"), pd.Series, np.integer) check(assert_type(left.mul(f), "pd.Series[float]"), pd.Series, np.floating) check(assert_type(left.mul(c), "pd.Series[complex]"), pd.Series, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _25 = left.mul(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + check(assert_type(left.mul(d), "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) check(assert_type(left.rmul(b), "pd.Series[int]"), pd.Series, np.integer) check(assert_type(left.rmul(i), "pd.Series[int]"), pd.Series, np.integer) @@ -127,6 +191,9 @@ def test_mul_pd_index() -> None: check( assert_type(left.rmul(c), "pd.Series[complex]"), pd.Series, np.complexfloating ) + if TYPE_CHECKING_INVALID_USAGE: + _35 = left.rmul(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + check(assert_type(left.rmul(d), "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) def test_mul_pd_series() -> None: @@ -135,21 +202,32 @@ def test_mul_pd_series() -> None: i = pd.Series([2, 3, 5]) f = pd.Series([1.0, 2.0, 3.0]) c = pd.Series([1.1j, 2.2j, 4.1j]) + s = pd.Series([datetime(2025, 9, d) for d in (27, 28, 29)]) + d = pd.Series([timedelta(seconds=s + 1) for s in range(3)]) check(assert_type(left * b, "pd.Series[int]"), pd.Series, np.integer) check(assert_type(left * i, "pd.Series[int]"), pd.Series, np.integer) check(assert_type(left * f, "pd.Series[float]"), pd.Series, np.floating) check(assert_type(left * c, "pd.Series[complex]"), pd.Series, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _05 = left * s # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + check(assert_type(left * d, "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) check(assert_type(b * left, "pd.Series[int]"), pd.Series, np.integer) check(assert_type(i * left, "pd.Series[int]"), pd.Series, np.integer) check(assert_type(f * left, "pd.Series[float]"), pd.Series, np.floating) check(assert_type(c * left, "pd.Series[complex]"), pd.Series, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _15 = s * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + check(assert_type(d * left, "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) check(assert_type(left.mul(b), "pd.Series[int]"), pd.Series, np.integer) check(assert_type(left.mul(i), "pd.Series[int]"), pd.Series, np.integer) check(assert_type(left.mul(f), "pd.Series[float]"), pd.Series, np.floating) check(assert_type(left.mul(c), "pd.Series[complex]"), pd.Series, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _25 = left.mul(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + check(assert_type(left.mul(d), "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) check(assert_type(left.rmul(b), "pd.Series[int]"), pd.Series, np.integer) check(assert_type(left.rmul(i), "pd.Series[int]"), pd.Series, np.integer) @@ -157,3 +235,6 @@ def test_mul_pd_series() -> None: check( assert_type(left.rmul(c), "pd.Series[complex]"), pd.Series, np.complexfloating ) + if TYPE_CHECKING_INVALID_USAGE: + _35 = left.rmul(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + check(assert_type(left.rmul(d), "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) diff --git a/tests/series/arithmetic/test_mul.py b/tests/series/arithmetic/test_mul.py index f89633416..e95b4e02b 100644 --- a/tests/series/arithmetic/test_mul.py +++ b/tests/series/arithmetic/test_mul.py @@ -179,5 +179,5 @@ def test_mul_str_py_str() -> None: if TYPE_CHECKING_INVALID_USAGE: left_i * s # type: ignore[operator] # pyright:ignore[reportOperatorIssue] s * left_i # type: ignore[operator] # pyright:ignore[reportOperatorIssue] - left_i.mul(s) # type: ignore[type-var] # pyright: ignore[reportArgumentType,reportCallIssue] - left_i.rmul(s) # type: ignore[type-var] # pyright: ignore[reportArgumentType,reportCallIssue] + left_i.mul(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + left_i.rmul(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] diff --git a/tests/series/arithmetic/timedelta/test_mul.py b/tests/series/arithmetic/timedelta/test_mul.py index 8f83ca19e..107886c86 100644 --- a/tests/series/arithmetic/timedelta/test_mul.py +++ b/tests/series/arithmetic/timedelta/test_mul.py @@ -41,7 +41,7 @@ def test_mul_py_scalar() -> None: check(assert_type(left.mul(i), "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) check(assert_type(left.mul(f), "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) if TYPE_CHECKING_INVALID_USAGE: - left.mul(c) # type: ignore[call-overload] # pyright: ignore[reportArgumentType,reportCallIssue] + left.mul(c) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] if PD_LTE_23: check( @@ -52,7 +52,7 @@ def test_mul_py_scalar() -> None: check(assert_type(left.rmul(i), "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) check(assert_type(left.rmul(f), "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) if TYPE_CHECKING_INVALID_USAGE: - left.rmul(c) # type: ignore[call-overload] # pyright: ignore[reportArgumentType,reportCallIssue] + left.rmul(c) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] def test_mul_py_sequence() -> None: diff --git a/tests/series/test_series.py b/tests/series/test_series.py index 12c75f50d..09d5499ea 100644 --- a/tests/series/test_series.py +++ b/tests/series/test_series.py @@ -3278,30 +3278,6 @@ def test_series_mapping() -> None: ) -def test_timedeltaseries_operators() -> None: - series = pd.Series([pd.Timedelta(days=1)]) - check( - assert_type(series + datetime.datetime.now(), "pd.Series[pd.Timestamp]"), - pd.Series, - pd.Timestamp, - ) - check( - assert_type(series + datetime.timedelta(1), "pd.Series[pd.Timedelta]"), - pd.Series, - pd.Timedelta, - ) - check( - assert_type(datetime.datetime.now() + series, "pd.Series[pd.Timestamp]"), - pd.Series, - pd.Timestamp, - ) - check( - assert_type(series - datetime.timedelta(1), "pd.Series[pd.Timedelta]"), - pd.Series, - pd.Timedelta, - ) - - def test_timestamp_series() -> None: series = pd.Series([pd.Timestamp(2024, 4, 4)]) check( diff --git a/tests/test_timefuncs.py b/tests/test_timefuncs.py index 2fdf6a138..8e7395e6d 100644 --- a/tests/test_timefuncs.py +++ b/tests/test_timefuncs.py @@ -1,9 +1,6 @@ from __future__ import annotations import datetime as dt -from typing import ( - Optional, -) from dateutil.relativedelta import ( FR, @@ -154,7 +151,7 @@ def test_timedelta_arithmetic() -> None: check(assert_type(td3 / 10.2, pd.Timedelta), pd.Timedelta) -def test_timedelta_series_arithmetic() -> None: +def test_timedelta_index_arithmetic() -> None: tds1 = pd.to_timedelta([2, 3], "minutes") td1 = pd.Timedelta("2 days") check(assert_type(tds1, pd.TimedeltaIndex), pd.TimedeltaIndex) @@ -170,7 +167,7 @@ def test_timedelta_float_value() -> None: check(assert_type(pd.Timedelta(1.5, "h"), pd.Timedelta), pd.Timedelta) -def test_timedelta_series_string() -> None: +def test_timedelta_index_string() -> None: seq_list = ["1 day"] check(assert_type(pd.to_timedelta(seq_list), pd.TimedeltaIndex), pd.TimedeltaIndex) @@ -198,19 +195,6 @@ def test_datetimeindex_plus_timedelta() -> None: pd.Timestamp, ) dti = pd.to_datetime(["2022-03-08", "2022-03-15"]) - td_s = pd.to_timedelta(pd.Series([10, 20]), "minutes") - dti_td_s = dti + td_s - check( - assert_type(dti_td_s, "pd.Series[pd.Timestamp]"), - pd.Series, - pd.Timestamp, - ) - td_dti_s = td_s + dti - check( - assert_type(td_dti_s, "pd.Series[pd.Timestamp]"), - pd.Series, - pd.Timestamp, - ) tdi = pd.to_timedelta([10, 20], "minutes") dti_tdi_dti = dti + tdi check(assert_type(dti_tdi_dti, "pd.DatetimeIndex"), pd.DatetimeIndex) @@ -233,13 +217,6 @@ def test_datetimeindex_minus_timedelta() -> None: pd.Timestamp, ) dti = pd.to_datetime(["2022-03-08", "2022-03-15"]) - td_s = pd.to_timedelta(pd.Series([10, 20]), "minutes") - dti_td_s = dti - td_s - check( - assert_type(dti_td_s, "pd.Series[pd.Timestamp]"), - pd.Series, - pd.Timestamp, - ) tdi = pd.to_timedelta([10, 20], "minutes") dti_tdi_dti = dti - tdi check(assert_type(dti_tdi_dti, "pd.DatetimeIndex"), pd.DatetimeIndex) @@ -249,7 +226,7 @@ def test_datetimeindex_minus_timedelta() -> None: check(assert_type(dti_ts_tdi, pd.TimedeltaIndex), pd.TimedeltaIndex) -def test_timestamp_plus_timedelta_series() -> None: +def test_timestamp_series_construction() -> None: check( assert_type( pd.Series([pd.Timestamp("2022-03-05"), pd.Timestamp("2022-03-06")]), @@ -258,18 +235,6 @@ def test_timestamp_plus_timedelta_series() -> None: pd.Series, pd.Timestamp, ) - ts = pd.Timestamp("2022-03-05") - td = pd.to_timedelta(pd.Series([10, 20]), "minutes") - r3 = td + ts - check(assert_type(r3, "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp) - r4 = ts + td - check(assert_type(r4, "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp) - - -def test_timedelta_series_mult() -> None: - df = pd.DataFrame({"x": [1, 3, 5], "y": [2, 2, 6]}) - std = (df["x"] < df["y"]) * pd.Timedelta(10, "minutes") - check(assert_type(std, "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) def test_timedelta_series_sum() -> None: @@ -289,19 +254,10 @@ def test_iso_calendar() -> None: check(assert_type(dates.isocalendar(), pd.DataFrame), pd.DataFrame) -def test_fail_on_adding_two_timestamps() -> None: - s1 = pd.Series(pd.to_datetime(["2022-05-01", "2022-06-01"])) - s2 = pd.Series(pd.to_datetime(["2022-05-15", "2022-06-15"])) - if TYPE_CHECKING_INVALID_USAGE: - ssum = s1 + s2 # type: ignore[operator] # pyright: ignore[reportOperatorIssue] - ts = pd.Timestamp("2022-06-30") - tsum = s1 + ts # type: ignore[operator] # pyright: ignore[reportOperatorIssue] - - def test_dtindex_tzinfo() -> None: # GH 71 dti = pd.date_range("2000-1-1", periods=10) - check(assert_type(dti.tzinfo, Optional[dt.tzinfo]), type(None)) + check(assert_type(dti.tzinfo, dt.tzinfo | None), type(None)) def test_todatetime_fromnumpy() -> None: @@ -376,8 +332,8 @@ def test_series_dt_accessors() -> None: check(assert_type(s0.dt.is_leap_year, "pd.Series[bool]"), pd.Series, np.bool_) check(assert_type(s0.dt.daysinmonth, "pd.Series[int]"), pd.Series, np.integer) check(assert_type(s0.dt.days_in_month, "pd.Series[int]"), pd.Series, np.integer) - check(assert_type(s0.dt.tz, Optional[dt.tzinfo]), type(None)) - check(assert_type(s0.dt.freq, Optional[str]), str) + check(assert_type(s0.dt.tz, dt.tzinfo | None), type(None)) + check(assert_type(s0.dt.freq, str | None), str) check(assert_type(s0.dt.isocalendar(), pd.DataFrame), pd.DataFrame) check( assert_type(s0.dt.to_period("D"), "pd.Series[pd.Period]"), pd.Series, pd.Period @@ -448,8 +404,8 @@ def test_series_dt_accessors() -> None: pd.Series, pd.Timestamp, ) - check(assert_type(s0.dt.tz, Optional[dt.tzinfo]), type(None)) - check(assert_type(s0_local.dt.tz, Optional[dt.tzinfo]), dt.tzinfo) + check(assert_type(s0.dt.tz, dt.tzinfo | None), type(None)) + check(assert_type(s0_local.dt.tz, dt.tzinfo | None), dt.tzinfo) check( assert_type(s0.dt.normalize(), "pd.Series[pd.Timestamp]"), pd.Series, @@ -676,8 +632,8 @@ def test_datetimeindex_accessors() -> None: check(assert_type(i0.is_leap_year, np_1darray[np.bool]), np_1darray[np.bool]) check(assert_type(i0.daysinmonth, "pd.Index[int]"), pd.Index, np.int32) check(assert_type(i0.days_in_month, "pd.Index[int]"), pd.Index, np.int32) - check(assert_type(i0.tz, Optional[dt.tzinfo]), type(None)) - check(assert_type(i0.freq, Optional[BaseOffset]), BaseOffset) + check(assert_type(i0.tz, dt.tzinfo | None), type(None)) + check(assert_type(i0.freq, BaseOffset | None), BaseOffset) check(assert_type(i0.isocalendar(), pd.DataFrame), pd.DataFrame) check(assert_type(i0.to_period("D"), pd.PeriodIndex), pd.PeriodIndex, pd.Period) check( @@ -698,7 +654,7 @@ def test_datetimeindex_accessors() -> None: assert_type(ilocal.tz_convert(pytz.timezone("US/Pacific")), pd.DatetimeIndex), pd.DatetimeIndex, ) - check(assert_type(ilocal.tz, Optional[dt.tzinfo]), dt.tzinfo) + check(assert_type(ilocal.tz, dt.tzinfo | None), dt.tzinfo) check(assert_type(i0.normalize(), pd.DatetimeIndex), pd.DatetimeIndex, pd.Timestamp) check(assert_type(i0.strftime("%Y"), pd.Index), pd.Index, str) check(assert_type(i0.round("D"), pd.DatetimeIndex), pd.DatetimeIndex, pd.Timestamp) @@ -763,7 +719,7 @@ def test_periodindex_accessors() -> None: check(assert_type(i0.quarter, "pd.Index[int]"), pd.Index, np.integer) check(assert_type(i0.daysinmonth, "pd.Index[int]"), pd.Index, np.integer) check(assert_type(i0.days_in_month, "pd.Index[int]"), pd.Index, np.integer) - check(assert_type(i0.freq, Optional[BaseOffset]), BaseOffset) + check(assert_type(i0.freq, BaseOffset | None), BaseOffset) check(assert_type(i0.strftime("%Y"), pd.Index), pd.Index, str) check(assert_type(i0.asfreq("D"), pd.PeriodIndex), pd.PeriodIndex, pd.Period) check(assert_type(i0.end_time, pd.DatetimeIndex), pd.DatetimeIndex, pd.Timestamp)