diff --git a/pandas-stubs/_libs/tslibs/period.pyi b/pandas-stubs/_libs/tslibs/period.pyi index 9a7a23f4d..0d90395ca 100644 --- a/pandas-stubs/_libs/tslibs/period.pyi +++ b/pandas-stubs/_libs/tslibs/period.pyi @@ -13,6 +13,7 @@ from pandas import ( Timedelta, TimedeltaIndex, ) +from typing_extensions import Self from pandas._libs.tslibs import NaTType from pandas._libs.tslibs.offsets import BaseOffset @@ -87,15 +88,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 +177,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 71cf65005..1b8406a51 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, @@ -133,9 +136,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 @@ -143,7 +146,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] @@ -153,13 +156,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] @@ -168,9 +171,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 @@ -180,11 +183,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 @@ -203,44 +204,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__( @@ -264,14 +252,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]: ... @@ -282,7 +270,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 @@ -295,7 +283,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 @@ -308,18 +296,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__( @@ -328,7 +316,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 @@ -339,7 +327,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 @@ -350,7 +338,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 @@ -361,7 +349,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 d84b91342..887d2a240 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/arrays/datetimelike.pyi b/pandas-stubs/core/arrays/datetimelike.pyi index 59841aa0f..74f62e405 100644 --- a/pandas-stubs/core/arrays/datetimelike.pyi +++ b/pandas-stubs/core/arrays/datetimelike.pyi @@ -60,7 +60,7 @@ class DatetimeLikeArrayMixin(ExtensionOpsMixin, ExtensionArray): @property def shape(self): ... def reshape(self, *args: Any, **kwargs: Any): ... - def ravel(self, *args: Any, **kwargs: Any): ... # pyrefly: ignore + def ravel(self, *args: Any, **kwargs: Any): ... def __iter__(self): ... @property def asi8(self) -> np.ndarray: ... @@ -85,7 +85,7 @@ class DatetimeLikeArrayMixin(ExtensionOpsMixin, ExtensionArray): def unique(self): ... def copy(self): ... def shift(self, periods: int = 1, fill_value=..., axis: int = ...): ... - def repeat(self, repeats, *args: Any, **kwargs: Any): ... # pyrefly: ignore + def repeat(self, repeats, *args: Any, **kwargs: Any): ... def value_counts(self, dropna: bool = True): ... def map(self, mapper): ... def isna(self): ... diff --git a/pandas-stubs/core/frame.pyi b/pandas-stubs/core/frame.pyi index 347cab728..e330064ee 100644 --- a/pandas-stubs/core/frame.pyi +++ b/pandas-stubs/core/frame.pyi @@ -60,9 +60,7 @@ from pandas.core.reshape.pivot import ( _PivotTableIndexTypes, _PivotTableValuesTypes, ) -from pandas.core.series import ( - Series, -) +from pandas.core.series import Series from pandas.core.window import ( Expanding, ExponentialMovingWindow, diff --git a/pandas-stubs/core/generic.pyi b/pandas-stubs/core/generic.pyi index 03053a9d3..8cc612667 100644 --- a/pandas-stubs/core/generic.pyi +++ b/pandas-stubs/core/generic.pyi @@ -24,9 +24,7 @@ import numpy as np from pandas import Index import pandas.core.indexing as indexing from pandas.core.resample import DatetimeIndexResampler -from pandas.core.series import ( - Series, -) +from pandas.core.series import Series import sqlalchemy.engine from typing_extensions import ( Never, diff --git a/pandas-stubs/core/groupby/generic.pyi b/pandas-stubs/core/groupby/generic.pyi index 2fba6ce66..40670c59d 100644 --- a/pandas-stubs/core/groupby/generic.pyi +++ b/pandas-stubs/core/groupby/generic.pyi @@ -57,7 +57,7 @@ class NamedAgg(NamedTuple): class SeriesGroupBy(GroupBy[Series[S2]], Generic[S2, ByT]): @overload - def aggregate( # pyrefly: ignore + def aggregate( self, func: Callable[Concatenate[Series[S2], P], S3], /, diff --git a/pandas-stubs/core/groupby/groupby.pyi b/pandas-stubs/core/groupby/groupby.pyi index dc49248de..fe8bbc744 100644 --- a/pandas-stubs/core/groupby/groupby.pyi +++ b/pandas-stubs/core/groupby/groupby.pyi @@ -20,9 +20,7 @@ from typing import ( import numpy as np from pandas.core.base import SelectionMixin from pandas.core.frame import DataFrame -from pandas.core.groupby import ( - generic, -) +from pandas.core.groupby import generic from pandas.core.groupby.indexing import ( GroupByIndexingMixin, GroupByNthSelector, diff --git a/pandas-stubs/core/indexes/base.pyi b/pandas-stubs/core/indexes/base.pyi index 7b23d6edc..b41353ef4 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 ( @@ -49,12 +51,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, @@ -84,8 +89,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, ) @@ -740,16 +747,191 @@ 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: ... + @overload + def __mul__(self: Index[complex], other: np_ndarray_td) -> Never: ... + # pandas-dev/pandas#62524: An index of Python native timedeltas can be + # produced, instead of a TimedeltaIndex, hence the overload + @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[Timedelta], other: np_ndarray_complex) -> Never: ... + @overload def __mul__( - self, other: float | Sequence[float] | Index[int] | Index[float] - ) -> Self: ... + self: Index[Timedelta], + other: ( + float + | Sequence[float] + | np_ndarray_bool + | np_ndarray_anyint + | np_ndarray_float + | Index[bool] + | Index[int] + | Index[float] + ), + ) -> Index[Timedelta]: ... + @overload + def __mul__( + self: Index[_str], + other: np_ndarray_float | np_ndarray_complex | np_ndarray_dt | np_ndarray_td, + ) -> Never: ... + # pandas-dev/pandas#62595: we may want to support Series[str] * bool + # also in 3.x + @overload + def __mul__( + self: Index[_str], + other: ( + int + | Sequence[int] + | np_ndarray_bool + | np_ndarray_anyint + | Index[bool] + | Index[int] + ), + ) -> Index[_str]: ... + @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: 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: ... + @overload + def __rmul__(self: Index[complex], other: np_ndarray_td) -> Never: ... + # pandas-dev/pandas#62524: An index of Python native timedeltas can be + # produced, instead of a TimedeltaIndex, hence the overload + @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[Timedelta], other: np_ndarray_complex) -> Never: ... + @overload + def __rmul__( + self: Index[Timedelta], + other: ( + float + | Sequence[float] + | np_ndarray_bool + | np_ndarray_anyint + | np_ndarray_float + | Index[bool] + | Index[int] + | Index[float] + ), + ) -> Index[Timedelta]: ... + @overload + def __rmul__( + self: Index[_str], + other: np_ndarray_float | np_ndarray_complex | np_ndarray_dt | np_ndarray_td, + ) -> Never: ... + # pandas-dev/pandas#62595: we may want to support Series[str] * bool + # also in 3.x + @overload + def __rmul__( + self: Index[_str], + other: ( + int + | Sequence[int] + | np_ndarray_bool + | np_ndarray_anyint + | Index[bool] + | Index[int] + ), + ) -> Index[_str]: ... + @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: ... @@ -767,7 +949,7 @@ class Index(IndexOpsMixin[S1]): @type_check_only class _IndexSubclassBase(Index[S1], Generic[S1, GenericT_co]): @overload - def to_numpy( # pyrefly: ignore + def to_numpy( self, dtype: None = None, copy: bool = False, diff --git a/pandas-stubs/core/indexes/category.pyi b/pandas-stubs/core/indexes/category.pyi index 2be826208..22475842b 100644 --- a/pandas-stubs/core/indexes/category.pyi +++ b/pandas-stubs/core/indexes/category.pyi @@ -2,9 +2,7 @@ from collections.abc import ( Hashable, Iterable, ) -from typing import ( - final, -) +from typing import final import numpy as np from pandas.core import accessor diff --git a/pandas-stubs/core/indexes/datetimelike.pyi b/pandas-stubs/core/indexes/datetimelike.pyi index a6c4b6489..32739270e 100644 --- a/pandas-stubs/core/indexes/datetimelike.pyi +++ b/pandas-stubs/core/indexes/datetimelike.pyi @@ -2,8 +2,10 @@ from typing import Any 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 ( @@ -11,6 +13,7 @@ from pandas._typing import ( AxisIndex, GenericT_co, TimeUnit, + np_ndarray_complex, ) class DatetimeIndexOpsMixin(ExtensionIndex[S1, GenericT_co]): @@ -48,9 +51,12 @@ class DatetimeIndexOpsMixin(ExtensionIndex[S1, GenericT_co]): *args: Any, **kwargs: Any, ) -> 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/multi.pyi b/pandas-stubs/core/indexes/multi.pyi index a4301ad29..b64cd1f2d 100644 --- a/pandas-stubs/core/indexes/multi.pyi +++ b/pandas-stubs/core/indexes/multi.pyi @@ -109,7 +109,7 @@ class MultiIndex(Index): def dropna(self, how: AnyAll = "any") -> Self: ... def get_level_values(self, level: str | int) -> Index: ... def unique(self, level=...): ... - def to_frame( # pyrefly: ignore + def to_frame( self, index: bool = True, name: list[HashableT] = ..., @@ -131,7 +131,7 @@ class MultiIndex(Index): def __getitem__( # pyright: ignore[reportIncompatibleMethodOverride] self, key: int ) -> tuple: ... - def append(self, other): ... # pyrefly: ignore + def append(self, other): ... def repeat(self, repeats, axis=...): ... def drop(self, codes, level: Level | None = None, errors: str = "raise") -> Self: ... # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] def swaplevel(self, i: int = -2, j: int = -1): ... diff --git a/pandas-stubs/core/indexes/range.pyi b/pandas-stubs/core/indexes/range.pyi index e2fe1c8c0..5e4055fb8 100644 --- a/pandas-stubs/core/indexes/range.pyi +++ b/pandas-stubs/core/indexes/range.pyi @@ -81,7 +81,7 @@ class RangeIndex(_IndexSubclassBase[int, np.int64]): def all(self, *args: Any, **kwargs: Any) -> bool: ... def any(self, *args: Any, **kwargs: Any) -> bool: ... @final - def union( # pyrefly: ignore + def union( self, other: list[HashableT] | Index, sort: bool | None = None ) -> Index | Index[int] | RangeIndex: ... @overload # type: ignore[override] diff --git a/pandas-stubs/core/indexes/timedeltas.pyi b/pandas-stubs/core/indexes/timedeltas.pyi index e87592dc8..5b0915c6b 100644 --- a/pandas-stubs/core/indexes/timedeltas.pyi +++ b/pandas-stubs/core/indexes/timedeltas.pyi @@ -20,13 +20,21 @@ from pandas.core.indexes.datetimelike import DatetimeTimedeltaMixin from pandas.core.indexes.datetimes import DatetimeIndex from pandas.core.indexes.period import PeriodIndex from pandas.core.series import Series -from typing_extensions import Self +from typing_extensions import ( + Never, + Self, +) from pandas._libs import Timedelta from pandas._libs.tslibs import BaseOffset from pandas._typing import ( AxesData, TimedeltaConvertibleTypes, + np_ndarray_anyint, + np_ndarray_bool, + np_ndarray_complex, + np_ndarray_dt, + np_ndarray_float, np_ndarray_td, num, ) @@ -64,9 +72,48 @@ 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: ... + @overload # type: ignore[override] + def __mul__(self, other: np_ndarray_complex) -> Never: ... + @overload + def __mul__( + self, + other: ( + float + | Sequence[float] + | np_ndarray_bool + | np_ndarray_anyint + | np_ndarray_float + | Index[bool] + | Index[int] + | Index[float] + ), + ) -> Self: ... + @overload # type: ignore[override] + def __rmul__(self, other: np_ndarray_complex) -> Never: ... + @overload + def __rmul__( + self, + other: ( + 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 be7ca541c..a0513eaf4 100644 --- a/pandas-stubs/core/series.pyi +++ b/pandas-stubs/core/series.pyi @@ -37,7 +37,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, @@ -2543,85 +2546,137 @@ 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[complex], other: np_ndarray_td) -> Never: ... @overload - def __mul__(self: Series[bool], other: np_ndarray_anyint) -> Series[int]: ... + 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_float) -> Series[float]: ... + def __mul__(self: Series[Timestamp], other: np_ndarray) -> Never: ... + @overload + 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]: ... - @overload - def __mul__(self: Series[int], other: np_ndarray_float) -> Series[float]: ... + self: Series[_str], + other: np_ndarray_float | np_ndarray_complex | np_ndarray_dt | np_ndarray_td, + ) -> Never: ... + # pandas-dev/pandas#62595: we may want to support Series[str] * bool + # also in 3.x @overload def __mul__( - self: Series[float], + self: Series[_str], other: ( int | Sequence[int] | np_ndarray_bool | np_ndarray_anyint - | np_ndarray_float - | Index[T_INT] - | Series[T_INT] + | Index[bool] + | Index[int] + | Series[bool] + | Series[int] ), - ) -> Series[float]: ... + ) -> Series[_str]: ... + @overload + def __mul__(self: Series[T_INT], other: bool | Sequence[bool]) -> Series[T_INT]: ... + @overload + def __mul__(self: Series[float], other: int | Sequence[int]) -> Series[float]: ... @overload def __mul__( - self: Series[float], - other: T_COMPLEX | Sequence[T_COMPLEX] | Index[T_COMPLEX] | Series[T_COMPLEX], - ) -> Series[T_COMPLEX]: ... + self: Series[complex], other: float | Sequence[float] + ) -> Series[complex]: ... @overload def __mul__( - self: Series[complex], + self: Series[S1_CT], other: ( - T_COMPLEX - | Sequence[T_COMPLEX] - | np_ndarray_bool - | np_ndarray_anyint - | np_ndarray_float - | Index[T_COMPLEX] - | Series[T_COMPLEX] + SupportsRMul[S1_CT, S1_CO_NSDT] | Sequence[SupportsRMul[S1_CT, S1_CO_NSDT]] ), - ) -> Series[complex]: ... + ) -> Series[S1_CO_NSDT]: ... @overload def __mul__( - self: Series[T_COMPLEX], other: np_ndarray_complex - ) -> Series[complex]: ... + self: Series[T_COMPLEX], other: np_ndarray_bool | Index[bool] | Series[bool] + ) -> Series[T_COMPLEX]: ... @overload def __mul__( + self: Series[bool], other: np_ndarray_anyint | Index[int] | Series[int] + ) -> Series[int]: ... + @overload + def __mul__( + 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 @@ -2636,136 +2691,121 @@ class Series(IndexOpsMixin[S1], NDFrame): | Series[int] | Series[float] ), + level: Level | None = ..., + fill_value: float | None = None, + axis: AxisIndex | None = 0, ) -> Series[Timedelta]: ... + # pandas-dev/pandas#62595: we may want to support Series[str] * bool + # also in 3.x @overload def mul( - self: Series[Never], - other: complex | _ListLike | Index | Series, + self: Series[_str], + other: ( + int + | Sequence[int] + | np_ndarray_bool + | np_ndarray_anyint + | Index[bool] + | Index[int] + | Series[bool] + | Series[int] + ), level: Level | None = None, fill_value: float | None = None, axis: int = 0, - ) -> Series: ... + ) -> Series[_str]: ... @overload def mul( - self, - other: Index[Never] | Series[Never], + 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: Series[bool], - other: T_COMPLEX | Sequence[T_COMPLEX] | Index[T_COMPLEX] | Series[T_COMPLEX], + self: Series[float], + other: int | Sequence[int], level: Level | None = None, fill_value: float | None = None, axis: int = 0, - ) -> Series[T_COMPLEX]: ... + ) -> Series[float]: ... @overload def mul( - self: Series[bool], - other: np_ndarray_bool, + self: Series[complex], + other: float | Sequence[float], level: Level | None = None, fill_value: float | None = None, axis: int = 0, - ) -> Series[bool]: ... + ) -> Series[complex]: ... @overload def mul( - self: Series[bool], - other: np_ndarray_anyint, + 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[int]: ... + ) -> Series[S1_CO_NSDT]: ... @overload def mul( - self: Series[bool], - other: np_ndarray_float, + 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[float]: ... + ) -> Series[T_COMPLEX]: ... @overload def mul( - self: Series[int], - other: ( - bool - | Sequence[bool] - | np_ndarray_bool - | np_ndarray_anyint - | Index[bool] - | Series[bool] - ), + self: 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 @@ -2775,12 +2815,15 @@ 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[complex], other: np_ndarray_td) -> Never: ... + @overload + def __rmul__(self: Series[Timedelta], other: np_ndarray_complex) -> Never: ... + @overload + def __rmul__( self: Series[Timedelta], other: ( float @@ -2795,96 +2838,104 @@ 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]: ... - @overload - def __rmul__(self: Series[bool], other: np_ndarray_anyint) -> Series[int]: ... - @overload - def __rmul__(self: Series[bool], other: np_ndarray_float) -> Series[float]: ... + self: Series[_str], + other: np_ndarray_float | np_ndarray_complex | np_ndarray_dt | np_ndarray_td, + ) -> Never: ... + # pandas-dev/pandas#62595: we may want to support Series[str] * bool + # also in 3.x @overload def __rmul__( - self: Series[int], + self: Series[_str], other: ( - bool - | Sequence[bool] + int + | Sequence[int] | np_ndarray_bool | np_ndarray_anyint | Index[bool] + | Index[int] | Series[bool] + | Series[int] ), - ) -> Series[int]: ... + ) -> Series[_str]: ... @overload def __rmul__( - self: Series[int], - other: T_COMPLEX | Sequence[T_COMPLEX] | Index[T_COMPLEX] | Series[T_COMPLEX], - ) -> Series[T_COMPLEX]: ... + self: Series[T_INT], other: bool | Sequence[bool] + ) -> Series[T_INT]: ... @overload - def __rmul__(self: Series[int], other: np_ndarray_float) -> Series[float]: ... + def __rmul__(self: Series[float], other: int | Sequence[int]) -> Series[float]: ... @overload def __rmul__( - self: Series[float], + self: Series[complex], other: float | Sequence[float] + ) -> Series[complex]: ... + @overload + def __rmul__( + self: Series[S1_CT], other: ( - int - | Sequence[int] - | np_ndarray_bool - | np_ndarray_anyint - | np_ndarray_float - | Index[T_INT] - | Series[T_INT] + SupportsMul[S1_CT, S1_CO_NSDT] | Sequence[SupportsMul[S1_CT, S1_CO_NSDT]] ), - ) -> Series[float]: ... + ) -> Series[S1_CO_NSDT]: ... @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_bool | Index[bool] | Series[bool] ) -> 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] - ), - ) -> Series[complex]: ... + self: Series[bool], other: np_ndarray_anyint | Index[int] | Series[int] + ) -> Series[int]: ... @overload def __rmul__( - 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 __rmul__( + self: Series[bool] | Series[int], + other: np_ndarray_float | Index[float] | Series[float], + ) -> Series[float]: ... + @overload + def __rmul__( + self: Series[T_COMPLEX], other: np_ndarray_float | Index[float] | Series[float] + ) -> Series[T_COMPLEX]: ... + @overload + def __rmul__( + self: Series[T_COMPLEX], + other: np_ndarray_complex | Index[complex] | Series[complex], + ) -> Series[complex]: ... + @overload + 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( + 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 @@ -2899,169 +2950,111 @@ class Series(IndexOpsMixin[S1], NDFrame): | Series[int] | Series[float] ), + level: Level | None = ..., + fill_value: float | None = None, + axis: AxisIndex | None = 0, ) -> Series[Timedelta]: ... + # pandas-dev/pandas#62595: we may want to support Series[str] * bool + # also in 3.x @overload def rmul( - self: Series[Never], - other: complex | _ListLike | Index | Series, + self: Series[_str], + other: ( + int + | Sequence[int] + | np_ndarray_bool + | np_ndarray_anyint + | Index[bool] + | Index[int] + | Series[bool] + | Series[int] + ), level: Level | None = None, fill_value: float | None = None, axis: int = 0, - ) -> Series: ... + ) -> Series[_str]: ... @overload def rmul( - self, - other: Index[Never] | Series[Never], + 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: Series[bool], - other: T_COMPLEX | Sequence[T_COMPLEX] | Index[T_COMPLEX] | Series[T_COMPLEX], + self: Series[float], + other: int | Sequence[int], level: Level | None = None, fill_value: float | None = None, axis: int = 0, - ) -> Series[T_COMPLEX]: ... + ) -> Series[float]: ... @overload def rmul( - self: Series[bool], - other: np_ndarray_bool, + self: Series[complex], + other: float | Sequence[float], level: Level | None = None, fill_value: float | None = None, axis: int = 0, - ) -> Series[bool]: ... + ) -> Series[complex]: ... @overload def rmul( - self: Series[bool], - other: np_ndarray_anyint, + 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[int]: ... + ) -> Series[S1_CO_NSDT]: ... @overload def rmul( - self: Series[bool], - other: np_ndarray_float, + 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[float]: ... + ) -> Series[T_COMPLEX]: ... @overload def rmul( - self: Series[int], - other: ( - bool - | Sequence[bool] - | np_ndarray_bool - | np_ndarray_anyint - | Index[bool] - | Series[bool] - ), + self: 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]: ... @@ -4643,7 +4636,7 @@ class Series(IndexOpsMixin[S1], NDFrame): **kwargs: Any, ) -> np_1darray: ... @overload - def to_numpy( # pyrefly: ignore[bad-override] + def to_numpy( self: Series[Timestamp], dtype: type[np.datetime64] | None = None, copy: bool = False, @@ -4659,7 +4652,7 @@ class Series(IndexOpsMixin[S1], NDFrame): **kwargs: Any, ) -> np_1darray[GenericT]: ... @overload - def to_numpy( # pyrefly: ignore[bad-override] + def to_numpy( self: Series[Timedelta], dtype: type[np.timedelta64] | None = None, copy: bool = False, diff --git a/pandas-stubs/core/window/ewm.pyi b/pandas-stubs/core/window/ewm.pyi index eb3c6ed33..ea7e3b0ce 100644 --- a/pandas-stubs/core/window/ewm.pyi +++ b/pandas-stubs/core/window/ewm.pyi @@ -51,7 +51,7 @@ class ExponentialMovingWindowGroupby( class OnlineExponentialMovingWindow(ExponentialMovingWindow[NDFrameT]): def reset(self) -> None: ... def aggregate(self, func, *args: Any, **kwargs: Any): ... - def std(self, bias: bool = False, *args: Any, **kwargs: Any): ... # pyrefly: ignore + def std(self, bias: bool = False, *args: Any, **kwargs: Any): ... def corr( self, other: DataFrame | Series | None = None, @@ -66,7 +66,7 @@ class OnlineExponentialMovingWindow(ExponentialMovingWindow[NDFrameT]): numeric_only: bool = False, ): ... def var(self, bias: bool = False, numeric_only: bool = False): ... - def mean( # pyrefly: ignore + def mean( self, *args: Any, update: NDFrameT | None = ..., diff --git a/tests/extension/decimal/array.py b/tests/extension/decimal/array.py index 2f6faa0be..b043996f1 100644 --- a/tests/extension/decimal/array.py +++ b/tests/extension/decimal/array.py @@ -23,9 +23,7 @@ ) from pandas.core.indexers import check_array_indexer -from pandas._typing import ( - TakeIndexer, -) +from pandas._typing import TakeIndexer from pandas.core.dtypes.base import ExtensionDtype from pandas.core.dtypes.common import ( diff --git a/tests/indexes/arithmetic/bool/test_mul.py b/tests/indexes/arithmetic/bool/test_mul.py new file mode 100644 index 000000000..a68a63ce2 --- /dev/null +++ b/tests/indexes/arithmetic/bool/test_mul.py @@ -0,0 +1,147 @@ +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 +import pytest +from typing_extensions import ( + Never, + assert_type, +) + +from tests import ( + PD_LTE_23, + TYPE_CHECKING_INVALID_USAGE, + check, +) + + +@pytest.fixture +def left() -> "pd.Index[bool]": + """left operand""" + lo = pd.Index([True, True, False]) + return check(assert_type(lo, "pd.Index[bool]"), pd.Index, np.bool_) + + +def test_mul_py_scalar(left: "pd.Index[bool]") -> None: + """Test pd.Index[bool] * Python native scalars""" + b, i, f, c = True, 1, 1.0, 1j + s, d = datetime(2025, 10, 1), timedelta(seconds=1) + + check(assert_type(left * b, "pd.Index[bool]"), pd.Index, np.bool_) + 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: + _04 = 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[bool]"), pd.Index, np.bool_) + 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: + _14 = s * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + check(assert_type(d * left, pd.TimedeltaIndex), pd.TimedeltaIndex, pd.Timedelta) + + +def test_mul_py_sequence(left: "pd.Index[bool]") -> None: + """Test pd.Index[bool] * 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, 10, d) for d in (1, 2, 3)] + d = [timedelta(seconds=s + 1) for s in range(3)] + + check(assert_type(left * b, "pd.Index[bool]"), pd.Index, np.bool_) + 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: + _04 = 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[bool]"), pd.Index, np.bool_) + 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: + _14 = 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(left: "pd.Index[bool]") -> None: + """Test pd.Index[bool] * 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-10-{d:02d}") for d in (1, 2, 3)], np.datetime64) + d = np.array([np.timedelta64(s + 1, "s") for s in range(3)], np.timedelta64) + + check(assert_type(left * b, "pd.Index[bool]"), pd.Index, np.bool_) + 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) + if PD_LTE_23: + # pandas-dev/pandas#62316: both timedelta * bool + # and np.timedelta64 * bool works, so pandas probably also should work + 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 + # `Index` with the correct element type. + check(assert_type(b * left, "npt.NDArray[np.bool_]"), pd.Index, np.bool_) + 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) + if PD_LTE_23: + # pandas-dev/pandas#62316: both timedelta * bool + # and np.timedelta64 * bool works, so pandas probably also should work + check( + assert_type(d * left, "npt.NDArray[np.timedelta64]"), + pd.TimedeltaIndex, + pd.Timedelta, + ) + + +def test_mul_pd_index(left: "pd.Index[bool]") -> None: + """Test pd.Index[bool] * 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, 10, d) for d in (1, 2, 3)]) + d = pd.Index([timedelta(seconds=s + 1) for s in range(3)]) + + check(assert_type(left * b, "pd.Index[bool]"), pd.Index, np.bool_) + 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: + _04 = left * s # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + if PD_LTE_23: + # pandas-dev/pandas#62316: both timedelta * bool + # and np.timedelta64 * bool works, so pandas probably also should work + check(assert_type(left * d, pd.TimedeltaIndex), pd.Index, timedelta) + + check(assert_type(b * left, "pd.Index[bool]"), pd.Index, np.bool_) + 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: + _14 = s * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + if PD_LTE_23: + # pandas-dev/pandas#62316: both timedelta * bool + # and np.timedelta64 * bool works, so pandas probably also should work + check(assert_type(d * left, pd.TimedeltaIndex), pd.Index, timedelta) diff --git a/tests/indexes/arithmetic/complex/test_mul.py b/tests/indexes/arithmetic/complex/test_mul.py new file mode 100644 index 000000000..136d4824b --- /dev/null +++ b/tests/indexes/arithmetic/complex/test_mul.py @@ -0,0 +1,132 @@ +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 +import pytest +from typing_extensions import ( + Never, + assert_type, +) + +from tests import ( + TYPE_CHECKING_INVALID_USAGE, + check, +) + + +@pytest.fixture +def left() -> "pd.Index[complex]": + """left operand""" + lo = pd.Index([1j, 2j, 3j]) + return check(assert_type(lo, "pd.Index[complex]"), pd.Index, np.complexfloating) + + +def test_mul_py_scalar(left: "pd.Index[complex]") -> None: + """Test pd.Index[complex] * 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[complex]"), pd.Index, np.complexfloating) + check(assert_type(left * i, "pd.Index[complex]"), pd.Index, np.complexfloating) + check(assert_type(left * f, "pd.Index[complex]"), pd.Index, np.complexfloating) + check(assert_type(left * c, "pd.Index[complex]"), pd.Index, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _04 = left * s # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _05 = left * d # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + + check(assert_type(b * left, "pd.Index[complex]"), pd.Index, np.complexfloating) + check(assert_type(i * left, "pd.Index[complex]"), pd.Index, np.complexfloating) + check(assert_type(f * left, "pd.Index[complex]"), pd.Index, np.complexfloating) + check(assert_type(c * left, "pd.Index[complex]"), pd.Index, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _14 = s * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _15 = d * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + + +def test_mul_py_sequence(left: "pd.Index[complex]") -> None: + """Test pd.Index[complex] * 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[complex]"), pd.Index, np.complexfloating) + check(assert_type(left * i, "pd.Index[complex]"), pd.Index, np.complexfloating) + check(assert_type(left * f, "pd.Index[complex]"), pd.Index, np.complexfloating) + check(assert_type(left * c, "pd.Index[complex]"), pd.Index, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _04 = left * s # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _05 = left * d # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + + check(assert_type(b * left, "pd.Index[complex]"), pd.Index, np.complexfloating) + check(assert_type(i * left, "pd.Index[complex]"), pd.Index, np.complexfloating) + check(assert_type(f * left, "pd.Index[complex]"), pd.Index, np.complexfloating) + check(assert_type(c * left, "pd.Index[complex]"), pd.Index, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _14 = s * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _15 = d * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + + +def test_mul_numpy_array(left: "pd.Index[complex]") -> None: + """Test pd.Index[complex] * 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[complex]"), pd.Index, np.complexfloating) + check(assert_type(left * i, "pd.Index[complex]"), pd.Index, np.complexfloating) + check(assert_type(left * f, "pd.Index[complex]"), pd.Index, np.complexfloating) + check(assert_type(left * c, "pd.Index[complex]"), pd.Index, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + assert_type(left * s, Never) + assert_type(left * d, Never) + + # `numpy` typing gives the corresponding `ndarray`s in the static type + # checking, where our `__rmul__` cannot override. At runtime, they return + # `Index` with the correct element type. + check(assert_type(b * left, "npt.NDArray[np.bool_]"), pd.Index, np.complexfloating) + check(assert_type(i * left, "npt.NDArray[np.int64]"), pd.Index, np.complexfloating) + check( + assert_type(f * left, "npt.NDArray[np.float64]"), pd.Index, np.complexfloating + ) + check( + assert_type(c * left, "npt.NDArray[np.complex128]"), + pd.Index, + np.complexfloating, + ) + if TYPE_CHECKING_INVALID_USAGE: + assert_type(s * left, Any) + assert_type(d * left, "npt.NDArray[np.timedelta64]") + + +def test_mul_pd_index(left: "pd.Index[complex]") -> None: + """Test pd.Index[complex] * 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[complex]"), pd.Index, np.complexfloating) + check(assert_type(left * i, "pd.Index[complex]"), pd.Index, np.complexfloating) + check(assert_type(left * f, "pd.Index[complex]"), pd.Index, np.complexfloating) + check(assert_type(left * c, "pd.Index[complex]"), pd.Index, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _04 = left * s # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _05 = left * d # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + + check(assert_type(b * left, "pd.Index[complex]"), pd.Index, np.complexfloating) + check(assert_type(i * left, "pd.Index[complex]"), pd.Index, np.complexfloating) + check(assert_type(f * left, "pd.Index[complex]"), pd.Index, np.complexfloating) + check(assert_type(c * left, "pd.Index[complex]"), pd.Index, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _14 = s * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _15 = d * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] diff --git a/tests/indexes/arithmetic/datetimeindex/__init__.py b/tests/indexes/arithmetic/datetimeindex/__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..45a1587e9 --- /dev/null +++ b/tests/indexes/arithmetic/float/test_mul.py @@ -0,0 +1,134 @@ +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 +import pytest +from typing_extensions import ( + Never, + assert_type, +) + +from tests import ( + TYPE_CHECKING_INVALID_USAGE, + check, +) + + +@pytest.fixture +def left() -> "pd.Index[float]": + """left operand""" + lo = pd.Index([1.0, 2.0, 3.0]) + return check(assert_type(lo, "pd.Index[float]"), pd.Index, np.floating) + + +def test_mul_py_scalar(left: "pd.Index[float]") -> 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: + _04 = 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: + _14 = s * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + check(assert_type(d * left, "pd.TimedeltaIndex"), pd.TimedeltaIndex, timedelta) + + +def test_mul_py_sequence(left: "pd.Index[float]") -> 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: + _04 = 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: + _14 = 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(left: "pd.Index[float]") -> 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 + # `Index` 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(left: "pd.Index[float]") -> 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: + _04 = 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: + _14 = 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..69c49cad9 --- /dev/null +++ b/tests/indexes/arithmetic/int/test_mul.py @@ -0,0 +1,134 @@ +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 +import pytest +from typing_extensions import ( + Never, + assert_type, +) + +from tests import ( + TYPE_CHECKING_INVALID_USAGE, + check, +) + + +@pytest.fixture +def left() -> "pd.Index[int]": + """left operand""" + lo = pd.Index([1, 2, 3]) + return check(assert_type(lo, "pd.Index[int]"), pd.Index, np.integer) + + +def test_mul_py_scalar(left: "pd.Index[int]") -> 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: + _04 = 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: + _14 = s * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + check(assert_type(d * left, pd.TimedeltaIndex), pd.Index, pd.Timedelta) + + +def test_mul_py_sequence(left: "pd.Index[int]") -> 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: + _04 = left * s # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + # pandas-dev/pandas#62524: An index of Python native timedeltas can be + # produced, instead of a TimedeltaIndex, hence the test + 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: + _14 = s * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + # pandas-dev/pandas#62524: An index of Python native timedeltas can be + # produced, instead of a TimedeltaIndex, hence the test + check(assert_type(d * left, "pd.Index[pd.Timedelta]"), pd.Index, timedelta) + + +def test_mul_numpy_array(left: "pd.Index[int]") -> 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 + # `Index` 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(left: "pd.Index[int]") -> 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: + _04 = 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: + _14 = 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/str/test_mul.py b/tests/indexes/arithmetic/str/test_mul.py new file mode 100644 index 000000000..2f1b45bfd --- /dev/null +++ b/tests/indexes/arithmetic/str/test_mul.py @@ -0,0 +1,143 @@ +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 +import pytest +from typing_extensions import ( + Never, + assert_type, +) + +from tests import ( + PD_LTE_23, + TYPE_CHECKING_INVALID_USAGE, + check, +) + + +@pytest.fixture +def left() -> "pd.Index[str]": + """left operand""" + lo = pd.Index(["1", "2", "3"]) + return check(assert_type(lo, "pd.Index[str]"), pd.Index, str) + + +def test_mul_py_scalar(left: "pd.Index[str]") -> None: + """Test pd.Index[str] * Python native scalars""" + b, i, f, c = True, 1, 1.0, 1j + s, d = datetime(2025, 9, 27), timedelta(seconds=1) + + # pandas-dev/pandas#62595: we may want to support Series[str] * bool + # also in 3.x + if PD_LTE_23: + check(assert_type(left * b, "pd.Index[str]"), pd.Index, str) + check(assert_type(left * i, "pd.Index[str]"), pd.Index, str) + if TYPE_CHECKING_INVALID_USAGE: + _02 = left * f # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _03 = left * c # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _04 = left * s # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _05 = left * d # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + + if PD_LTE_23: + check(assert_type(b * left, "pd.Index[str]"), pd.Index, str) + check(assert_type(i * left, "pd.Index[str]"), pd.Index, str) + if TYPE_CHECKING_INVALID_USAGE: + _12 = f * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _13 = c * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _14 = s * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _15 = d * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + + +def test_mul_py_sequence(left: "pd.Index[str]") -> None: + """Test pd.Index[str] * 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)] + + # pandas-dev/pandas#62595: we may want to support Series[str] * bool + # also in 3.x + if PD_LTE_23: + check(assert_type(left * b, "pd.Index[str]"), pd.Index, str) + check(assert_type(left * i, "pd.Index[str]"), pd.Index, str) + if TYPE_CHECKING_INVALID_USAGE: + _02 = left * f # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _03 = left * c # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _04 = left * s # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _05 = left * d # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + + if PD_LTE_23: + check(assert_type(b * left, "pd.Index[str]"), pd.Index, str) + check(assert_type(i * left, "pd.Index[str]"), pd.Index, str) + if TYPE_CHECKING_INVALID_USAGE: + _12 = f * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _13 = c * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _14 = s * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _15 = d * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + + +def test_mul_numpy_array(left: "pd.Index[str]") -> None: + """Test pd.Index[str] * 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) + + # pandas-dev/pandas#62595: we may want to support Series[str] * bool + # also in 3.x + if PD_LTE_23: + check(assert_type(left * b, "pd.Index[str]"), pd.Index, str) + check(assert_type(left * i, "pd.Index[str]"), pd.Index, str) + if TYPE_CHECKING_INVALID_USAGE: + assert_type(left * f, Never) + assert_type(left * c, Never) + assert_type(left * s, Never) + assert_type(left * d, Never) + + # `numpy` typing gives the corresponding `ndarray`s in the static type + # checking, where our `__rmul__` cannot override. At runtime, they return + # `Index` with the correct element type. + if PD_LTE_23: + check(assert_type(b * left, "npt.NDArray[np.bool_]"), pd.Index, str) + check(assert_type(i * left, "npt.NDArray[np.int64]"), pd.Index, str) + if TYPE_CHECKING_INVALID_USAGE: + assert_type(f * left, "npt.NDArray[np.float64]") + assert_type(c * left, "npt.NDArray[np.complex128]") + assert_type(s * left, Any) + assert_type(d * left, "npt.NDArray[np.timedelta64]") + + +def test_mul_pd_index(left: "pd.Index[str]") -> None: + """Test pd.Index[str] * 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)]) + + # pandas-dev/pandas#62595: we may want to support Series[str] * bool + # also in 3.x + if PD_LTE_23: + check(assert_type(left * b, "pd.Index[str]"), pd.Index, str) + check(assert_type(left * i, "pd.Index[str]"), pd.Index, str) + if TYPE_CHECKING_INVALID_USAGE: + _02 = left * f # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _03 = left * c # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _04 = left * s # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _05 = left * d # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + + if PD_LTE_23: + check(assert_type(b * left, "pd.Index[str]"), pd.Index, str) + check(assert_type(i * left, "pd.Index[str]"), pd.Index, str) + if TYPE_CHECKING_INVALID_USAGE: + _12 = f * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _13 = c * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _14 = s * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _15 = d * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] diff --git a/tests/indexes/arithmetic/test_mul.py b/tests/indexes/arithmetic/test_mul.py new file mode 100644 index 000000000..5239e44dc --- /dev/null +++ b/tests/indexes/arithmetic/test_mul.py @@ -0,0 +1,112 @@ +from typing import Any + +import numpy as np +import pandas as pd +import pytest +from typing_extensions import assert_type + +from tests import ( + TYPE_CHECKING_INVALID_USAGE, + check, +) + + +@pytest.fixture +def left_i() -> pd.Index: + """left operand""" + lo = pd.MultiIndex.from_tuples([(1,), (2,), (3,)]).levels[0] + return check(assert_type(lo, pd.Index), pd.Index) + + +def test_mul_py_scalar(left_i: pd.Index) -> None: + """Test pd.Index[Any] (int) * Python native scalars""" + b, i, f, c = True, 1, 1.0, 1j + + check(assert_type(left_i * b, pd.Index), pd.Index) + check(assert_type(left_i * i, pd.Index), pd.Index) + check(assert_type(left_i * f, pd.Index), pd.Index) + check(assert_type(left_i * c, pd.Index), pd.Index) + + check(assert_type(b * left_i, pd.Index), pd.Index) + check(assert_type(i * left_i, pd.Index), pd.Index) + check(assert_type(f * left_i, pd.Index), pd.Index) + check(assert_type(c * left_i, pd.Index), pd.Index) + + +def test_mul_py_sequence(left_i: pd.Index) -> None: + """Test pd.Index[Any] (int) * Python native sequences""" + b, i, f, c = [True, False, True], [2, 3, 5], [1.0, 2.0, 3.0], [1j, 1j, 4j] + + check(assert_type(left_i * b, pd.Index), pd.Index) + check(assert_type(left_i * i, pd.Index), pd.Index) + check(assert_type(left_i * f, pd.Index), pd.Index) + check(assert_type(left_i * c, pd.Index), pd.Index) + + check(assert_type(b * left_i, pd.Index), pd.Index) + check(assert_type(i * left_i, pd.Index), pd.Index) + check(assert_type(f * left_i, pd.Index), pd.Index) + check(assert_type(c * left_i, pd.Index), pd.Index) + + +def test_mul_numpy_array(left_i: pd.Index) -> None: + """Test pd.Index[Any] (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) + + check(assert_type(left_i * b, pd.Index), pd.Index) + check(assert_type(left_i * i, pd.Index), pd.Index) + check(assert_type(left_i * f, pd.Index), pd.Index) + check(assert_type(left_i * c, pd.Index), pd.Index) + + # `numpy` typing gives the corresponding `ndarray`s in the static type + # checking, where our `__rmul__` cannot override. At runtime, they return + # `Index`. + # microsoft/pyright#10924 + check( + assert_type(b * left_i, Any), # pyright: ignore[reportAssertTypeFailure] + pd.Index, + ) + check( + assert_type(i * left_i, Any), # pyright: ignore[reportAssertTypeFailure] + pd.Index, + ) + check( + assert_type(f * left_i, Any), # pyright: ignore[reportAssertTypeFailure] + pd.Index, + ) + check( + assert_type(c * left_i, Any), # pyright: ignore[reportAssertTypeFailure] + pd.Index, + ) + + +def test_mul_pd_index(left_i: pd.Index) -> None: + """Test pd.Index[Any] (int) * pandas Indexes""" + a = pd.MultiIndex.from_tuples([(1,), (2,), (3,)]).levels[0] + 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]) + + check(assert_type(left_i * a, pd.Index), pd.Index) + check(assert_type(left_i * b, pd.Index), pd.Index) + check(assert_type(left_i * i, pd.Index), pd.Index) + check(assert_type(left_i * f, pd.Index), pd.Index) + check(assert_type(left_i * c, pd.Index), pd.Index) + + check(assert_type(a * left_i, pd.Index), pd.Index) + check(assert_type(b * left_i, pd.Index), pd.Index) + check(assert_type(i * left_i, pd.Index), pd.Index) + check(assert_type(f * left_i, pd.Index), pd.Index) + check(assert_type(c * left_i, pd.Index), pd.Index) + + +def test_mul_str_py_str(left_i: pd.Index) -> None: + """Test pd.Index[Any] (int) * Python str""" + s = "abc" + + if TYPE_CHECKING_INVALID_USAGE: + _0 = left_i * s # type: ignore[operator] # pyright:ignore[reportOperatorIssue] + _1 = s * left_i # type: ignore[operator] # pyright:ignore[reportOperatorIssue] 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..590dc57d6 --- /dev/null +++ b/tests/indexes/arithmetic/timedelta/test_mul.py @@ -0,0 +1,115 @@ +from datetime import timedelta + +import numpy as np +from numpy import typing as npt # noqa: F401 +import pandas as pd +import pytest +from typing_extensions import ( + Never, + assert_type, +) + +from tests import ( + PD_LTE_23, + TYPE_CHECKING_INVALID_USAGE, + check, +) + + +@pytest.fixture +def left() -> "pd.Index[pd.Timedelta]": + """left operand""" + # pandas-dev/pandas#62524: An index of Python native timedeltas can be + # produced, instead of a TimedeltaIndex, hence this test file + lo = pd.Index([1]) * [timedelta(seconds=1)] # left operand + return check(assert_type(lo, "pd.Index[pd.Timedelta]"), pd.Index, timedelta) + + +def test_mul_py_scalar(left: "pd.Index[pd.Timedelta]") -> None: + """Test pd.Index[pd.Timedelta] * Python native scalars""" + b, i, f, c = True, 1, 1.0, 1j + + # pandas-dev/pandas#62316 + if PD_LTE_23: + check(assert_type(left * b, "pd.Index[pd.Timedelta]"), pd.Index, timedelta) + check(assert_type(left * i, "pd.Index[pd.Timedelta]"), pd.Index, timedelta) + check(assert_type(left * f, "pd.Index[pd.Timedelta]"), pd.Index, timedelta) + if TYPE_CHECKING_INVALID_USAGE: + _0 = left * c # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + + if PD_LTE_23: + check(assert_type(b * left, "pd.Index[pd.Timedelta]"), pd.Index, timedelta) + check(assert_type(i * left, "pd.Index[pd.Timedelta]"), pd.Index, timedelta) + check(assert_type(f * left, "pd.Index[pd.Timedelta]"), pd.Index, timedelta) + if TYPE_CHECKING_INVALID_USAGE: + _1 = c * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + + +def test_mul_py_sequence(left: "pd.Index[pd.Timedelta]") -> None: + """Test pd.Index[pd.Timedelta] * Python native sequences""" + b, i, f, c = [True], [2], [1.5], [1.7j] + + # pandas-dev/pandas#62316 + if PD_LTE_23: + check(assert_type(left * b, "pd.Index[pd.Timedelta]"), pd.Index, timedelta) + check(assert_type(left * i, "pd.Index[pd.Timedelta]"), pd.Index, timedelta) + check(assert_type(left * f, "pd.Index[pd.Timedelta]"), pd.Index, timedelta) + if TYPE_CHECKING_INVALID_USAGE: + _0 = left * c # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + + if PD_LTE_23: + check(assert_type(b * left, "pd.Index[pd.Timedelta]"), pd.Index, timedelta) + check(assert_type(i * left, "pd.Index[pd.Timedelta]"), pd.Index, timedelta) + check(assert_type(f * left, "pd.Index[pd.Timedelta]"), pd.Index, timedelta) + if TYPE_CHECKING_INVALID_USAGE: + _1 = c * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + + +def test_mul_numpy_array(left: "pd.Index[pd.Timedelta]") -> None: + """Test pd.Index[pd.Timedelta] * numpy arrays""" + b = np.array([True], np.bool_) + i = np.array([2], np.int64) + f = np.array([1.5], np.float64) + c = np.array([1.7j], np.complex128) + + # pandas-dev/pandas#62316 + if PD_LTE_23: + check(assert_type(left * b, "pd.Index[pd.Timedelta]"), pd.Index, timedelta) + check(assert_type(left * i, "pd.Index[pd.Timedelta]"), pd.Index, timedelta) + check(assert_type(left * f, "pd.Index[pd.Timedelta]"), pd.Index, timedelta) + if TYPE_CHECKING_INVALID_USAGE: + assert_type(left * c, Never) + + # `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. + if PD_LTE_23: + check(assert_type(b * left, "npt.NDArray[np.bool_]"), pd.Index, timedelta) + check(assert_type(i * left, "npt.NDArray[np.int64]"), pd.Index, timedelta) + check(assert_type(f * left, "npt.NDArray[np.float64]"), pd.Index, timedelta) + if TYPE_CHECKING_INVALID_USAGE: + # We made it Never, but numpy takes over + assert_type(c * left, "npt.NDArray[np.complex128]") + + +def test_mul_pd_index(left: "pd.Index[pd.Timedelta]") -> None: + """Test pd.Index[pd.Timedelta] * pandas Indexes""" + b = pd.Index([True]) + i = pd.Index([2]) + f = pd.Index([1.5]) + c = pd.Index([1.7j]) + + # pandas-dev/pandas#62316 + if PD_LTE_23: + check(assert_type(left * b, "pd.Index[pd.Timedelta]"), pd.Index, timedelta) + check(assert_type(left * i, "pd.Index[pd.Timedelta]"), pd.Index, timedelta) + check(assert_type(left * f, "pd.Index[pd.Timedelta]"), pd.Index, timedelta) + if TYPE_CHECKING_INVALID_USAGE: + _0 = left * c # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + + if PD_LTE_23: + check(assert_type(b * left, "pd.Index[pd.Timedelta]"), pd.Index, timedelta) + check(assert_type(i * left, "pd.Index[pd.Timedelta]"), pd.Index, timedelta) + check(assert_type(f * left, "pd.Index[pd.Timedelta]"), pd.Index, timedelta) + if TYPE_CHECKING_INVALID_USAGE: + _1 = c * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] diff --git a/tests/indexes/arithmetic/timedeltaindex/__init__.py b/tests/indexes/arithmetic/timedeltaindex/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/indexes/arithmetic/timedeltaindex/test_mul.py b/tests/indexes/arithmetic/timedeltaindex/test_mul.py new file mode 100644 index 000000000..3bc6577c3 --- /dev/null +++ b/tests/indexes/arithmetic/timedeltaindex/test_mul.py @@ -0,0 +1,121 @@ +import numpy as np +from numpy import typing as npt # noqa: F401 +import pandas as pd +import pytest +from typing_extensions import ( + Never, + assert_type, +) + +from tests import ( + PD_LTE_23, + TYPE_CHECKING_INVALID_USAGE, + check, +) + + +@pytest.fixture +def left() -> pd.TimedeltaIndex: + """left operand""" + lo = pd.Index([pd.Timedelta(1, "s")]) # left operand + return check(assert_type(lo, pd.TimedeltaIndex), pd.TimedeltaIndex, pd.Timedelta) + + +def test_mul_py_scalar(left: pd.TimedeltaIndex) -> None: + """Test pd.TimedeltaIndex * Python native scalars""" + b, i, f, c = True, 1, 1.0, 1j + + # pandas-dev/pandas#62316 + if PD_LTE_23: + check(assert_type(left * b, pd.TimedeltaIndex), pd.TimedeltaIndex, pd.Timedelta) + check(assert_type(left * i, pd.TimedeltaIndex), pd.TimedeltaIndex, pd.Timedelta) + check(assert_type(left * f, pd.TimedeltaIndex), pd.TimedeltaIndex, pd.Timedelta) + if TYPE_CHECKING_INVALID_USAGE: + _0 = left * c # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + + if PD_LTE_23: + check(assert_type(b * left, pd.TimedeltaIndex), pd.TimedeltaIndex, pd.Timedelta) + check(assert_type(i * left, pd.TimedeltaIndex), pd.TimedeltaIndex, pd.Timedelta) + check(assert_type(f * left, pd.TimedeltaIndex), pd.TimedeltaIndex, pd.Timedelta) + if TYPE_CHECKING_INVALID_USAGE: + _1 = c * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + + +def test_mul_py_sequence(left: pd.TimedeltaIndex) -> None: + """Test pd.TimedeltaIndex * Python native sequences""" + b, i, f, c = [True], [2], [1.5], [1.7j] + + # pandas-dev/pandas#62316 + if PD_LTE_23: + check(assert_type(left * b, pd.TimedeltaIndex), pd.TimedeltaIndex, pd.Timedelta) + check(assert_type(left * i, pd.TimedeltaIndex), pd.TimedeltaIndex, pd.Timedelta) + check(assert_type(left * f, pd.TimedeltaIndex), pd.TimedeltaIndex, pd.Timedelta) + if TYPE_CHECKING_INVALID_USAGE: + _0 = left * c # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + + if PD_LTE_23: + check(assert_type(b * left, pd.TimedeltaIndex), pd.TimedeltaIndex, pd.Timedelta) + check(assert_type(i * left, pd.TimedeltaIndex), pd.TimedeltaIndex, pd.Timedelta) + check(assert_type(f * left, pd.TimedeltaIndex), pd.TimedeltaIndex, pd.Timedelta) + if TYPE_CHECKING_INVALID_USAGE: + _1 = c * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + + +def test_mul_numpy_array(left: pd.TimedeltaIndex) -> None: + """Test pd.TimedeltaIndex * numpy arrays""" + b = np.array([True], np.bool_) + i = np.array([2], np.int64) + f = np.array([1.5], np.float64) + c = np.array([1.7j], np.complex128) + + # pandas-dev/pandas#62316 + if PD_LTE_23: + check(assert_type(left * b, pd.TimedeltaIndex), pd.TimedeltaIndex, pd.Timedelta) + check(assert_type(left * i, pd.TimedeltaIndex), pd.TimedeltaIndex, pd.Timedelta) + check(assert_type(left * f, pd.TimedeltaIndex), pd.TimedeltaIndex, pd.Timedelta) + if TYPE_CHECKING_INVALID_USAGE: + assert_type(left * c, Never) + + # `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. + if PD_LTE_23: + check( + assert_type(b * left, "npt.NDArray[np.bool_]"), + pd.TimedeltaIndex, + pd.Timedelta, + ) + check( + assert_type(i * left, "npt.NDArray[np.int64]"), pd.TimedeltaIndex, pd.Timedelta + ) + check( + assert_type(f * left, "npt.NDArray[np.float64]"), + pd.TimedeltaIndex, + pd.Timedelta, + ) + if TYPE_CHECKING_INVALID_USAGE: + # We made it Never, but numpy takes over + assert_type(c * left, "npt.NDArray[np.complex128]") + + +def test_mul_pd_index(left: pd.TimedeltaIndex) -> None: + """Test pd.TimedeltaIndex * pandas Indexes""" + b = pd.Index([True]) + i = pd.Index([2]) + f = pd.Index([1.5]) + c = pd.Index([1.7j]) + + # pandas-dev/pandas#62316 + if PD_LTE_23: + check(assert_type(left * b, pd.TimedeltaIndex), pd.TimedeltaIndex, pd.Timedelta) + check(assert_type(left * i, pd.TimedeltaIndex), pd.TimedeltaIndex, pd.Timedelta) + check(assert_type(left * f, pd.TimedeltaIndex), pd.TimedeltaIndex, pd.Timedelta) + if TYPE_CHECKING_INVALID_USAGE: + _0 = left * c # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + + if PD_LTE_23: + check(assert_type(b * left, pd.TimedeltaIndex), pd.TimedeltaIndex, pd.Timedelta) + check(assert_type(i * left, pd.TimedeltaIndex), pd.TimedeltaIndex, pd.Timedelta) + check(assert_type(f * left, pd.TimedeltaIndex), pd.TimedeltaIndex, pd.Timedelta) + if TYPE_CHECKING_INVALID_USAGE: + _1 = c * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] diff --git a/tests/scalars/test_scalars.py b/tests/scalars/test_scalars.py index c2cc3786a..03053901d 100644 --- a/tests/scalars/test_scalars.py +++ b/tests/scalars/test_scalars.py @@ -518,7 +518,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 @@ -534,11 +533,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( @@ -576,11 +570,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( @@ -611,11 +600,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, @@ -645,11 +629,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( @@ -702,18 +681,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) @@ -727,18 +694,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/bool/test_mul.py b/tests/series/arithmetic/bool/test_mul.py index c7e76e917..d781e0d8d 100644 --- a/tests/series/arithmetic/bool/test_mul.py +++ b/tests/series/arithmetic/bool/test_mul.py @@ -1,31 +1,60 @@ +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 +import pytest +from typing_extensions import ( + Never, + assert_type, +) + +from tests import ( + PD_LTE_23, + TYPE_CHECKING_INVALID_USAGE, + check, +) -from tests import check -left = pd.Series([True, True, False]) # left operand +@pytest.fixture +def left() -> "pd.Series[bool]": + """left operand""" + lo = pd.Series([True, True, False]) + return check(assert_type(lo, "pd.Series[bool]"), pd.Series, np.bool_) -def test_mul_py_scalar() -> None: +def test_mul_py_scalar(left: "pd.Series[bool]") -> None: """Test pd.Series[bool] * Python native scalars""" b, i, f, c = True, 1, 1.0, 1j + s, d = datetime(2025, 10, 1), timedelta(seconds=1) check(assert_type(left * b, "pd.Series[bool]"), pd.Series, np.bool_) 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: + _04 = 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[bool]"), pd.Series, np.bool_) 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: + _14 = 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[bool]"), pd.Series, np.bool_) 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: + 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[bool]"), pd.Series, np.bool_) check(assert_type(left.rmul(i), "pd.Series[int]"), pd.Series, np.integer) @@ -33,26 +62,49 @@ def test_mul_py_scalar() -> None: check( assert_type(left.rmul(c), "pd.Series[complex]"), pd.Series, np.complexfloating ) + if TYPE_CHECKING_INVALID_USAGE: + 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: +def test_mul_py_sequence(left: "pd.Series[bool]") -> None: """Test pd.Series[bool] * 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, 10, d) for d in (1, 2, 3)] + d = [timedelta(seconds=s + 1) for s in range(3)] check(assert_type(left * b, "pd.Series[bool]"), pd.Series, np.bool_) 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: + _04 = left * s # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + if PD_LTE_23: + # pandas-dev/pandas#62316: both timedelta * bool + # and np.timedelta64 * bool works, so pandas probably also should work + check(assert_type(left * d, "pd.Series[pd.Timedelta]"), pd.Series, timedelta) check(assert_type(b * left, "pd.Series[bool]"), pd.Series, np.bool_) 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: + _14 = s * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + if PD_LTE_23: + # pandas-dev/pandas#62316: both timedelta * bool + # and np.timedelta64 * bool works, so pandas probably also should work + check(assert_type(d * left, "pd.Series[pd.Timedelta]"), pd.Series, timedelta) check(assert_type(left.mul(b), "pd.Series[bool]"), pd.Series, np.bool_) 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: + left.mul(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + if PD_LTE_23: + # pandas-dev/pandas#62316: both timedelta * bool + # and np.timedelta64 * bool works, so pandas probably also should work + check(assert_type(left.mul(d), "pd.Series[pd.Timedelta]"), pd.Series, timedelta) check(assert_type(left.rmul(b), "pd.Series[bool]"), pd.Series, np.bool_) check(assert_type(left.rmul(i), "pd.Series[int]"), pd.Series, np.integer) @@ -60,19 +112,35 @@ def test_mul_py_sequence() -> None: check( assert_type(left.rmul(c), "pd.Series[complex]"), pd.Series, np.complexfloating ) + if TYPE_CHECKING_INVALID_USAGE: + left.rmul(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + if PD_LTE_23: + # pandas-dev/pandas#62316: both timedelta * bool + # and np.timedelta64 * bool works, so pandas probably also should work + check( + assert_type(left.rmul(d), "pd.Series[pd.Timedelta]"), pd.Series, timedelta + ) -def test_mul_numpy_array() -> None: +def test_mul_numpy_array(left: "pd.Series[bool]") -> None: """Test pd.Series[bool] * 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-10-{d:02d}") for d in (1, 2, 3)], np.datetime64) + d = np.array([np.timedelta64(s + 1, "s") for s in range(3)], np.timedelta64) check(assert_type(left * b, "pd.Series[bool]"), pd.Series, np.bool_) 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) + if PD_LTE_23: + # pandas-dev/pandas#62316: both timedelta * bool + # and np.timedelta64 * bool works, so pandas probably also should work + 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 +153,29 @@ def test_mul_numpy_array() -> None: pd.Series, np.complexfloating, ) + if TYPE_CHECKING_INVALID_USAGE: + assert_type(s * left, Any) + if PD_LTE_23: + # pandas-dev/pandas#62316: both timedelta * bool + # and np.timedelta64 * bool works, so pandas probably also should work + check( + assert_type(d * left, "npt.NDArray[np.timedelta64]"), + pd.Series, + pd.Timedelta, + ) check(assert_type(left.mul(b), "pd.Series[bool]"), pd.Series, np.bool_) 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: + left.mul(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + if PD_LTE_23: + # pandas-dev/pandas#62316: both timedelta * bool + # and np.timedelta64 * bool works, so pandas probably also should work + check( + assert_type(left.mul(d), "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta + ) check(assert_type(left.rmul(b), "pd.Series[bool]"), pd.Series, np.bool_) check(assert_type(left.rmul(i), "pd.Series[int]"), pd.Series, np.integer) @@ -97,29 +183,59 @@ def test_mul_numpy_array() -> None: check( assert_type(left.rmul(c), "pd.Series[complex]"), pd.Series, np.complexfloating ) - - -def test_mul_pd_index() -> None: + if TYPE_CHECKING_INVALID_USAGE: + left.rmul(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + if PD_LTE_23: + # pandas-dev/pandas#62316: both timedelta * bool + # and np.timedelta64 * bool works, so pandas probably also should work + check( + assert_type(left.rmul(d), "pd.Series[pd.Timedelta]"), + pd.Series, + pd.Timedelta, + ) + + +def test_mul_pd_index(left: "pd.Series[bool]") -> None: """Test pd.Series[bool] * 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, 10, d) for d in (1, 2, 3)]) + d = pd.Index([timedelta(seconds=s + 1) for s in range(3)]) check(assert_type(left * b, "pd.Series[bool]"), pd.Series, np.bool_) 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: + _04 = left * s # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + if PD_LTE_23: + # pandas-dev/pandas#62316: both timedelta * bool + # and np.timedelta64 * bool works, so pandas probably also should work + check(assert_type(left * d, "pd.Series[pd.Timedelta]"), pd.Series, timedelta) check(assert_type(b * left, "pd.Series[bool]"), pd.Series, np.bool_) 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: + _14 = s * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + if PD_LTE_23: + # pandas-dev/pandas#62316: both timedelta * bool + # and np.timedelta64 * bool works, so pandas probably also should work + check(assert_type(d * left, "pd.Series[pd.Timedelta]"), pd.Series, timedelta) check(assert_type(left.mul(b), "pd.Series[bool]"), pd.Series, np.bool_) 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: + left.mul(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + if PD_LTE_23: + # pandas-dev/pandas#62316: both timedelta * bool + # and np.timedelta64 * bool works, so pandas probably also should work + check(assert_type(left.mul(d), "pd.Series[pd.Timedelta]"), pd.Series, timedelta) check(assert_type(left.rmul(b), "pd.Series[bool]"), pd.Series, np.bool_) check(assert_type(left.rmul(i), "pd.Series[int]"), pd.Series, np.integer) @@ -127,29 +243,57 @@ def test_mul_pd_index() -> None: check( assert_type(left.rmul(c), "pd.Series[complex]"), pd.Series, np.complexfloating ) + if TYPE_CHECKING_INVALID_USAGE: + left.rmul(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + if PD_LTE_23: + # pandas-dev/pandas#62316: both timedelta * bool + # and np.timedelta64 * bool works, so pandas probably also should work + check( + assert_type(left.rmul(d), "pd.Series[pd.Timedelta]"), pd.Series, timedelta + ) -def test_mul_pd_series() -> None: +def test_mul_pd_series(left: "pd.Series[bool]") -> None: """Test pd.Series[bool] * pandas Series""" b = pd.Series([True, False, True]) 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, 10, d) for d in (1, 2, 3)]) + d = pd.Series([timedelta(seconds=s + 1) for s in range(3)]) check(assert_type(left * b, "pd.Series[bool]"), pd.Series, np.bool_) 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: + _04 = left * s # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + if PD_LTE_23: + # pandas-dev/pandas#62316: both timedelta * bool + # and np.timedelta64 * bool works, so pandas probably also should work + check(assert_type(left * d, "pd.Series[pd.Timedelta]"), pd.Series, timedelta) check(assert_type(b * left, "pd.Series[bool]"), pd.Series, np.bool_) 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: + _14 = s * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + if PD_LTE_23: + # pandas-dev/pandas#62316: both timedelta * bool + # and np.timedelta64 * bool works, so pandas probably also should work + check(assert_type(d * left, "pd.Series[pd.Timedelta]"), pd.Series, timedelta) check(assert_type(left.mul(b), "pd.Series[bool]"), pd.Series, np.bool_) 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: + left.mul(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + if PD_LTE_23: + # pandas-dev/pandas#62316: both timedelta * bool + # and np.timedelta64 * bool works, so pandas probably also should work + check(assert_type(left.mul(d), "pd.Series[pd.Timedelta]"), pd.Series, timedelta) check(assert_type(left.rmul(b), "pd.Series[bool]"), pd.Series, np.bool_) check(assert_type(left.rmul(i), "pd.Series[int]"), pd.Series, np.integer) @@ -157,3 +301,11 @@ def test_mul_pd_series() -> None: check( assert_type(left.rmul(c), "pd.Series[complex]"), pd.Series, np.complexfloating ) + if TYPE_CHECKING_INVALID_USAGE: + left.rmul(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + if PD_LTE_23: + # pandas-dev/pandas#62316: both timedelta * bool + # and np.timedelta64 * bool works, so pandas probably also should work + check( + assert_type(left.rmul(d), "pd.Series[pd.Timedelta]"), pd.Series, timedelta + ) diff --git a/tests/series/arithmetic/complex/test_mul.py b/tests/series/arithmetic/complex/test_mul.py index e36a822c6..b997b4d3b 100644 --- a/tests/series/arithmetic/complex/test_mul.py +++ b/tests/series/arithmetic/complex/test_mul.py @@ -1,31 +1,59 @@ +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 +import pytest +from typing_extensions import ( + Never, + assert_type, +) + +from tests import ( + TYPE_CHECKING_INVALID_USAGE, + check, +) -from tests import check -left = pd.Series([1j, 2j, 3j]) # left operand +@pytest.fixture +def left() -> "pd.Series[complex]": + """left operand""" + lo = pd.Series([1j, 2j, 3j]) + return check(assert_type(lo, "pd.Series[complex]"), pd.Series, np.complexfloating) -def test_mul_py_scalar() -> None: +def test_mul_py_scalar(left: "pd.Series[complex]") -> None: """Test pd.Series[complex] * 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[complex]"), pd.Series, np.complexfloating) check(assert_type(left * i, "pd.Series[complex]"), pd.Series, np.complexfloating) check(assert_type(left * f, "pd.Series[complex]"), pd.Series, np.complexfloating) check(assert_type(left * c, "pd.Series[complex]"), pd.Series, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _04 = left * s # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _05 = left * d # type: ignore[operator] # pyright: ignore[reportOperatorIssue] check(assert_type(b * left, "pd.Series[complex]"), pd.Series, np.complexfloating) check(assert_type(i * left, "pd.Series[complex]"), pd.Series, np.complexfloating) check(assert_type(f * left, "pd.Series[complex]"), pd.Series, np.complexfloating) check(assert_type(c * left, "pd.Series[complex]"), pd.Series, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _14 = s * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _15 = d * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] check(assert_type(left.mul(b), "pd.Series[complex]"), pd.Series, np.complexfloating) check(assert_type(left.mul(i), "pd.Series[complex]"), pd.Series, np.complexfloating) check(assert_type(left.mul(f), "pd.Series[complex]"), pd.Series, np.complexfloating) check(assert_type(left.mul(c), "pd.Series[complex]"), pd.Series, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + left.mul(s) # type: ignore[call-overload] # pyright: ignore[reportArgumentType,reportCallIssue] + left.mul(d) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] check( assert_type(left.rmul(b), "pd.Series[complex]"), pd.Series, np.complexfloating @@ -39,26 +67,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: + left.rmul(s) # type: ignore[call-overload] # pyright: ignore[reportArgumentType,reportCallIssue] + left.rmul(d) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] -def test_mul_py_sequence() -> None: +def test_mul_py_sequence(left: "pd.Series[complex]") -> None: """Test pd.Series[complex] * 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[complex]"), pd.Series, np.complexfloating) check(assert_type(left * i, "pd.Series[complex]"), pd.Series, np.complexfloating) check(assert_type(left * f, "pd.Series[complex]"), pd.Series, np.complexfloating) check(assert_type(left * c, "pd.Series[complex]"), pd.Series, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _04 = left * s # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _05 = left * d # type: ignore[operator] # pyright: ignore[reportOperatorIssue] check(assert_type(b * left, "pd.Series[complex]"), pd.Series, np.complexfloating) check(assert_type(i * left, "pd.Series[complex]"), pd.Series, np.complexfloating) check(assert_type(f * left, "pd.Series[complex]"), pd.Series, np.complexfloating) check(assert_type(c * left, "pd.Series[complex]"), pd.Series, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _14 = s * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _15 = d * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] check(assert_type(left.mul(b), "pd.Series[complex]"), pd.Series, np.complexfloating) check(assert_type(left.mul(i), "pd.Series[complex]"), pd.Series, np.complexfloating) check(assert_type(left.mul(f), "pd.Series[complex]"), pd.Series, np.complexfloating) check(assert_type(left.mul(c), "pd.Series[complex]"), pd.Series, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + left.mul(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + left.mul(d) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] check( assert_type(left.rmul(b), "pd.Series[complex]"), pd.Series, np.complexfloating @@ -72,19 +114,27 @@ def test_mul_py_sequence() -> None: check( assert_type(left.rmul(c), "pd.Series[complex]"), pd.Series, np.complexfloating ) + if TYPE_CHECKING_INVALID_USAGE: + left.rmul(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + left.rmul(d) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] -def test_mul_numpy_array() -> None: +def test_mul_numpy_array(left: "pd.Series[complex]") -> None: """Test pd.Series[complex] * 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.Series[complex]"), pd.Series, np.complexfloating) check(assert_type(left * i, "pd.Series[complex]"), pd.Series, np.complexfloating) check(assert_type(left * f, "pd.Series[complex]"), pd.Series, np.complexfloating) check(assert_type(left * c, "pd.Series[complex]"), pd.Series, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + assert_type(left * s, Never) + assert_type(left * d, Never) # `numpy` typing gives the corresponding `ndarray`s in the static type # checking, where our `__rmul__` cannot override. At runtime, they return @@ -99,11 +149,17 @@ def test_mul_numpy_array() -> None: pd.Series, np.complexfloating, ) + if TYPE_CHECKING_INVALID_USAGE: + assert_type(s * left, Any) + assert_type(d * left, "npt.NDArray[np.timedelta64]") check(assert_type(left.mul(b), "pd.Series[complex]"), pd.Series, np.complexfloating) check(assert_type(left.mul(i), "pd.Series[complex]"), pd.Series, np.complexfloating) check(assert_type(left.mul(f), "pd.Series[complex]"), pd.Series, np.complexfloating) check(assert_type(left.mul(c), "pd.Series[complex]"), pd.Series, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + left.mul(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + left.mul(d) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] check( assert_type(left.rmul(b), "pd.Series[complex]"), pd.Series, np.complexfloating @@ -117,29 +173,43 @@ def test_mul_numpy_array() -> None: check( assert_type(left.rmul(c), "pd.Series[complex]"), pd.Series, np.complexfloating ) + if TYPE_CHECKING_INVALID_USAGE: + left.rmul(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + left.rmul(d) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] -def test_mul_pd_index() -> None: +def test_mul_pd_index(left: "pd.Series[complex]") -> None: """Test pd.Series[complex] * 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.Series[complex]"), pd.Series, np.complexfloating) check(assert_type(left * i, "pd.Series[complex]"), pd.Series, np.complexfloating) check(assert_type(left * f, "pd.Series[complex]"), pd.Series, np.complexfloating) check(assert_type(left * c, "pd.Series[complex]"), pd.Series, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _04 = left * s # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _05 = left * d # type: ignore[operator] # pyright: ignore[reportOperatorIssue] check(assert_type(b * left, "pd.Series[complex]"), pd.Series, np.complexfloating) check(assert_type(i * left, "pd.Series[complex]"), pd.Series, np.complexfloating) check(assert_type(f * left, "pd.Series[complex]"), pd.Series, np.complexfloating) check(assert_type(c * left, "pd.Series[complex]"), pd.Series, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _14 = s * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _15 = d * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] check(assert_type(left.mul(b), "pd.Series[complex]"), pd.Series, np.complexfloating) check(assert_type(left.mul(i), "pd.Series[complex]"), pd.Series, np.complexfloating) check(assert_type(left.mul(f), "pd.Series[complex]"), pd.Series, np.complexfloating) check(assert_type(left.mul(c), "pd.Series[complex]"), pd.Series, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + left.mul(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + left.mul(d) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] check( assert_type(left.rmul(b), "pd.Series[complex]"), pd.Series, np.complexfloating @@ -153,29 +223,43 @@ def test_mul_pd_index() -> None: check( assert_type(left.rmul(c), "pd.Series[complex]"), pd.Series, np.complexfloating ) + if TYPE_CHECKING_INVALID_USAGE: + left.rmul(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + left.rmul(d) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] -def test_mul_pd_series() -> None: +def test_mul_pd_series(left: "pd.Series[complex]") -> None: """Test pd.Series[complex] * pandas Series""" b = pd.Series([True, False, True]) 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[complex]"), pd.Series, np.complexfloating) check(assert_type(left * i, "pd.Series[complex]"), pd.Series, np.complexfloating) check(assert_type(left * f, "pd.Series[complex]"), pd.Series, np.complexfloating) check(assert_type(left * c, "pd.Series[complex]"), pd.Series, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _04 = left * s # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _05 = left * d # type: ignore[operator] # pyright: ignore[reportOperatorIssue] check(assert_type(b * left, "pd.Series[complex]"), pd.Series, np.complexfloating) check(assert_type(i * left, "pd.Series[complex]"), pd.Series, np.complexfloating) check(assert_type(f * left, "pd.Series[complex]"), pd.Series, np.complexfloating) check(assert_type(c * left, "pd.Series[complex]"), pd.Series, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + _14 = s * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _15 = d * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] check(assert_type(left.mul(b), "pd.Series[complex]"), pd.Series, np.complexfloating) check(assert_type(left.mul(i), "pd.Series[complex]"), pd.Series, np.complexfloating) check(assert_type(left.mul(f), "pd.Series[complex]"), pd.Series, np.complexfloating) check(assert_type(left.mul(c), "pd.Series[complex]"), pd.Series, np.complexfloating) + if TYPE_CHECKING_INVALID_USAGE: + left.mul(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + left.mul(d) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] check( assert_type(left.rmul(b), "pd.Series[complex]"), pd.Series, np.complexfloating @@ -189,3 +273,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: + left.rmul(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + left.rmul(d) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] diff --git a/tests/series/arithmetic/float/test_mul.py b/tests/series/arithmetic/float/test_mul.py index f4afe08d2..ae320658b 100644 --- a/tests/series/arithmetic/float/test_mul.py +++ b/tests/series/arithmetic/float/test_mul.py @@ -1,31 +1,59 @@ +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 +import pytest +from typing_extensions import ( + Never, + assert_type, +) + +from tests import ( + TYPE_CHECKING_INVALID_USAGE, + check, +) -from tests import check -left = pd.Series([1.0, 2.0, 3.0]) # left operand +@pytest.fixture +def left() -> "pd.Series[float]": + """left operand""" + lo = pd.Series([1.0, 2.0, 3.0]) + return check(assert_type(lo, "pd.Series[float]"), pd.Series, np.floating) -def test_mul_py_scalar() -> None: +def test_mul_py_scalar(left: "pd.Series[float]") -> 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: + _04 = 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: + _14 = 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: + 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 +61,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: + 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: +def test_mul_py_sequence(left: "pd.Series[float]") -> 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: + _04 = 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: + _14 = 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: + 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,19 +102,27 @@ def test_mul_py_sequence() -> None: check( assert_type(left.rmul(c), "pd.Series[complex]"), pd.Series, np.complexfloating ) + if TYPE_CHECKING_INVALID_USAGE: + 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: +def test_mul_numpy_array(left: "pd.Series[float]") -> None: """Test pd.Series[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.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 +135,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: + 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,29 +153,43 @@ def test_mul_numpy_array() -> None: check( assert_type(left.rmul(c), "pd.Series[complex]"), pd.Series, np.complexfloating ) + if TYPE_CHECKING_INVALID_USAGE: + 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: +def test_mul_pd_index(left: "pd.Series[float]") -> None: """Test pd.Series[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.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: + _04 = 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: + _14 = 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: + 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,29 +197,43 @@ def test_mul_pd_index() -> None: check( assert_type(left.rmul(c), "pd.Series[complex]"), pd.Series, np.complexfloating ) + if TYPE_CHECKING_INVALID_USAGE: + 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: +def test_mul_pd_series(left: "pd.Series[float]") -> None: """Test pd.Series[float] * pandas Series""" b = pd.Series([True, False, True]) 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: + _04 = 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: + _14 = 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: + 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 +241,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: + 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..030adfc98 100644 --- a/tests/series/arithmetic/int/test_mul.py +++ b/tests/series/arithmetic/int/test_mul.py @@ -1,31 +1,59 @@ +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 +import pytest +from typing_extensions import ( + Never, + assert_type, +) + +from tests import ( + TYPE_CHECKING_INVALID_USAGE, + check, +) -from tests import check -left = pd.Series([1, 2, 3]) # left operand +@pytest.fixture +def left() -> "pd.Series[int]": + """left operand""" + lo = pd.Series([1, 2, 3]) + return check(assert_type(lo, "pd.Series[int]"), pd.Series, np.integer) -def test_mul_py_scalar() -> None: +def test_mul_py_scalar(left: "pd.Series[int]") -> 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: + _04 = 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: + _14 = 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: + 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 +61,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: + 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: +def test_mul_py_sequence(left: "pd.Series[int]") -> 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: + _04 = 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: + _14 = 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: + 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,19 +102,27 @@ def test_mul_py_sequence() -> None: check( assert_type(left.rmul(c), "pd.Series[complex]"), pd.Series, np.complexfloating ) + if TYPE_CHECKING_INVALID_USAGE: + 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: +def test_mul_numpy_array(left: "pd.Series[int]") -> None: """Test pd.Series[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.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 +135,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: + 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,29 +153,43 @@ def test_mul_numpy_array() -> None: check( assert_type(left.rmul(c), "pd.Series[complex]"), pd.Series, np.complexfloating ) + if TYPE_CHECKING_INVALID_USAGE: + 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: +def test_mul_pd_index(left: "pd.Series[int]") -> None: """Test pd.Series[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.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: + _04 = 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: + _14 = 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: + 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,29 +197,43 @@ def test_mul_pd_index() -> None: check( assert_type(left.rmul(c), "pd.Series[complex]"), pd.Series, np.complexfloating ) + if TYPE_CHECKING_INVALID_USAGE: + 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: +def test_mul_pd_series(left: "pd.Series[int]") -> None: """Test pd.Series[int] * pandas Series""" b = pd.Series([True, False, True]) 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: + _04 = 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: + _14 = 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: + 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 +241,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: + 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/str/test_mul.py b/tests/series/arithmetic/str/test_mul.py new file mode 100644 index 000000000..2d7d60efc --- /dev/null +++ b/tests/series/arithmetic/str/test_mul.py @@ -0,0 +1,263 @@ +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 +import pytest +from typing_extensions import ( + Never, + assert_type, +) + +from tests import ( + PD_LTE_23, + TYPE_CHECKING_INVALID_USAGE, + check, +) + + +@pytest.fixture +def left() -> "pd.Series[str]": + """left operand""" + lo = pd.Series(["1", "2", "3"]) + return check(assert_type(lo, "pd.Series[str]"), pd.Series, str) + + +def test_mul_py_scalar(left: "pd.Series[str]") -> None: + """Test pd.Series[str] * Python native scalars""" + b, i, f, c = True, 1, 1.0, 1j + s, d = datetime(2025, 9, 27), timedelta(seconds=1) + + # pandas-dev/pandas#62595: we may want to support Series[str] * bool + # also in 3.x + if PD_LTE_23: + check(assert_type(left * b, "pd.Series[str]"), pd.Series, str) + check(assert_type(left * i, "pd.Series[str]"), pd.Series, str) + if TYPE_CHECKING_INVALID_USAGE: + _02 = left * f # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _03 = left * c # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _04 = left * s # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _05 = left * d # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + + if PD_LTE_23: + check(assert_type(b * left, "pd.Series[str]"), pd.Series, str) + check(assert_type(i * left, "pd.Series[str]"), pd.Series, str) + if TYPE_CHECKING_INVALID_USAGE: + _12 = f * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _13 = c * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _14 = s * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _15 = d * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + + if PD_LTE_23: + check(assert_type(left.mul(b), "pd.Series[str]"), pd.Series, str) + check(assert_type(left.mul(i), "pd.Series[str]"), pd.Series, str) + if TYPE_CHECKING_INVALID_USAGE: + left.mul(f) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + left.mul(c) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + left.mul(s) # type: ignore[call-overload] # pyright: ignore[reportArgumentType,reportCallIssue] + left.mul(d) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + + if PD_LTE_23: + check(assert_type(left.rmul(b), "pd.Series[str]"), pd.Series, str) + check(assert_type(left.rmul(i), "pd.Series[str]"), pd.Series, str) + if TYPE_CHECKING_INVALID_USAGE: + left.rmul(f) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + left.rmul(c) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + left.rmul(s) # type: ignore[call-overload] # pyright: ignore[reportArgumentType,reportCallIssue] + left.rmul(d) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + + +def test_mul_py_sequence(left: "pd.Series[str]") -> None: + """Test pd.Series[str] * 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)] + + # pandas-dev/pandas#62595: we may want to support Series[str] * bool + # also in 3.x + if PD_LTE_23: + check(assert_type(left * b, "pd.Series[str]"), pd.Series, str) + check(assert_type(left * i, "pd.Series[str]"), pd.Series, str) + if TYPE_CHECKING_INVALID_USAGE: + _02 = left * f # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _03 = left * c # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _04 = left * s # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _05 = left * d # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + + if PD_LTE_23: + check(assert_type(b * left, "pd.Series[str]"), pd.Series, str) + check(assert_type(i * left, "pd.Series[str]"), pd.Series, str) + if TYPE_CHECKING_INVALID_USAGE: + _12 = f * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _13 = c * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _14 = s * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _15 = d * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + + if PD_LTE_23: + check(assert_type(left.mul(b), "pd.Series[str]"), pd.Series, str) + check(assert_type(left.mul(i), "pd.Series[str]"), pd.Series, str) + if TYPE_CHECKING_INVALID_USAGE: + left.mul(f) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + left.mul(c) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + left.mul(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + left.mul(d) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + + if PD_LTE_23: + check(assert_type(left.rmul(b), "pd.Series[str]"), pd.Series, str) + check(assert_type(left.rmul(i), "pd.Series[str]"), pd.Series, str) + if TYPE_CHECKING_INVALID_USAGE: + left.rmul(f) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + left.rmul(c) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + left.rmul(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + left.rmul(d) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + + +def test_mul_numpy_array(left: "pd.Series[str]") -> None: + """Test pd.Series[str] * 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) + + # pandas-dev/pandas#62595: we may want to support Series[str] * bool + # also in 3.x + if PD_LTE_23: + check(assert_type(left * b, "pd.Series[str]"), pd.Series, str) + check(assert_type(left * i, "pd.Series[str]"), pd.Series, str) + if TYPE_CHECKING_INVALID_USAGE: + assert_type(left * f, Never) + assert_type(left * c, Never) + assert_type(left * s, Never) + assert_type(left * d, Never) + + # `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. + if PD_LTE_23: + check(assert_type(b * left, "npt.NDArray[np.bool_]"), pd.Series, str) + check(assert_type(i * left, "npt.NDArray[np.int64]"), pd.Series, str) + if TYPE_CHECKING_INVALID_USAGE: + assert_type(f * left, "npt.NDArray[np.float64]") + assert_type(c * left, "npt.NDArray[np.complex128]") + assert_type(s * left, Any) + assert_type(d * left, "npt.NDArray[np.timedelta64]") + + if PD_LTE_23: + check(assert_type(left.mul(b), "pd.Series[str]"), pd.Series, str) + check(assert_type(left.mul(i), "pd.Series[str]"), pd.Series, str) + if TYPE_CHECKING_INVALID_USAGE: + left.mul(f) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + left.mul(c) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + left.mul(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + left.mul(d) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + + if PD_LTE_23: + check(assert_type(left.rmul(b), "pd.Series[str]"), pd.Series, str) + check(assert_type(left.rmul(i), "pd.Series[str]"), pd.Series, str) + if TYPE_CHECKING_INVALID_USAGE: + left.rmul(f) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + left.rmul(c) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + left.rmul(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + left.rmul(d) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + + +def test_mul_pd_index(left: "pd.Series[str]") -> None: + """Test pd.Series[str] * 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)]) + + # pandas-dev/pandas#62595: we may want to support Series[str] * bool + # also in 3.x + if TYPE_CHECKING_INVALID_USAGE: + check(assert_type(left * b, "pd.Series[str]"), pd.Series, str) + check(assert_type(left * i, "pd.Series[str]"), pd.Series, str) + if TYPE_CHECKING_INVALID_USAGE: + _02 = left * f # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _03 = left * c # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _04 = left * s # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _05 = left * d # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + + if TYPE_CHECKING_INVALID_USAGE: + check(assert_type(b * left, "pd.Series[str]"), pd.Series, str) + check(assert_type(i * left, "pd.Series[str]"), pd.Series, str) + if TYPE_CHECKING_INVALID_USAGE: + _12 = f * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _13 = c * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _14 = s * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _15 = d * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + + if TYPE_CHECKING_INVALID_USAGE: + check(assert_type(left.mul(b), "pd.Series[str]"), pd.Series, str) + check(assert_type(left.mul(i), "pd.Series[str]"), pd.Series, str) + if TYPE_CHECKING_INVALID_USAGE: + left.mul(f) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + left.mul(c) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + left.mul(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + left.mul(d) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + + if PD_LTE_23: + check(assert_type(left.rmul(b), "pd.Series[str]"), pd.Series, str) + check(assert_type(left.rmul(i), "pd.Series[str]"), pd.Series, str) + if TYPE_CHECKING_INVALID_USAGE: + left.rmul(f) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + left.rmul(c) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + left.rmul(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + left.rmul(d) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + + +def test_mul_pd_series(left: "pd.Series[str]") -> None: + """Test pd.Series[str] * pandas Series""" + b = pd.Series([True, False, True]) + 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)]) + + # pandas-dev/pandas#62595: we may want to support Series[str] * bool + # also in 3.x + if PD_LTE_23: + check(assert_type(left * b, "pd.Series[str]"), pd.Series, str) + check(assert_type(left * i, "pd.Series[str]"), pd.Series, str) + if TYPE_CHECKING_INVALID_USAGE: + _02 = left * f # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _03 = left * c # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _04 = left * s # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _05 = left * d # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + + if PD_LTE_23: + check(assert_type(b * left, "pd.Series[str]"), pd.Series, str) + check(assert_type(i * left, "pd.Series[str]"), pd.Series, str) + if TYPE_CHECKING_INVALID_USAGE: + _12 = f * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _13 = c * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _14 = s * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _15 = d * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + + if PD_LTE_23: + check(assert_type(left.mul(b), "pd.Series[str]"), pd.Series, str) + check(assert_type(left.mul(i), "pd.Series[str]"), pd.Series, str) + if TYPE_CHECKING_INVALID_USAGE: + left.mul(f) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + left.mul(c) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + left.mul(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + left.mul(d) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + + if PD_LTE_23: + check(assert_type(left.rmul(b), "pd.Series[str]"), pd.Series, str) + check(assert_type(left.rmul(i), "pd.Series[str]"), pd.Series, str) + if TYPE_CHECKING_INVALID_USAGE: + left.rmul(f) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + left.rmul(c) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + left.rmul(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] + left.rmul(d) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] diff --git a/tests/series/arithmetic/test_mul.py b/tests/series/arithmetic/test_mul.py index 65ab42f0d..4d1b3aaa3 100644 --- a/tests/series/arithmetic/test_mul.py +++ b/tests/series/arithmetic/test_mul.py @@ -2,6 +2,7 @@ import numpy as np import pandas as pd +import pytest from typing_extensions import assert_type from tests import ( @@ -9,10 +10,15 @@ check, ) -left_i = pd.DataFrame({"a": [1, 2, 3]})["a"] # left operand +@pytest.fixture +def left_i() -> pd.Series: + """left operand""" + lo = pd.DataFrame({"a": [1, 2, 3]})["a"] + return check(assert_type(lo, pd.Series), pd.Series) -def test_mul_py_scalar() -> None: + +def test_mul_py_scalar(left_i: pd.Series) -> None: """Test pd.Series[Any] (int) * Python native scalars""" b, i, f, c = True, 1, 1.0, 1j @@ -37,7 +43,7 @@ def test_mul_py_scalar() -> None: check(assert_type(left_i.rmul(c), pd.Series), pd.Series) -def test_mul_py_sequence() -> None: +def test_mul_py_sequence(left_i: pd.Series) -> None: """Test pd.Series[Any] (int) * Python native sequences""" b, i, f, c = [True, False, True], [2, 3, 5], [1.0, 2.0, 3.0], [1j, 1j, 4j] @@ -62,7 +68,7 @@ def test_mul_py_sequence() -> None: check(assert_type(left_i.rmul(c), pd.Series), pd.Series) -def test_mul_numpy_array() -> None: +def test_mul_numpy_array(left_i: pd.Series) -> None: """Test pd.Series[Any] (int) * numpy arrays""" b = np.array([True, False, True], np.bool_) i = np.array([2, 3, 5], np.int64) @@ -106,7 +112,7 @@ def test_mul_numpy_array() -> None: check(assert_type(left_i.rmul(c), pd.Series), pd.Series) -def test_mul_pd_index() -> None: +def test_mul_pd_index(left_i: pd.Series) -> None: """Test pd.Series[Any] (int) * pandas Indexes""" a = pd.MultiIndex.from_tuples([(1,), (2,), (3,)]).levels[0] b = pd.Index([True, False, True]) @@ -139,7 +145,7 @@ def test_mul_pd_index() -> None: check(assert_type(left_i.rmul(c), pd.Series), pd.Series) -def test_mul_pd_series() -> None: +def test_mul_pd_series(left_i: pd.Series) -> None: """Test pd.Series[Any] (int) * pandas Series""" a = pd.DataFrame({"a": [1, 2, 3]})["a"] b = pd.Series([True, False, True]) @@ -172,12 +178,12 @@ def test_mul_pd_series() -> None: check(assert_type(left_i.rmul(c), pd.Series), pd.Series) -def test_mul_str_py_str() -> None: +def test_mul_str_py_str(left_i: pd.Series) -> None: """Test pd.Series[Any] (int) * Python str""" s = "abc" if TYPE_CHECKING_INVALID_USAGE: _0 = left_i * s # type: ignore[operator] # pyright:ignore[reportOperatorIssue] _1 = 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..5eaf68ffd 100644 --- a/tests/series/arithmetic/timedelta/test_mul.py +++ b/tests/series/arithmetic/timedelta/test_mul.py @@ -1,6 +1,7 @@ import numpy as np from numpy import typing as npt # noqa: F401 import pandas as pd +import pytest from typing_extensions import ( Never, assert_type, @@ -12,10 +13,15 @@ check, ) -left = pd.Series([pd.Timedelta(1, "s")]) # left operand +@pytest.fixture +def left() -> "pd.Series[pd.Timedelta]": + """left operand""" + lo = pd.Series([pd.Timedelta(1, "s")]) # left operand + return check(assert_type(lo, "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) -def test_mul_py_scalar() -> None: + +def test_mul_py_scalar(left: "pd.Series[pd.Timedelta]") -> None: """Test pd.Series[pd.Timedelta] * Python native scalars""" b, i, f, c = True, 1, 1.0, 1j @@ -41,7 +47,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,10 +58,10 @@ 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: +def test_mul_py_sequence(left: "pd.Series[pd.Timedelta]") -> None: """Test pd.Series[pd.Timedelta] * Python native sequences""" b, i, f, c = [True], [2], [1.5], [1.7j] @@ -95,7 +101,7 @@ def test_mul_py_sequence() -> None: left.rmul(c) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] -def test_mul_numpy_array() -> None: +def test_mul_numpy_array(left: "pd.Series[pd.Timedelta]") -> None: """Test pd.Series[pd.Timedelta] * numpy arrays""" b = np.array([True], np.bool_) i = np.array([2], np.int64) @@ -142,7 +148,7 @@ def test_mul_numpy_array() -> None: left.rmul(c) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] -def test_mul_pd_index() -> None: +def test_mul_pd_index(left: "pd.Series[pd.Timedelta]") -> None: """Test pd.Series[pd.Timedelta] * pandas Indexes""" b = pd.Index([True]) i = pd.Index([2]) @@ -185,7 +191,7 @@ def test_mul_pd_index() -> None: left.rmul(c) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] -def test_mul_pd_series() -> None: +def test_mul_pd_series(left: "pd.Series[pd.Timedelta]") -> None: """Test pd.Series[pd.Timedelta] * pandas Series""" b = pd.Series([True]) i = pd.Series([2]) diff --git a/tests/series/test_properties.py b/tests/series/test_properties.py index ecba403d5..21ce75931 100644 --- a/tests/series/test_properties.py +++ b/tests/series/test_properties.py @@ -1,6 +1,4 @@ -from typing import ( - TYPE_CHECKING, -) +from typing import TYPE_CHECKING import numpy as np from pandas.core.arrays import DatetimeArray diff --git a/tests/series/test_series.py b/tests/series/test_series.py index 7345dd275..0a9aa85ba 100644 --- a/tests/series/test_series.py +++ b/tests/series/test_series.py @@ -3285,30 +3285,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( @@ -3509,9 +3485,7 @@ def test_diff() -> None: ) # bool -> Any check( - assert_type( - pd.Series([True, True, False, False, True]).diff(), "pd.Series[Any]" - ), + assert_type(pd.Series([True, True, False, False, True]).diff(), pd.Series), pd.Series, bool, index_to_check_for_type=-1, @@ -3532,7 +3506,7 @@ def test_diff() -> None: ) # Any -> float s_o = s.astype(object) - assert_type(s_o, "pd.Series[Any]") + assert_type(s_o, pd.Series) check(assert_type(s_o.diff(), "pd.Series[float]"), pd.Series, float) # complex -> complex check( @@ -3830,7 +3804,8 @@ def test_info() -> None: check(assert_type(s.info(memory_usage="deep"), None), type(None)) check(assert_type(s.info(memory_usage=None), None), type(None)) check(assert_type(s.info(show_counts=True), None), type(None)) - check(assert_type(s.info(show_counts=False), None), type(None)) + if PD_LTE_23: # pandas-dev/pandas#62590 + check(assert_type(s.info(show_counts=False), None), type(None)) check(assert_type(s.info(show_counts=None), None), type(None)) diff --git a/tests/test_dtypes.py b/tests/test_dtypes.py index dbf3e5fe2..89d6e24ab 100644 --- a/tests/test_dtypes.py +++ b/tests/test_dtypes.py @@ -5,9 +5,7 @@ timedelta, timezone, ) -from typing import ( - Literal, -) +from typing import Literal import numpy as np import pandas as pd diff --git a/tests/test_frame.py b/tests/test_frame.py index 8d2b95135..e504be7d4 100644 --- a/tests/test_frame.py +++ b/tests/test_frame.py @@ -47,9 +47,7 @@ ) import xarray as xr -from pandas._typing import ( - Scalar, -) +from pandas._typing import Scalar from tests import ( PD_LTE_23, diff --git a/tests/test_natype.py b/tests/test_natype.py index bef677d66..7a524bc37 100644 --- a/tests/test_natype.py +++ b/tests/test_natype.py @@ -1,6 +1,4 @@ -from typing import ( - Literal, -) +from typing import Literal import pandas as pd from pandas.api.typing import NAType diff --git a/tests/test_plotting.py b/tests/test_plotting.py index 38649ea50..a27725059 100644 --- a/tests/test_plotting.py +++ b/tests/test_plotting.py @@ -1,8 +1,6 @@ import io import itertools -from typing import ( - Any, -) +from typing import Any from matplotlib.axes import Axes from matplotlib.figure import Figure diff --git a/tests/test_timefuncs.py b/tests/test_timefuncs.py index a654015fb..03115f88a 100644 --- a/tests/test_timefuncs.py +++ b/tests/test_timefuncs.py @@ -1,9 +1,7 @@ from __future__ import annotations import datetime as dt -from typing import ( - TypeAlias, -) +from typing import TypeAlias from dateutil.relativedelta import ( FR, @@ -153,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) @@ -169,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) @@ -197,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) @@ -232,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) @@ -248,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")]), @@ -257,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: @@ -288,15 +254,6 @@ 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)