From be3fdccf171ae5cc293960110ca9ddf1f0b7c341 Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Tue, 14 Oct 2025 15:12:16 +0200 Subject: [PATCH 1/8] refactor: simplify add --- pandas-stubs/_typing.pyi | 7 +- pandas-stubs/core/base.pyi | 57 +++++++++++++- pandas-stubs/core/indexes/base.pyi | 35 ++++----- pandas-stubs/core/series.pyi | 117 ++++++----------------------- 4 files changed, 97 insertions(+), 119 deletions(-) diff --git a/pandas-stubs/_typing.pyi b/pandas-stubs/_typing.pyi index 8392880df..cd28db68e 100644 --- a/pandas-stubs/_typing.pyi +++ b/pandas-stubs/_typing.pyi @@ -872,15 +872,16 @@ SeriesDType: TypeAlias = ( | datetime.timedelta # includes pd.Timedelta ) S1 = TypeVar("S1", bound=SeriesDType, default=Any) +S1_CO = TypeVar("S1_CO", bound=SeriesDType, default=Any, covariant=True) +S1_CT = TypeVar("S1_CT", bound=SeriesDType, default=Any, contravariant=True) S1_CT_NDT = TypeVar( "S1_CT_NDT", bound=SeriesDTypeNoDateTime, default=Any, contravariant=True ) -S1_CO = TypeVar("S1_CO", bound=SeriesDType, default=Any, covariant=True) -S1_CT = TypeVar("S1_CT", bound=SeriesDType, default=Any, contravariant=True) # Like S1, but without `default=Any`. S2 = TypeVar("S2", bound=SeriesDType) -S2_CT = TypeVar("S2_CT", bound=SeriesDType, contravariant=True) +S2_CO = TypeVar("S2_CO", bound=SeriesDType, covariant=True) S2_CO_NSDT = TypeVar("S2_CO_NSDT", bound=SeriesDTypeNoStrDateTime, covariant=True) +S2_CT = TypeVar("S2_CT", bound=SeriesDType, contravariant=True) S3 = TypeVar("S3", bound=SeriesDType) # Constraint, instead of bound diff --git a/pandas-stubs/core/base.pyi b/pandas-stubs/core/base.pyi index a81a07be1..5bd603484 100644 --- a/pandas-stubs/core/base.pyi +++ b/pandas-stubs/core/base.pyi @@ -7,24 +7,26 @@ from typing import ( Any, Generic, Literal, + Protocol, TypeAlias, final, overload, + type_check_only, ) +from _typeshed import _T_contra import numpy as np -from pandas import ( - Index, - Series, -) from pandas.core.arraylike import OpsMixin from pandas.core.arrays import ExtensionArray from pandas.core.arrays.categorical import Categorical from pandas.core.indexes.accessors import ArrayDescriptor +from pandas.core.indexes.base import Index +from pandas.core.series import Series from typing_extensions import Self from pandas._typing import ( S1, + S2, ArrayLike, AxisIndex, DropKeep, @@ -176,3 +178,50 @@ NumListLike: TypeAlias = ( | Sequence[complex] | IndexOpsMixin[complex] ) + +@type_check_only +class ElementOpsMixin(Generic[S2]): + @overload + def _add( + self: ElementOpsMixin[bool], other: bool | np.bool_ + ) -> ElementOpsMixin[bool]: ... + @overload + def _add( + self: ElementOpsMixin[int], other: bool | np.bool_ + ) -> ElementOpsMixin[int]: ... + @overload + def _add( + self: ElementOpsMixin[float], other: int | np.integer + ) -> ElementOpsMixin[float]: ... + @overload + def _add( + self: ElementOpsMixin[complex], other: float | np.floating + ) -> ElementOpsMixin[complex]: ... + @overload + def _add(self: ElementOpsMixin[str], other: str) -> ElementOpsMixin[str]: ... + @overload + def _radd( + self: ElementOpsMixin[bool], other: bool | np.bool_ + ) -> ElementOpsMixin[bool]: ... + @overload + def _radd( + self: ElementOpsMixin[int], other: bool | np.bool_ + ) -> ElementOpsMixin[int]: ... + @overload + def _radd( + self: ElementOpsMixin[float], other: int | np.integer + ) -> ElementOpsMixin[float]: ... + @overload + def _radd( + self: ElementOpsMixin[complex], other: float | np.floating + ) -> ElementOpsMixin[complex]: ... + @overload + def _radd(self: ElementOpsMixin[str], other: str) -> ElementOpsMixin[str]: ... + +@type_check_only +class Supports_ElementAdd(Protocol[_T_contra, S2]): + def _add(self, other: _T_contra, /) -> ElementOpsMixin[S2]: ... + +@type_check_only +class Supports_ElementRAdd(Protocol[_T_contra, S2]): + def _radd(self, other: _T_contra, /) -> ElementOpsMixin[S2]: ... diff --git a/pandas-stubs/core/indexes/base.pyi b/pandas-stubs/core/indexes/base.pyi index deb90f221..cd5e27a0c 100644 --- a/pandas-stubs/core/indexes/base.pyi +++ b/pandas-stubs/core/indexes/base.pyi @@ -24,6 +24,7 @@ from _typeshed import ( SupportsMul, SupportsRAdd, SupportsRMul, + _T_contra, ) import numpy as np from pandas import ( @@ -39,8 +40,11 @@ from pandas import ( TimedeltaIndex, ) from pandas.core.base import ( + ElementOpsMixin, IndexOpsMixin, NumListLike, + Supports_ElementAdd, + Supports_ElementRAdd, _ListLike, ) from pandas.core.indexes.category import CategoricalIndex @@ -57,6 +61,7 @@ from pandas._typing import ( S1, S1_CO, S1_CT, + S2_CO, S2_CO_NSDT, S2_CT, T_COMPLEX, @@ -99,8 +104,8 @@ from pandas._typing import ( class InvalidIndexError(Exception): ... -class Index(IndexOpsMixin[S1]): - __hash__: ClassVar[None] # type: ignore[assignment] +class Index(IndexOpsMixin[S1], ElementOpsMixin[S1]): + __hash__: ClassVar[None] # type: ignore[assignment] # pyright: ignore[reportIncompatibleMethodOverride] # overloads with additional dtypes @overload def __new__( # pyright: ignore[reportOverlappingOverload] @@ -506,15 +511,10 @@ class Index(IndexOpsMixin[S1]): @overload def __add__(self, other: Index[Never]) -> Index: ... @overload - def __add__(self: Index[bool], other: bool | Sequence[bool]) -> Index[bool]: ... - @overload - def __add__(self: Index[int], other: bool | Sequence[bool]) -> Index[int]: ... - @overload - def __add__(self: Index[float], other: int | Sequence[int]) -> Index[float]: ... - @overload def __add__( - self: Index[complex], other: float | Sequence[float] - ) -> Index[complex]: ... + self: Supports_ElementAdd[_T_contra, S2_CO], + other: _T_contra | Sequence[_T_contra], + ) -> Index[S2_CO]: ... @overload def __add__( self: Index[S1_CT], @@ -553,22 +553,17 @@ class Index(IndexOpsMixin[S1]): ) -> Never: ... @overload def __add__( - self: Index[_str], other: _str | Sequence[_str] | np_ndarray_str | Index[_str] + self: Index[_str], other: np_ndarray_str | Index[_str] ) -> Index[_str]: ... @overload def __radd__(self: Index[Never], other: _str) -> Never: ... @overload def __radd__(self: Index[Never], other: complex | _ListLike | Index) -> Index: ... @overload - def __radd__(self: Index[bool], other: bool | Sequence[bool]) -> Index[bool]: ... - @overload - def __radd__(self: Index[int], other: bool | Sequence[bool]) -> Index[int]: ... - @overload - def __radd__(self: Index[float], other: int | Sequence[int]) -> Index[float]: ... - @overload def __radd__( - self: Index[complex], other: float | Sequence[float] - ) -> Index[complex]: ... + self: Supports_ElementRAdd[_T_contra, S2_CO], + other: _T_contra | Sequence[_T_contra], + ) -> Index[S2_CO]: ... @overload def __radd__( self: Index[S1_CT], @@ -607,7 +602,7 @@ class Index(IndexOpsMixin[S1]): ) -> Never: ... @overload def __radd__( - self: Index[_str], other: _str | Sequence[_str] | np_ndarray_str | Index[_str] + self: Index[_str], other: np_ndarray_str | Index[_str] ) -> Index[_str]: ... @overload def __sub__(self: Index[Never], other: DatetimeIndex) -> Never: ... diff --git a/pandas-stubs/core/series.pyi b/pandas-stubs/core/series.pyi index 815b949e1..a1e2a2580 100644 --- a/pandas-stubs/core/series.pyi +++ b/pandas-stubs/core/series.pyi @@ -40,6 +40,7 @@ from _typeshed import ( SupportsMul, SupportsRAdd, SupportsRMul, + _T_contra, ) from matplotlib.axes import ( Axes as PlotAxes, @@ -64,8 +65,11 @@ from pandas.core.arrays.categorical import CategoricalAccessor from pandas.core.arrays.datetimes import DatetimeArray from pandas.core.arrays.timedeltas import TimedeltaArray from pandas.core.base import ( + ElementOpsMixin, IndexOpsMixin, NumListLike, + Supports_ElementAdd, + Supports_ElementRAdd, _ListLike, ) from pandas.core.frame import DataFrame @@ -117,6 +121,7 @@ from pandas._typing import ( S1_CT, S1_CT_NDT, S2, + S2_CO, S2_CO_NSDT, S2_CT, T_COMPLEX, @@ -295,10 +300,10 @@ _ListLikeS1: TypeAlias = ( ArrayLike | dict[_str, np.ndarray] | Sequence[S1] | IndexOpsMixin[S1] ) -class Series(IndexOpsMixin[S1], NDFrame): +class Series(IndexOpsMixin[S1], ElementOpsMixin[S1], NDFrame): # Define __index__ because mypy thinks Series follows protocol `SupportsIndex` https://github.com/pandas-dev/pandas-stubs/pull/1332#discussion_r2285648790 __index__: ClassVar[None] - __hash__: ClassVar[None] + __hash__: ClassVar[None] # pyright: ignore[reportIncompatibleMethodOverride] @overload def __new__( @@ -1718,17 +1723,10 @@ class Series(IndexOpsMixin[S1], NDFrame): ), ) -> Series[Timedelta]: ... @overload - def __add__(self: Series[Timedelta], other: Period) -> Series[Period]: ... - @overload - def __add__(self: Series[bool], other: bool | Sequence[bool]) -> Series[bool]: ... - @overload - def __add__(self: Series[int], other: bool | Sequence[bool]) -> Series[int]: ... - @overload - def __add__(self: Series[float], other: int | Sequence[int]) -> Series[float]: ... - @overload def __add__( - self: Series[complex], other: float | Sequence[float] - ) -> Series[complex]: ... + self: Supports_ElementAdd[_T_contra, S2_CO], + other: _T_contra | Sequence[_T_contra], + ) -> Series[S2_CO]: ... @overload def __add__( self: Series[S1_CT], other: SupportsRAdd[S1_CT, S1_CO] @@ -1773,8 +1771,7 @@ class Series(IndexOpsMixin[S1], NDFrame): ) -> Never: ... @overload def __add__( - self: Series[_str], - other: _str | Sequence[_str] | np_ndarray_str | Index[_str] | Series[_str], + self: Series[_str], other: np_ndarray_str | Index[_str] | Series[_str] ) -> Series[_str]: ... @overload def add( @@ -1840,44 +1837,12 @@ class Series(IndexOpsMixin[S1], NDFrame): ) -> Series[Timedelta]: ... @overload def add( - self: Series[Timestamp], - other: Period, - level: Level | None = None, - fill_value: float | None = None, - axis: int = 0, - ) -> Series[Period]: ... - @overload - def add( - self: Series[bool], - other: bool | Sequence[bool], - level: Level | None = None, - fill_value: float | None = None, - axis: int = 0, - ) -> Series[bool]: ... - @overload - def add( - self: Series[int], - other: bool | Sequence[bool], - level: Level | None = None, - fill_value: float | None = None, - axis: int = 0, - ) -> Series[int]: ... - @overload - def add( - self: Series[float], - other: int | Sequence[int], - level: Level | None = None, - fill_value: float | None = None, - axis: int = 0, - ) -> Series[float]: ... - @overload - def add( - self: Series[complex], - other: float | Sequence[float], + self: Supports_ElementAdd[_T_contra, S2_CO], + other: _T_contra | Sequence[_T_contra], level: Level | None = None, fill_value: float | None = None, axis: int = 0, - ) -> Series[complex]: ... + ) -> Series[S2_CO]: ... @overload def add( self: Series[S1_CT], @@ -1936,7 +1901,7 @@ class Series(IndexOpsMixin[S1], NDFrame): @overload def add( self: Series[_str], - other: _str | Sequence[_str] | np_ndarray_str | Index[_str] | Series[_str], + other: np_ndarray_str | Index[_str] | Series[_str], level: Level | None = None, fill_value: float | None = None, axis: int = 0, @@ -1980,18 +1945,19 @@ class Series(IndexOpsMixin[S1], NDFrame): ), ) -> Series[Timedelta]: ... @overload - def __radd__(self: Series[Timedelta], other: Period) -> Series[Period]: ... - @overload def __radd__(self: Series[bool], other: bool | Sequence[bool]) -> Series[bool]: ... @overload - def __radd__(self: Series[int], other: bool | Sequence[bool]) -> Series[int]: ... - @overload def __radd__(self: Series[float], other: int | Sequence[int]) -> Series[float]: ... @overload def __radd__( self: Series[complex], other: float | Sequence[float] ) -> Series[complex]: ... @overload + def __radd__( + self: Supports_ElementRAdd[_T_contra, S2_CO], + other: _T_contra | Sequence[_T_contra], + ) -> Series[S2_CO]: ... + @overload def __radd__( self: Series[S1_CT], other: SupportsAdd[S1_CT, S1_CO] ) -> Series[S1_CO]: ... @@ -2035,8 +2001,7 @@ class Series(IndexOpsMixin[S1], NDFrame): ) -> Never: ... @overload def __radd__( - self: Series[_str], - other: _str | Sequence[_str] | np_ndarray_str | Index[_str] | Series[_str], + self: Series[_str], other: np_ndarray_str | Index[_str] | Series[_str] ) -> Series[_str]: ... @overload def __radd__(self: Series[BaseOffset], other: Period) -> Series[Period]: ... @@ -2106,44 +2071,12 @@ class Series(IndexOpsMixin[S1], NDFrame): ) -> Series[Timedelta]: ... @overload def radd( - self: Series[Timestamp], - other: Period, - level: Level | None = None, - fill_value: float | None = None, - axis: int = 0, - ) -> Series[Period]: ... - @overload - def radd( - self: Series[bool], - other: bool | Sequence[bool], - level: Level | None = None, - fill_value: float | None = None, - axis: int = 0, - ) -> Series[bool]: ... - @overload - def radd( - self: Series[int], - other: bool | Sequence[bool], - level: Level | None = None, - fill_value: float | None = None, - axis: int = 0, - ) -> Series[int]: ... - @overload - def radd( - self: Series[float], - other: int | Sequence[int], - level: Level | None = None, - fill_value: float | None = None, - axis: int = 0, - ) -> Series[float]: ... - @overload - def radd( - self: Series[complex], - other: float | Sequence[float], + self: Supports_ElementRAdd[_T_contra, S2_CO], + other: _T_contra | Sequence[_T_contra], level: Level | None = None, fill_value: float | None = None, axis: int = 0, - ) -> Series[complex]: ... + ) -> Series[S2_CO]: ... @overload def radd( self: Series[S1_CT], @@ -2202,7 +2135,7 @@ class Series(IndexOpsMixin[S1], NDFrame): @overload def radd( self: Series[_str], - other: _str | Sequence[_str] | np_ndarray_str | Index[_str] | Series[_str], + other: np_ndarray_str | Index[_str] | Series[_str], level: Level | None = None, fill_value: float | None = None, axis: int = 0, From 140d48df9cc57e0ef9285b6162404d3a63886b63 Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Tue, 14 Oct 2025 16:29:49 +0200 Subject: [PATCH 2/8] refactor: simplify mul --- pandas-stubs/core/base.pyi | 60 +++++++++++++++ pandas-stubs/core/series.pyi | 143 ++++++++--------------------------- 2 files changed, 91 insertions(+), 112 deletions(-) diff --git a/pandas-stubs/core/base.pyi b/pandas-stubs/core/base.pyi index 5bd603484..bde81c3c7 100644 --- a/pandas-stubs/core/base.pyi +++ b/pandas-stubs/core/base.pyi @@ -24,6 +24,7 @@ from pandas.core.indexes.base import Index from pandas.core.series import Series from typing_extensions import Self +from pandas._libs.tslibs.timedeltas import Timedelta from pandas._typing import ( S1, S2, @@ -33,6 +34,7 @@ from pandas._typing import ( DTypeLike, GenericT, GenericT_co, + Just, NDFrameT, Scalar, SequenceNotStr, @@ -217,6 +219,56 @@ class ElementOpsMixin(Generic[S2]): ) -> ElementOpsMixin[complex]: ... @overload def _radd(self: ElementOpsMixin[str], other: str) -> ElementOpsMixin[str]: ... + @overload + def _mul( + self: ElementOpsMixin[bool], other: bool | np.bool_ + ) -> ElementOpsMixin[bool]: ... + @overload + def _mul( + self: ElementOpsMixin[int], other: bool | np.bool_ + ) -> ElementOpsMixin[int]: ... + @overload + def _mul( + self: ElementOpsMixin[float], other: int | np.integer + ) -> Series[float]: ... + @overload + def _mul( + self: ElementOpsMixin[complex], other: float | np.floating + ) -> Series[complex]: ... + @overload + def _mul( + self: ElementOpsMixin[Timedelta], + other: Just[int] | Just[float] | np.integer | np.floating, + ) -> Series[Timedelta]: ... + @overload + def _mul( + self: ElementOpsMixin[str], other: Just[int] | np.integer + ) -> Series[str]: ... + @overload + def _rmul( + self: ElementOpsMixin[bool], other: bool | np.bool_ + ) -> ElementOpsMixin[bool]: ... + @overload + def _rmul( + self: ElementOpsMixin[int], other: bool | np.bool_ + ) -> ElementOpsMixin[int]: ... + @overload + def _rmul( + self: ElementOpsMixin[float], other: int | np.integer + ) -> Series[float]: ... + @overload + def _rmul( + self: ElementOpsMixin[complex], other: float | np.floating + ) -> Series[complex]: ... + @overload + def _rmul( + self: ElementOpsMixin[Timedelta], + other: Just[int] | Just[float] | np.integer | np.floating, + ) -> Series[Timedelta]: ... + @overload + def _rmul( + self: ElementOpsMixin[str], other: Just[int] | np.integer + ) -> Series[str]: ... @type_check_only class Supports_ElementAdd(Protocol[_T_contra, S2]): @@ -225,3 +277,11 @@ class Supports_ElementAdd(Protocol[_T_contra, S2]): @type_check_only class Supports_ElementRAdd(Protocol[_T_contra, S2]): def _radd(self, other: _T_contra, /) -> ElementOpsMixin[S2]: ... + +@type_check_only +class Supports_ElementMul(Protocol[_T_contra, S2]): + def _mul(self, other: _T_contra, /) -> ElementOpsMixin[S2]: ... + +@type_check_only +class Supports_ElementRMul(Protocol[_T_contra, S2]): + def _rmul(self, other: _T_contra, /) -> ElementOpsMixin[S2]: ... diff --git a/pandas-stubs/core/series.pyi b/pandas-stubs/core/series.pyi index a1e2a2580..54d6b84f0 100644 --- a/pandas-stubs/core/series.pyi +++ b/pandas-stubs/core/series.pyi @@ -69,7 +69,9 @@ from pandas.core.base import ( IndexOpsMixin, NumListLike, Supports_ElementAdd, + Supports_ElementMul, Supports_ElementRAdd, + Supports_ElementRMul, _ListLike, ) from pandas.core.frame import DataFrame @@ -121,7 +123,6 @@ from pandas._typing import ( S1_CT, S1_CT_NDT, S2, - S2_CO, S2_CO_NSDT, S2_CT, T_COMPLEX, @@ -1724,9 +1725,8 @@ class Series(IndexOpsMixin[S1], ElementOpsMixin[S1], NDFrame): ) -> Series[Timedelta]: ... @overload def __add__( - self: Supports_ElementAdd[_T_contra, S2_CO], - other: _T_contra | Sequence[_T_contra], - ) -> Series[S2_CO]: ... + self: Supports_ElementAdd[_T_contra, S2], other: _T_contra | Sequence[_T_contra] + ) -> Series[S2]: ... @overload def __add__( self: Series[S1_CT], other: SupportsRAdd[S1_CT, S1_CO] @@ -1837,12 +1837,12 @@ class Series(IndexOpsMixin[S1], ElementOpsMixin[S1], NDFrame): ) -> Series[Timedelta]: ... @overload def add( - self: Supports_ElementAdd[_T_contra, S2_CO], + self: Supports_ElementAdd[_T_contra, S2], other: _T_contra | Sequence[_T_contra], level: Level | None = None, fill_value: float | None = None, axis: int = 0, - ) -> Series[S2_CO]: ... + ) -> Series[S2]: ... @overload def add( self: Series[S1_CT], @@ -1944,6 +1944,7 @@ class Series(IndexOpsMixin[S1], ElementOpsMixin[S1], NDFrame): | Series[Timedelta] ), ) -> Series[Timedelta]: ... + # pyright is unhappy without the 3 overloads below @overload def __radd__(self: Series[bool], other: bool | Sequence[bool]) -> Series[bool]: ... @overload @@ -1952,11 +1953,12 @@ class Series(IndexOpsMixin[S1], ElementOpsMixin[S1], NDFrame): def __radd__( self: Series[complex], other: float | Sequence[float] ) -> Series[complex]: ... + # pyright is unhappy without the above 3 overloads @overload def __radd__( - self: Supports_ElementRAdd[_T_contra, S2_CO], + self: Supports_ElementRAdd[_T_contra, S2], other: _T_contra | Sequence[_T_contra], - ) -> Series[S2_CO]: ... + ) -> Series[S2]: ... @overload def __radd__( self: Series[S1_CT], other: SupportsAdd[S1_CT, S1_CO] @@ -2071,12 +2073,12 @@ class Series(IndexOpsMixin[S1], ElementOpsMixin[S1], NDFrame): ) -> Series[Timedelta]: ... @overload def radd( - self: Supports_ElementRAdd[_T_contra, S2_CO], + self: Supports_ElementRAdd[_T_contra, S2], other: _T_contra | Sequence[_T_contra], level: Level | None = None, fill_value: float | None = None, axis: int = 0, - ) -> Series[S2_CO]: ... + ) -> Series[S2]: ... @overload def radd( self: Series[S1_CT], @@ -2508,11 +2510,7 @@ class Series(IndexOpsMixin[S1], ElementOpsMixin[S1], NDFrame): def __mul__( self: Series[Timedelta], other: ( - Just[int] - | Just[float] - | Sequence[Just[int]] - | Sequence[Just[float]] - | np_ndarray_anyint + np_ndarray_anyint | np_ndarray_float | Index[int] | Index[float] @@ -2533,23 +2531,12 @@ class Series(IndexOpsMixin[S1], ElementOpsMixin[S1], NDFrame): ) -> Never: ... @overload def __mul__( - self: Series[_str], - other: ( - Just[int] - | Sequence[Just[int]] - | np_ndarray_anyint - | Index[int] - | Series[int] - ), + self: Series[_str], other: np_ndarray_anyint | Index[int] | Series[int] ) -> 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[complex], other: float | Sequence[float] - ) -> Series[complex]: ... + self: Supports_ElementMul[_T_contra, S2], other: _T_contra | Sequence[_T_contra] + ) -> Series[S2]: ... @overload def __mul__( self: Series[S2_CT], @@ -2618,11 +2605,7 @@ class Series(IndexOpsMixin[S1], ElementOpsMixin[S1], NDFrame): def mul( self: Series[Timedelta], other: ( - Just[int] - | Just[float] - | Sequence[Just[int]] - | Sequence[Just[float]] - | np_ndarray_anyint + np_ndarray_anyint | np_ndarray_float | Index[int] | Index[float] @@ -2636,41 +2619,19 @@ class Series(IndexOpsMixin[S1], ElementOpsMixin[S1], NDFrame): @overload def mul( self: Series[_str], - other: ( - Just[int] - | Sequence[Just[int]] - | np_ndarray_anyint - | Index[int] - | Series[int] - ), + other: np_ndarray_anyint | Index[int] | Series[int], level: Level | None = None, fill_value: float | None = None, axis: int = 0, ) -> Series[_str]: ... @overload def mul( - self: Series[T_INT], - other: bool | Sequence[bool], - level: Level | None = None, - fill_value: float | None = None, - axis: int = 0, - ) -> Series[T_INT]: ... - @overload - def mul( - self: Series[float], - other: int | Sequence[int], - level: Level | None = None, - fill_value: float | None = None, - axis: int = 0, - ) -> Series[float]: ... - @overload - def mul( - self: Series[complex], - other: float | Sequence[float], + self: Supports_ElementMul[_T_contra, S2], + other: _T_contra | Sequence[_T_contra], level: Level | None = None, fill_value: float | None = None, axis: int = 0, - ) -> Series[complex]: ... + ) -> Series[S2]: ... @overload def mul( self: Series[S2_CT], @@ -2763,11 +2724,7 @@ class Series(IndexOpsMixin[S1], ElementOpsMixin[S1], NDFrame): def __rmul__( self: Series[Timedelta], other: ( - Just[int] - | Just[float] - | Sequence[Just[int]] - | Sequence[Just[float]] - | np_ndarray_anyint + np_ndarray_anyint | np_ndarray_float | Index[int] | Index[float] @@ -2788,25 +2745,13 @@ class Series(IndexOpsMixin[S1], ElementOpsMixin[S1], NDFrame): ) -> Never: ... @overload def __rmul__( - self: Series[_str], - other: ( - Just[int] - | Sequence[Just[int]] - | np_ndarray_anyint - | Index[int] - | Series[int] - ), + self: Series[_str], other: np_ndarray_anyint | Index[int] | Series[int] ) -> Series[_str]: ... @overload def __rmul__( - self: Series[T_INT], other: bool | Sequence[bool] - ) -> Series[T_INT]: ... - @overload - def __rmul__(self: Series[float], other: int | Sequence[int]) -> Series[float]: ... - @overload - def __rmul__( - self: Series[complex], other: float | Sequence[float] - ) -> Series[complex]: ... + self: Supports_ElementRMul[_T_contra, S2], + other: _T_contra | Sequence[_T_contra], + ) -> Series[S2]: ... @overload def __rmul__( self: Series[S2_CT], @@ -2875,11 +2820,7 @@ class Series(IndexOpsMixin[S1], ElementOpsMixin[S1], NDFrame): def rmul( self: Series[Timedelta], other: ( - Just[int] - | Just[float] - | Sequence[Just[int]] - | Sequence[Just[float]] - | np_ndarray_anyint + np_ndarray_anyint | np_ndarray_float | Index[int] | Index[float] @@ -2893,41 +2834,19 @@ class Series(IndexOpsMixin[S1], ElementOpsMixin[S1], NDFrame): @overload def rmul( self: Series[_str], - other: ( - Just[int] - | Sequence[Just[int]] - | np_ndarray_anyint - | Index[int] - | Series[int] - ), + other: np_ndarray_anyint | Index[int] | Series[int], level: Level | None = None, fill_value: float | None = None, axis: int = 0, ) -> Series[_str]: ... @overload def rmul( - self: Series[T_INT], - other: bool | Sequence[bool], - level: Level | None = None, - fill_value: float | None = None, - axis: int = 0, - ) -> Series[T_INT]: ... - @overload - def rmul( - self: Series[float], - other: int | Sequence[int], - level: Level | None = None, - fill_value: float | None = None, - axis: int = 0, - ) -> Series[float]: ... - @overload - def rmul( - self: Series[complex], - other: float | Sequence[float], + self: Supports_ElementRMul[_T_contra, S2], + other: _T_contra | Sequence[_T_contra], level: Level | None = None, fill_value: float | None = None, axis: int = 0, - ) -> Series[complex]: ... + ) -> Series[S2]: ... @overload def rmul( self: Series[S2_CT], From 81c58d5fbcd148c19dfa99a21af448478dcc3a64 Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Tue, 14 Oct 2025 16:35:42 +0200 Subject: [PATCH 3/8] chore: remove S2_CO --- pandas-stubs/_typing.pyi | 1 - pandas-stubs/core/indexes/base.pyi | 11 +++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/pandas-stubs/_typing.pyi b/pandas-stubs/_typing.pyi index cd28db68e..15c71edd1 100644 --- a/pandas-stubs/_typing.pyi +++ b/pandas-stubs/_typing.pyi @@ -879,7 +879,6 @@ S1_CT_NDT = TypeVar( ) # Like S1, but without `default=Any`. S2 = TypeVar("S2", bound=SeriesDType) -S2_CO = TypeVar("S2_CO", bound=SeriesDType, covariant=True) S2_CO_NSDT = TypeVar("S2_CO_NSDT", bound=SeriesDTypeNoStrDateTime, covariant=True) S2_CT = TypeVar("S2_CT", bound=SeriesDType, contravariant=True) S3 = TypeVar("S3", bound=SeriesDType) diff --git a/pandas-stubs/core/indexes/base.pyi b/pandas-stubs/core/indexes/base.pyi index cd5e27a0c..e16f3196d 100644 --- a/pandas-stubs/core/indexes/base.pyi +++ b/pandas-stubs/core/indexes/base.pyi @@ -61,7 +61,7 @@ from pandas._typing import ( S1, S1_CO, S1_CT, - S2_CO, + S2, S2_CO_NSDT, S2_CT, T_COMPLEX, @@ -512,9 +512,8 @@ class Index(IndexOpsMixin[S1], ElementOpsMixin[S1]): def __add__(self, other: Index[Never]) -> Index: ... @overload def __add__( - self: Supports_ElementAdd[_T_contra, S2_CO], - other: _T_contra | Sequence[_T_contra], - ) -> Index[S2_CO]: ... + self: Supports_ElementAdd[_T_contra, S2], other: _T_contra | Sequence[_T_contra] + ) -> Index[S2]: ... @overload def __add__( self: Index[S1_CT], @@ -561,9 +560,9 @@ class Index(IndexOpsMixin[S1], ElementOpsMixin[S1]): def __radd__(self: Index[Never], other: complex | _ListLike | Index) -> Index: ... @overload def __radd__( - self: Supports_ElementRAdd[_T_contra, S2_CO], + self: Supports_ElementRAdd[_T_contra, S2], other: _T_contra | Sequence[_T_contra], - ) -> Index[S2_CO]: ... + ) -> Index[S2]: ... @overload def __radd__( self: Index[S1_CT], From f1cb20317474fe7bfc9bfa6534ab4018e2ea17dd Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Tue, 14 Oct 2025 17:05:54 +0200 Subject: [PATCH 4/8] refactor: simplify typevar --- pandas-stubs/_typing.pyi | 8 +--- pandas-stubs/core/indexes/base.pyi | 76 +++++++++--------------------- pandas-stubs/core/series.pyi | 62 ++++++++++-------------- 3 files changed, 48 insertions(+), 98 deletions(-) diff --git a/pandas-stubs/_typing.pyi b/pandas-stubs/_typing.pyi index 15c71edd1..ffb5ce263 100644 --- a/pandas-stubs/_typing.pyi +++ b/pandas-stubs/_typing.pyi @@ -872,15 +872,11 @@ SeriesDType: TypeAlias = ( | datetime.timedelta # includes pd.Timedelta ) S1 = TypeVar("S1", bound=SeriesDType, default=Any) -S1_CO = TypeVar("S1_CO", bound=SeriesDType, default=Any, covariant=True) -S1_CT = TypeVar("S1_CT", bound=SeriesDType, default=Any, contravariant=True) -S1_CT_NDT = TypeVar( - "S1_CT_NDT", bound=SeriesDTypeNoDateTime, default=Any, contravariant=True -) # Like S1, but without `default=Any`. S2 = TypeVar("S2", bound=SeriesDType) -S2_CO_NSDT = TypeVar("S2_CO_NSDT", bound=SeriesDTypeNoStrDateTime, covariant=True) S2_CT = TypeVar("S2_CT", bound=SeriesDType, contravariant=True) +S2_CT_NDT = TypeVar("S2_CT_NDT", bound=SeriesDTypeNoDateTime, contravariant=True) +S2_NSDT = TypeVar("S2_NSDT", bound=SeriesDTypeNoStrDateTime) S3 = TypeVar("S3", bound=SeriesDType) # Constraint, instead of bound diff --git a/pandas-stubs/core/indexes/base.pyi b/pandas-stubs/core/indexes/base.pyi index e16f3196d..a5e8217ea 100644 --- a/pandas-stubs/core/indexes/base.pyi +++ b/pandas-stubs/core/indexes/base.pyi @@ -44,7 +44,9 @@ from pandas.core.base import ( IndexOpsMixin, NumListLike, Supports_ElementAdd, + Supports_ElementMul, Supports_ElementRAdd, + Supports_ElementRMul, _ListLike, ) from pandas.core.indexes.category import CategoricalIndex @@ -59,13 +61,10 @@ from pandas._libs.tslibs.timedeltas import Timedelta from pandas._typing import ( C2, S1, - S1_CO, - S1_CT, S2, - S2_CO_NSDT, S2_CT, + S2_NSDT, T_COMPLEX, - T_INT, AnyAll, ArrayLike, AxesData, @@ -516,9 +515,9 @@ class Index(IndexOpsMixin[S1], ElementOpsMixin[S1]): ) -> Index[S2]: ... @overload def __add__( - self: Index[S1_CT], - other: SupportsRAdd[S1_CT, S1_CO] | Sequence[SupportsRAdd[S1_CT, S1_CO]], - ) -> Index[S1_CO]: ... + self: Index[S2_CT], + other: SupportsRAdd[S2_CT, S2] | Sequence[SupportsRAdd[S2_CT, S2]], + ) -> Index[S2]: ... @overload def __add__( self: Index[T_COMPLEX], other: np_ndarray_bool | Index[bool] @@ -565,9 +564,9 @@ class Index(IndexOpsMixin[S1], ElementOpsMixin[S1]): ) -> Index[S2]: ... @overload def __radd__( - self: Index[S1_CT], - other: SupportsAdd[S1_CT, S1_CO] | Sequence[SupportsAdd[S1_CT, S1_CO]], - ) -> Index[S1_CO]: ... + self: Index[S2_CT], + other: SupportsAdd[S2_CT, S2] | Sequence[SupportsAdd[S2_CT, S2]], + ) -> Index[S2]: ... @overload def __radd__( self: Index[T_COMPLEX], other: np_ndarray_bool | Index[bool] @@ -767,16 +766,7 @@ class Index(IndexOpsMixin[S1], ElementOpsMixin[S1]): @overload def __mul__( self: Index[Timedelta], - other: ( - Just[int] - | Just[float] - | Sequence[Just[int]] - | Sequence[Just[float]] - | np_ndarray_anyint - | np_ndarray_float - | Index[int] - | Index[float] - ), + other: np_ndarray_anyint | np_ndarray_float | Index[int] | Index[float], ) -> Index[Timedelta]: ... @overload def __mul__( @@ -791,24 +781,17 @@ class Index(IndexOpsMixin[S1], ElementOpsMixin[S1]): ) -> Never: ... @overload def __mul__( - self: Index[_str], - other: Just[int] | Sequence[Just[int]] | np_ndarray_anyint | Index[int], + self: Index[_str], other: np_ndarray_anyint | 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]: ... + self: Supports_ElementMul[_T_contra, S2], other: _T_contra | Sequence[_T_contra] + ) -> Index[S2]: ... @overload def __mul__( self: Index[S2_CT], - other: ( - SupportsRMul[S2_CT, S2_CO_NSDT] | Sequence[SupportsRMul[S2_CT, S2_CO_NSDT]] - ), - ) -> Index[S2_CO_NSDT]: ... + other: SupportsRMul[S2_CT, S2_NSDT] | Sequence[SupportsRMul[S2_CT, S2_NSDT]], + ) -> Index[S2_NSDT]: ... @overload def __mul__( self: Index[T_COMPLEX], other: np_ndarray_bool | Index[bool] @@ -859,16 +842,7 @@ class Index(IndexOpsMixin[S1], ElementOpsMixin[S1]): @overload def __rmul__( self: Index[Timedelta], - other: ( - Just[int] - | Just[float] - | Sequence[Just[int]] - | Sequence[Just[float]] - | np_ndarray_anyint - | np_ndarray_float - | Index[int] - | Index[float] - ), + other: np_ndarray_anyint | np_ndarray_float | Index[int] | Index[float], ) -> Index[Timedelta]: ... @overload def __rmul__( @@ -883,24 +857,18 @@ class Index(IndexOpsMixin[S1], ElementOpsMixin[S1]): ) -> Never: ... @overload def __rmul__( - self: Index[_str], - other: Just[int] | Sequence[Just[int]] | np_ndarray_anyint | Index[int], + self: Index[_str], other: np_ndarray_anyint | 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]: ... + self: Supports_ElementRMul[_T_contra, S2], + other: _T_contra | Sequence[_T_contra], + ) -> Index[S2]: ... @overload def __rmul__( self: Index[S2_CT], - other: ( - SupportsMul[S2_CT, S2_CO_NSDT] | Sequence[SupportsMul[S2_CT, S2_CO_NSDT]] - ), - ) -> Index[S2_CO_NSDT]: ... + other: SupportsMul[S2_CT, S2_NSDT] | Sequence[SupportsMul[S2_CT, S2_NSDT]], + ) -> Index[S2_NSDT]: ... @overload def __rmul__( self: Index[T_COMPLEX], other: np_ndarray_bool | Index[bool] diff --git a/pandas-stubs/core/series.pyi b/pandas-stubs/core/series.pyi index 54d6b84f0..6b2524847 100644 --- a/pandas-stubs/core/series.pyi +++ b/pandas-stubs/core/series.pyi @@ -119,12 +119,10 @@ from pandas._libs.tslibs.nattype import NaTType from pandas._libs.tslibs.offsets import DateOffset from pandas._typing import ( S1, - S1_CO, - S1_CT, - S1_CT_NDT, S2, - S2_CO_NSDT, S2_CT, + S2_CT_NDT, + S2_NSDT, T_COMPLEX, T_INT, AggFuncTypeBase, @@ -880,8 +878,8 @@ class Series(IndexOpsMixin[S1], ElementOpsMixin[S1], NDFrame): def diff(self: Series[Interval], periods: int = ...) -> Never: ... @overload def diff( - self: SupportsGetItem[Scalar, SupportsSelfSub[S1_CO]], periods: int = ... - ) -> Series[S1_CO]: ... + self: SupportsGetItem[Scalar, SupportsSelfSub[S2]], periods: int = ... + ) -> Series[S2]: ... def autocorr(self, lag: int = 1) -> float: ... @overload def dot(self, other: Series[S1]) -> Scalar: ... @@ -1728,14 +1726,12 @@ class Series(IndexOpsMixin[S1], ElementOpsMixin[S1], NDFrame): self: Supports_ElementAdd[_T_contra, S2], other: _T_contra | Sequence[_T_contra] ) -> Series[S2]: ... @overload - def __add__( - self: Series[S1_CT], other: SupportsRAdd[S1_CT, S1_CO] - ) -> Series[S1_CO]: ... + def __add__(self: Series[S2_CT], other: SupportsRAdd[S2_CT, S2]) -> Series[S2]: ... # pandas-dev/pandas#62353 @overload def __add__( - self: Series[S1_CT_NDT], other: Sequence[SupportsRAdd[S1_CT_NDT, S1_CO]] - ) -> Series[S1_CO]: ... + self: Series[S2_CT_NDT], other: Sequence[SupportsRAdd[S2_CT_NDT, S2]] + ) -> Series[S2]: ... @overload def __add__( self: Series[T_COMPLEX], other: np_ndarray_bool | Index[bool] | Series[bool] @@ -1845,12 +1841,12 @@ class Series(IndexOpsMixin[S1], ElementOpsMixin[S1], NDFrame): ) -> Series[S2]: ... @overload def add( - self: Series[S1_CT], - other: SupportsRAdd[S1_CT, S1_CO] | Sequence[SupportsRAdd[S1_CT, S1_CO]], + self: Series[S2_CT], + other: SupportsRAdd[S2_CT, S2] | Sequence[SupportsRAdd[S2_CT, S2]], level: Level | None = None, fill_value: float | None = None, axis: int = 0, - ) -> Series[S1_CO]: ... + ) -> Series[S2]: ... @overload def add( self: Series[T_COMPLEX], @@ -1960,14 +1956,12 @@ class Series(IndexOpsMixin[S1], ElementOpsMixin[S1], NDFrame): other: _T_contra | Sequence[_T_contra], ) -> Series[S2]: ... @overload - def __radd__( - self: Series[S1_CT], other: SupportsAdd[S1_CT, S1_CO] - ) -> Series[S1_CO]: ... + def __radd__(self: Series[S2_CT], other: SupportsAdd[S2_CT, S2]) -> Series[S2]: ... # pandas-dev/pandas#62353 @overload def __radd__( - self: Series[S1_CT_NDT], other: Sequence[SupportsAdd[S1_CT_NDT, S1_CO]] - ) -> Series[S1_CO]: ... + self: Series[S2_CT_NDT], other: Sequence[SupportsAdd[S2_CT_NDT, S2]] + ) -> Series[S2]: ... @overload def __radd__( self: Series[T_COMPLEX], other: np_ndarray_bool | Index[bool] | Series[bool] @@ -2081,12 +2075,12 @@ class Series(IndexOpsMixin[S1], ElementOpsMixin[S1], NDFrame): ) -> Series[S2]: ... @overload def radd( - self: Series[S1_CT], - other: SupportsAdd[S1_CT, S1_CO] | Sequence[SupportsAdd[S1_CT, S1_CO]], + self: Series[S2_CT], + other: SupportsAdd[S2_CT, S2] | Sequence[SupportsAdd[S2_CT, S2]], level: Level | None = None, fill_value: float | None = None, axis: int = 0, - ) -> Series[S1_CO]: ... + ) -> Series[S2]: ... @overload def radd( self: Series[T_COMPLEX], @@ -2540,10 +2534,8 @@ class Series(IndexOpsMixin[S1], ElementOpsMixin[S1], NDFrame): @overload def __mul__( self: Series[S2_CT], - other: ( - SupportsRMul[S2_CT, S2_CO_NSDT] | Sequence[SupportsRMul[S2_CT, S2_CO_NSDT]] - ), - ) -> Series[S2_CO_NSDT]: ... + other: SupportsRMul[S2_CT, S2_NSDT] | Sequence[SupportsRMul[S2_CT, S2_NSDT]], + ) -> Series[S2_NSDT]: ... @overload def __mul__( self: Series[T_COMPLEX], other: np_ndarray_bool | Index[bool] | Series[bool] @@ -2635,13 +2627,11 @@ class Series(IndexOpsMixin[S1], ElementOpsMixin[S1], NDFrame): @overload def mul( self: Series[S2_CT], - other: ( - SupportsRMul[S2_CT, S2_CO_NSDT] | Sequence[SupportsRMul[S2_CT, S2_CO_NSDT]] - ), + other: SupportsRMul[S2_CT, S2_NSDT] | Sequence[SupportsRMul[S2_CT, S2_NSDT]], level: Level | None = None, fill_value: float | None = None, axis: int = 0, - ) -> Series[S2_CO_NSDT]: ... + ) -> Series[S2_NSDT]: ... @overload def mul( self: Series[T_COMPLEX], @@ -2755,10 +2745,8 @@ class Series(IndexOpsMixin[S1], ElementOpsMixin[S1], NDFrame): @overload def __rmul__( self: Series[S2_CT], - other: ( - SupportsMul[S2_CT, S2_CO_NSDT] | Sequence[SupportsMul[S2_CT, S2_CO_NSDT]] - ), - ) -> Series[S2_CO_NSDT]: ... + other: SupportsMul[S2_CT, S2_NSDT] | Sequence[SupportsMul[S2_CT, S2_NSDT]], + ) -> Series[S2_NSDT]: ... @overload def __rmul__( self: Series[T_COMPLEX], other: np_ndarray_bool | Index[bool] | Series[bool] @@ -2850,13 +2838,11 @@ class Series(IndexOpsMixin[S1], ElementOpsMixin[S1], NDFrame): @overload def rmul( self: Series[S2_CT], - other: ( - SupportsMul[S2_CT, S2_CO_NSDT] | Sequence[SupportsMul[S2_CT, S2_CO_NSDT]] - ), + other: SupportsMul[S2_CT, S2_NSDT] | Sequence[SupportsMul[S2_CT, S2_NSDT]], level: Level | None = None, fill_value: float | None = None, axis: int = 0, - ) -> Series[S2_CO_NSDT]: ... + ) -> Series[S2_NSDT]: ... @overload def rmul( self: Series[T_COMPLEX], From 6ffcd37177850ca9336e51f8ffd9975a79cc7015 Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Tue, 14 Oct 2025 17:26:19 +0200 Subject: [PATCH 5/8] typo --- pandas-stubs/core/base.pyi | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pandas-stubs/core/base.pyi b/pandas-stubs/core/base.pyi index bde81c3c7..b821c9be1 100644 --- a/pandas-stubs/core/base.pyi +++ b/pandas-stubs/core/base.pyi @@ -230,20 +230,20 @@ class ElementOpsMixin(Generic[S2]): @overload def _mul( self: ElementOpsMixin[float], other: int | np.integer - ) -> Series[float]: ... + ) -> ElementOpsMixin[float]: ... @overload def _mul( self: ElementOpsMixin[complex], other: float | np.floating - ) -> Series[complex]: ... + ) -> ElementOpsMixin[complex]: ... @overload def _mul( self: ElementOpsMixin[Timedelta], other: Just[int] | Just[float] | np.integer | np.floating, - ) -> Series[Timedelta]: ... + ) -> ElementOpsMixin[Timedelta]: ... @overload def _mul( self: ElementOpsMixin[str], other: Just[int] | np.integer - ) -> Series[str]: ... + ) -> ElementOpsMixin[str]: ... @overload def _rmul( self: ElementOpsMixin[bool], other: bool | np.bool_ @@ -255,20 +255,20 @@ class ElementOpsMixin(Generic[S2]): @overload def _rmul( self: ElementOpsMixin[float], other: int | np.integer - ) -> Series[float]: ... + ) -> ElementOpsMixin[float]: ... @overload def _rmul( self: ElementOpsMixin[complex], other: float | np.floating - ) -> Series[complex]: ... + ) -> ElementOpsMixin[complex]: ... @overload def _rmul( self: ElementOpsMixin[Timedelta], other: Just[int] | Just[float] | np.integer | np.floating, - ) -> Series[Timedelta]: ... + ) -> ElementOpsMixin[Timedelta]: ... @overload def _rmul( self: ElementOpsMixin[str], other: Just[int] | np.integer - ) -> Series[str]: ... + ) -> ElementOpsMixin[str]: ... @type_check_only class Supports_ElementAdd(Protocol[_T_contra, S2]): From 2a3d47eb5250e16bbcfed25c2c5415290dd0a952 Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Tue, 14 Oct 2025 18:03:10 +0200 Subject: [PATCH 6/8] test_dist --- tests/series/arithmetic/str/test_mul.py | 7 ++++--- tests/series/arithmetic/timedelta/test_mul.py | 5 +++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/tests/series/arithmetic/str/test_mul.py b/tests/series/arithmetic/str/test_mul.py index 1d562db75..7bfbf4244 100644 --- a/tests/series/arithmetic/str/test_mul.py +++ b/tests/series/arithmetic/str/test_mul.py @@ -179,11 +179,12 @@ def test_mul_pd_index(left: "pd.Series[str]") -> None: _05 = left * d # type: ignore[operator] # pyright: ignore[reportOperatorIssue] if TYPE_CHECKING_INVALID_USAGE: - _10 = b * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + # mypy gives different errors for mypy and test_dist + _10 = b * left # type: ignore[operator,type-var,unused-ignore] # pyright: ignore[reportOperatorIssue] 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] + _12 = f * left # type: ignore[operator,type-var,unused-ignore] # pyright: ignore[reportOperatorIssue] + _13 = c * left # type: ignore[operator,type-var,unused-ignore] # 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/series/arithmetic/timedelta/test_mul.py b/tests/series/arithmetic/timedelta/test_mul.py index 4d0b5fe26..b42eebe0b 100644 --- a/tests/series/arithmetic/timedelta/test_mul.py +++ b/tests/series/arithmetic/timedelta/test_mul.py @@ -141,11 +141,12 @@ def test_mul_pd_index(left: "pd.Series[pd.Timedelta]") -> None: _03 = left * c # type: ignore[operator] # pyright: ignore[reportOperatorIssue] if TYPE_CHECKING_INVALID_USAGE: - _10 = b * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + # mypy gives different errors for mypy and test_dist + _10 = b * left # type: ignore[operator,type-var,unused-ignore] # pyright: ignore[reportOperatorIssue] check(assert_type(i * left, "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) check(assert_type(f * left, "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) if TYPE_CHECKING_INVALID_USAGE: - _13 = c * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _13 = c * left # type: ignore[operator,type-var,unused-ignore] # pyright: ignore[reportOperatorIssue] if TYPE_CHECKING_INVALID_USAGE: left.mul(b) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] From cb645f1e96fa37b1e74bee4e3b2263f9865c47a3 Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Wed, 15 Oct 2025 10:17:05 +0200 Subject: [PATCH 7/8] fix(comment): https://github.com/pandas-dev/pandas-stubs/pull/1424#discussion_r2430145712 --- pandas-stubs/core/base.pyi | 60 +++++++++---------- pandas-stubs/core/indexes/base.pyi | 16 ++--- pandas-stubs/core/series.pyi | 24 ++++---- tests/series/arithmetic/str/test_mul.py | 7 +-- tests/series/arithmetic/timedelta/test_mul.py | 5 +- 5 files changed, 55 insertions(+), 57 deletions(-) diff --git a/pandas-stubs/core/base.pyi b/pandas-stubs/core/base.pyi index b821c9be1..13ecdd8b0 100644 --- a/pandas-stubs/core/base.pyi +++ b/pandas-stubs/core/base.pyi @@ -184,104 +184,104 @@ NumListLike: TypeAlias = ( @type_check_only class ElementOpsMixin(Generic[S2]): @overload - def _add( + def _proto_add( self: ElementOpsMixin[bool], other: bool | np.bool_ ) -> ElementOpsMixin[bool]: ... @overload - def _add( + def _proto_add( self: ElementOpsMixin[int], other: bool | np.bool_ ) -> ElementOpsMixin[int]: ... @overload - def _add( + def _proto_add( self: ElementOpsMixin[float], other: int | np.integer ) -> ElementOpsMixin[float]: ... @overload - def _add( + def _proto_add( self: ElementOpsMixin[complex], other: float | np.floating ) -> ElementOpsMixin[complex]: ... @overload - def _add(self: ElementOpsMixin[str], other: str) -> ElementOpsMixin[str]: ... + def _proto_add(self: ElementOpsMixin[str], other: str) -> ElementOpsMixin[str]: ... @overload - def _radd( + def _proto_radd( self: ElementOpsMixin[bool], other: bool | np.bool_ ) -> ElementOpsMixin[bool]: ... @overload - def _radd( + def _proto_radd( self: ElementOpsMixin[int], other: bool | np.bool_ ) -> ElementOpsMixin[int]: ... @overload - def _radd( + def _proto_radd( self: ElementOpsMixin[float], other: int | np.integer ) -> ElementOpsMixin[float]: ... @overload - def _radd( + def _proto_radd( self: ElementOpsMixin[complex], other: float | np.floating ) -> ElementOpsMixin[complex]: ... @overload - def _radd(self: ElementOpsMixin[str], other: str) -> ElementOpsMixin[str]: ... + def _proto_radd(self: ElementOpsMixin[str], other: str) -> ElementOpsMixin[str]: ... @overload - def _mul( + def _proto_mul( self: ElementOpsMixin[bool], other: bool | np.bool_ ) -> ElementOpsMixin[bool]: ... @overload - def _mul( + def _proto_mul( self: ElementOpsMixin[int], other: bool | np.bool_ ) -> ElementOpsMixin[int]: ... @overload - def _mul( + def _proto_mul( self: ElementOpsMixin[float], other: int | np.integer ) -> ElementOpsMixin[float]: ... @overload - def _mul( + def _proto_mul( self: ElementOpsMixin[complex], other: float | np.floating ) -> ElementOpsMixin[complex]: ... @overload - def _mul( + def _proto_mul( self: ElementOpsMixin[Timedelta], other: Just[int] | Just[float] | np.integer | np.floating, ) -> ElementOpsMixin[Timedelta]: ... @overload - def _mul( + def _proto_mul( self: ElementOpsMixin[str], other: Just[int] | np.integer ) -> ElementOpsMixin[str]: ... @overload - def _rmul( + def _proto_rmul( self: ElementOpsMixin[bool], other: bool | np.bool_ ) -> ElementOpsMixin[bool]: ... @overload - def _rmul( + def _proto_rmul( self: ElementOpsMixin[int], other: bool | np.bool_ ) -> ElementOpsMixin[int]: ... @overload - def _rmul( + def _proto_rmul( self: ElementOpsMixin[float], other: int | np.integer ) -> ElementOpsMixin[float]: ... @overload - def _rmul( + def _proto_rmul( self: ElementOpsMixin[complex], other: float | np.floating ) -> ElementOpsMixin[complex]: ... @overload - def _rmul( + def _proto_rmul( self: ElementOpsMixin[Timedelta], other: Just[int] | Just[float] | np.integer | np.floating, ) -> ElementOpsMixin[Timedelta]: ... @overload - def _rmul( + def _proto_rmul( self: ElementOpsMixin[str], other: Just[int] | np.integer ) -> ElementOpsMixin[str]: ... @type_check_only -class Supports_ElementAdd(Protocol[_T_contra, S2]): - def _add(self, other: _T_contra, /) -> ElementOpsMixin[S2]: ... +class Supports_ProtoAdd(Protocol[_T_contra, S2]): + def _proto_add(self, other: _T_contra, /) -> ElementOpsMixin[S2]: ... @type_check_only -class Supports_ElementRAdd(Protocol[_T_contra, S2]): - def _radd(self, other: _T_contra, /) -> ElementOpsMixin[S2]: ... +class Supports_ProtoRAdd(Protocol[_T_contra, S2]): + def _proto_radd(self, other: _T_contra, /) -> ElementOpsMixin[S2]: ... @type_check_only -class Supports_ElementMul(Protocol[_T_contra, S2]): - def _mul(self, other: _T_contra, /) -> ElementOpsMixin[S2]: ... +class Supports_ProtoMul(Protocol[_T_contra, S2]): + def _proto_mul(self, other: _T_contra, /) -> ElementOpsMixin[S2]: ... @type_check_only -class Supports_ElementRMul(Protocol[_T_contra, S2]): - def _rmul(self, other: _T_contra, /) -> ElementOpsMixin[S2]: ... +class Supports_ProtoRMul(Protocol[_T_contra, S2]): + def _proto_rmul(self, other: _T_contra, /) -> ElementOpsMixin[S2]: ... diff --git a/pandas-stubs/core/indexes/base.pyi b/pandas-stubs/core/indexes/base.pyi index a5e8217ea..600061e4b 100644 --- a/pandas-stubs/core/indexes/base.pyi +++ b/pandas-stubs/core/indexes/base.pyi @@ -43,10 +43,10 @@ from pandas.core.base import ( ElementOpsMixin, IndexOpsMixin, NumListLike, - Supports_ElementAdd, - Supports_ElementMul, - Supports_ElementRAdd, - Supports_ElementRMul, + Supports_ProtoAdd, + Supports_ProtoMul, + Supports_ProtoRAdd, + Supports_ProtoRMul, _ListLike, ) from pandas.core.indexes.category import CategoricalIndex @@ -511,7 +511,7 @@ class Index(IndexOpsMixin[S1], ElementOpsMixin[S1]): def __add__(self, other: Index[Never]) -> Index: ... @overload def __add__( - self: Supports_ElementAdd[_T_contra, S2], other: _T_contra | Sequence[_T_contra] + self: Supports_ProtoAdd[_T_contra, S2], other: _T_contra | Sequence[_T_contra] ) -> Index[S2]: ... @overload def __add__( @@ -559,7 +559,7 @@ class Index(IndexOpsMixin[S1], ElementOpsMixin[S1]): def __radd__(self: Index[Never], other: complex | _ListLike | Index) -> Index: ... @overload def __radd__( - self: Supports_ElementRAdd[_T_contra, S2], + self: Supports_ProtoRAdd[_T_contra, S2], other: _T_contra | Sequence[_T_contra], ) -> Index[S2]: ... @overload @@ -785,7 +785,7 @@ class Index(IndexOpsMixin[S1], ElementOpsMixin[S1]): ) -> Index[_str]: ... @overload def __mul__( - self: Supports_ElementMul[_T_contra, S2], other: _T_contra | Sequence[_T_contra] + self: Supports_ProtoMul[_T_contra, S2], other: _T_contra | Sequence[_T_contra] ) -> Index[S2]: ... @overload def __mul__( @@ -861,7 +861,7 @@ class Index(IndexOpsMixin[S1], ElementOpsMixin[S1]): ) -> Index[_str]: ... @overload def __rmul__( - self: Supports_ElementRMul[_T_contra, S2], + self: Supports_ProtoRMul[_T_contra, S2], other: _T_contra | Sequence[_T_contra], ) -> Index[S2]: ... @overload diff --git a/pandas-stubs/core/series.pyi b/pandas-stubs/core/series.pyi index 6b2524847..fa98d12f6 100644 --- a/pandas-stubs/core/series.pyi +++ b/pandas-stubs/core/series.pyi @@ -68,10 +68,10 @@ from pandas.core.base import ( ElementOpsMixin, IndexOpsMixin, NumListLike, - Supports_ElementAdd, - Supports_ElementMul, - Supports_ElementRAdd, - Supports_ElementRMul, + Supports_ProtoAdd, + Supports_ProtoMul, + Supports_ProtoRAdd, + Supports_ProtoRMul, _ListLike, ) from pandas.core.frame import DataFrame @@ -1723,7 +1723,7 @@ class Series(IndexOpsMixin[S1], ElementOpsMixin[S1], NDFrame): ) -> Series[Timedelta]: ... @overload def __add__( - self: Supports_ElementAdd[_T_contra, S2], other: _T_contra | Sequence[_T_contra] + self: Supports_ProtoAdd[_T_contra, S2], other: _T_contra | Sequence[_T_contra] ) -> Series[S2]: ... @overload def __add__(self: Series[S2_CT], other: SupportsRAdd[S2_CT, S2]) -> Series[S2]: ... @@ -1833,7 +1833,7 @@ class Series(IndexOpsMixin[S1], ElementOpsMixin[S1], NDFrame): ) -> Series[Timedelta]: ... @overload def add( - self: Supports_ElementAdd[_T_contra, S2], + self: Supports_ProtoAdd[_T_contra, S2], other: _T_contra | Sequence[_T_contra], level: Level | None = None, fill_value: float | None = None, @@ -1952,7 +1952,7 @@ class Series(IndexOpsMixin[S1], ElementOpsMixin[S1], NDFrame): # pyright is unhappy without the above 3 overloads @overload def __radd__( - self: Supports_ElementRAdd[_T_contra, S2], + self: Supports_ProtoRAdd[_T_contra, S2], other: _T_contra | Sequence[_T_contra], ) -> Series[S2]: ... @overload @@ -2067,7 +2067,7 @@ class Series(IndexOpsMixin[S1], ElementOpsMixin[S1], NDFrame): ) -> Series[Timedelta]: ... @overload def radd( - self: Supports_ElementRAdd[_T_contra, S2], + self: Supports_ProtoRAdd[_T_contra, S2], other: _T_contra | Sequence[_T_contra], level: Level | None = None, fill_value: float | None = None, @@ -2529,7 +2529,7 @@ class Series(IndexOpsMixin[S1], ElementOpsMixin[S1], NDFrame): ) -> Series[_str]: ... @overload def __mul__( - self: Supports_ElementMul[_T_contra, S2], other: _T_contra | Sequence[_T_contra] + self: Supports_ProtoMul[_T_contra, S2], other: _T_contra | Sequence[_T_contra] ) -> Series[S2]: ... @overload def __mul__( @@ -2618,7 +2618,7 @@ class Series(IndexOpsMixin[S1], ElementOpsMixin[S1], NDFrame): ) -> Series[_str]: ... @overload def mul( - self: Supports_ElementMul[_T_contra, S2], + self: Supports_ProtoMul[_T_contra, S2], other: _T_contra | Sequence[_T_contra], level: Level | None = None, fill_value: float | None = None, @@ -2739,7 +2739,7 @@ class Series(IndexOpsMixin[S1], ElementOpsMixin[S1], NDFrame): ) -> Series[_str]: ... @overload def __rmul__( - self: Supports_ElementRMul[_T_contra, S2], + self: Supports_ProtoRMul[_T_contra, S2], other: _T_contra | Sequence[_T_contra], ) -> Series[S2]: ... @overload @@ -2829,7 +2829,7 @@ class Series(IndexOpsMixin[S1], ElementOpsMixin[S1], NDFrame): ) -> Series[_str]: ... @overload def rmul( - self: Supports_ElementRMul[_T_contra, S2], + self: Supports_ProtoRMul[_T_contra, S2], other: _T_contra | Sequence[_T_contra], level: Level | None = None, fill_value: float | None = None, diff --git a/tests/series/arithmetic/str/test_mul.py b/tests/series/arithmetic/str/test_mul.py index 7bfbf4244..6d5655bde 100644 --- a/tests/series/arithmetic/str/test_mul.py +++ b/tests/series/arithmetic/str/test_mul.py @@ -179,12 +179,11 @@ def test_mul_pd_index(left: "pd.Series[str]") -> None: _05 = left * d # type: ignore[operator] # pyright: ignore[reportOperatorIssue] if TYPE_CHECKING_INVALID_USAGE: - # mypy gives different errors for mypy and test_dist - _10 = b * left # type: ignore[operator,type-var,unused-ignore] # pyright: ignore[reportOperatorIssue] + _10 = b * left # type: ignore[operator,type-var,unused-ignore] # pyright: ignore[reportOperatorIssue] # mypy gives different errors for mypy and test_dist check(assert_type(i * left, "pd.Series[str]"), pd.Series, str) if TYPE_CHECKING_INVALID_USAGE: - _12 = f * left # type: ignore[operator,type-var,unused-ignore] # pyright: ignore[reportOperatorIssue] - _13 = c * left # type: ignore[operator,type-var,unused-ignore] # pyright: ignore[reportOperatorIssue] + _12 = f * left # type: ignore[operator,type-var,unused-ignore] # pyright: ignore[reportOperatorIssue] # mypy gives different errors for mypy and test_dist + _13 = c * left # type: ignore[operator,type-var,unused-ignore] # pyright: ignore[reportOperatorIssue] # mypy gives different errors for mypy and test_dist _14 = s * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] _15 = d * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] diff --git a/tests/series/arithmetic/timedelta/test_mul.py b/tests/series/arithmetic/timedelta/test_mul.py index b42eebe0b..0608e3e4a 100644 --- a/tests/series/arithmetic/timedelta/test_mul.py +++ b/tests/series/arithmetic/timedelta/test_mul.py @@ -141,12 +141,11 @@ def test_mul_pd_index(left: "pd.Series[pd.Timedelta]") -> None: _03 = left * c # type: ignore[operator] # pyright: ignore[reportOperatorIssue] if TYPE_CHECKING_INVALID_USAGE: - # mypy gives different errors for mypy and test_dist - _10 = b * left # type: ignore[operator,type-var,unused-ignore] # pyright: ignore[reportOperatorIssue] + _10 = b * left # type: ignore[operator,type-var,unused-ignore] # pyright: ignore[reportOperatorIssue] # mypy gives different errors for mypy and test_dist check(assert_type(i * left, "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) check(assert_type(f * left, "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) if TYPE_CHECKING_INVALID_USAGE: - _13 = c * left # type: ignore[operator,type-var,unused-ignore] # pyright: ignore[reportOperatorIssue] + _13 = c * left # type: ignore[operator,type-var,unused-ignore] # pyright: ignore[reportOperatorIssue] # mypy gives different errors for mypy and test_dist if TYPE_CHECKING_INVALID_USAGE: left.mul(b) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue] From fc74e6f9a5782a02eda037cf3076ddba507e9bba Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Wed, 15 Oct 2025 11:01:52 +0200 Subject: [PATCH 8/8] attempt to simplify the script --- pandas-stubs/core/base.pyi | 24 ++++++++++++------------ pandas-stubs/core/series.pyi | 16 +++++++--------- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/pandas-stubs/core/base.pyi b/pandas-stubs/core/base.pyi index 13ecdd8b0..dfbc41bbd 100644 --- a/pandas-stubs/core/base.pyi +++ b/pandas-stubs/core/base.pyi @@ -189,15 +189,15 @@ class ElementOpsMixin(Generic[S2]): ) -> ElementOpsMixin[bool]: ... @overload def _proto_add( - self: ElementOpsMixin[int], other: bool | np.bool_ + self: ElementOpsMixin[int], other: int | np.integer ) -> ElementOpsMixin[int]: ... @overload def _proto_add( - self: ElementOpsMixin[float], other: int | np.integer + self: ElementOpsMixin[float], other: float | np.floating ) -> ElementOpsMixin[float]: ... @overload def _proto_add( - self: ElementOpsMixin[complex], other: float | np.floating + self: ElementOpsMixin[complex], other: complex | np.complexfloating ) -> ElementOpsMixin[complex]: ... @overload def _proto_add(self: ElementOpsMixin[str], other: str) -> ElementOpsMixin[str]: ... @@ -207,15 +207,15 @@ class ElementOpsMixin(Generic[S2]): ) -> ElementOpsMixin[bool]: ... @overload def _proto_radd( - self: ElementOpsMixin[int], other: bool | np.bool_ + self: ElementOpsMixin[int], other: int | np.integer ) -> ElementOpsMixin[int]: ... @overload def _proto_radd( - self: ElementOpsMixin[float], other: int | np.integer + self: ElementOpsMixin[float], other: float | np.floating ) -> ElementOpsMixin[float]: ... @overload def _proto_radd( - self: ElementOpsMixin[complex], other: float | np.floating + self: ElementOpsMixin[complex], other: complex | np.complexfloating ) -> ElementOpsMixin[complex]: ... @overload def _proto_radd(self: ElementOpsMixin[str], other: str) -> ElementOpsMixin[str]: ... @@ -225,15 +225,15 @@ class ElementOpsMixin(Generic[S2]): ) -> ElementOpsMixin[bool]: ... @overload def _proto_mul( - self: ElementOpsMixin[int], other: bool | np.bool_ + self: ElementOpsMixin[int], other: int | np.integer ) -> ElementOpsMixin[int]: ... @overload def _proto_mul( - self: ElementOpsMixin[float], other: int | np.integer + self: ElementOpsMixin[float], other: float | np.floating ) -> ElementOpsMixin[float]: ... @overload def _proto_mul( - self: ElementOpsMixin[complex], other: float | np.floating + self: ElementOpsMixin[complex], other: complex | np.complexfloating ) -> ElementOpsMixin[complex]: ... @overload def _proto_mul( @@ -250,15 +250,15 @@ class ElementOpsMixin(Generic[S2]): ) -> ElementOpsMixin[bool]: ... @overload def _proto_rmul( - self: ElementOpsMixin[int], other: bool | np.bool_ + self: ElementOpsMixin[int], other: int | np.integer ) -> ElementOpsMixin[int]: ... @overload def _proto_rmul( - self: ElementOpsMixin[float], other: int | np.integer + self: ElementOpsMixin[float], other: float | np.floating ) -> ElementOpsMixin[float]: ... @overload def _proto_rmul( - self: ElementOpsMixin[complex], other: float | np.floating + self: ElementOpsMixin[complex], other: complex | np.complexfloating ) -> ElementOpsMixin[complex]: ... @overload def _proto_rmul( diff --git a/pandas-stubs/core/series.pyi b/pandas-stubs/core/series.pyi index fa98d12f6..bdd2193b2 100644 --- a/pandas-stubs/core/series.pyi +++ b/pandas-stubs/core/series.pyi @@ -1723,7 +1723,7 @@ class Series(IndexOpsMixin[S1], ElementOpsMixin[S1], NDFrame): ) -> Series[Timedelta]: ... @overload def __add__( - self: Supports_ProtoAdd[_T_contra, S2], other: _T_contra | Sequence[_T_contra] + self: Supports_ProtoAdd[S2_CT, S2], other: S2_CT | Sequence[S2_CT] ) -> Series[S2]: ... @overload def __add__(self: Series[S2_CT], other: SupportsRAdd[S2_CT, S2]) -> Series[S2]: ... @@ -1833,8 +1833,8 @@ class Series(IndexOpsMixin[S1], ElementOpsMixin[S1], NDFrame): ) -> Series[Timedelta]: ... @overload def add( - self: Supports_ProtoAdd[_T_contra, S2], - other: _T_contra | Sequence[_T_contra], + self: Supports_ProtoAdd[S2_CT, S2], + other: S2_CT | Sequence[S2_CT], level: Level | None = None, fill_value: float | None = None, axis: int = 0, @@ -1952,8 +1952,7 @@ class Series(IndexOpsMixin[S1], ElementOpsMixin[S1], NDFrame): # pyright is unhappy without the above 3 overloads @overload def __radd__( - self: Supports_ProtoRAdd[_T_contra, S2], - other: _T_contra | Sequence[_T_contra], + self: Supports_ProtoRAdd[S2_CT, S2], other: S2_CT | Sequence[S2_CT] ) -> Series[S2]: ... @overload def __radd__(self: Series[S2_CT], other: SupportsAdd[S2_CT, S2]) -> Series[S2]: ... @@ -2067,8 +2066,8 @@ class Series(IndexOpsMixin[S1], ElementOpsMixin[S1], NDFrame): ) -> Series[Timedelta]: ... @overload def radd( - self: Supports_ProtoRAdd[_T_contra, S2], - other: _T_contra | Sequence[_T_contra], + self: Supports_ProtoRAdd[S2_CT, S2], + other: S2_CT | Sequence[S2_CT], level: Level | None = None, fill_value: float | None = None, axis: int = 0, @@ -2739,8 +2738,7 @@ class Series(IndexOpsMixin[S1], ElementOpsMixin[S1], NDFrame): ) -> Series[_str]: ... @overload def __rmul__( - self: Supports_ProtoRMul[_T_contra, S2], - other: _T_contra | Sequence[_T_contra], + self: Supports_ProtoRMul[_T_contra, S2], other: _T_contra | Sequence[_T_contra] ) -> Series[S2]: ... @overload def __rmul__(