diff --git a/pandas-stubs/core/series.pyi b/pandas-stubs/core/series.pyi index 9c255c03b..fa0013fe2 100644 --- a/pandas-stubs/core/series.pyi +++ b/pandas-stubs/core/series.pyi @@ -210,6 +210,10 @@ from pandas.plotting import PlotAccessor class _SupportsAdd(Protocol[_T_co]): def __add__(self, value: Self, /) -> _T_co: ... +@type_check_only +class _SupportsMul(Protocol[_T_co]): + def __mul__(self, value: Self, /) -> _T_co: ... + class _iLocIndexerSeries(_iLocIndexer, Generic[S1]): # get item @overload @@ -3821,23 +3825,23 @@ class Series(IndexOpsMixin[S1], NDFrame): ) -> Series[S1]: ... @overload def cumprod( - self: Series[_str], + self: Series[Never], axis: AxisIndex = ..., skipna: _bool = ..., *args: Any, **kwargs: Any, - ) -> Never: ... + ) -> Series: ... @overload def cumprod( - self: Series[Timestamp], + self: Series[bool], axis: AxisIndex = ..., skipna: _bool = ..., *args: Any, **kwargs: Any, - ) -> Never: ... + ) -> Series[int]: ... @overload def cumprod( - self, + self: SupportsGetItem[Scalar, _SupportsMul[S1]], axis: AxisIndex = ..., skipna: _bool = ..., *args: Any, @@ -4369,39 +4373,18 @@ class TimedeltaSeries(_SeriesSubclassBase[Timedelta, np.timedelta64]): *args: Any, **kwargs: Any, ) -> TimedeltaSeries: ... - def cumprod( # pyrefly: ignore - self, - axis: AxisIndex = ..., - skipna: _bool = ..., - *args: Any, - **kwargs: Any, - ) -> Never: ... class PeriodSeries(_SeriesSubclassBase[Period, np.object_]): @property def dt(self) -> PeriodProperties: ... # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] def __sub__(self, other: PeriodSeries) -> OffsetSeries: ... # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] def diff(self, periods: int = ...) -> OffsetSeries: ... # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] - def cumprod( - self, - axis: AxisIndex = ..., - skipna: _bool = ..., - *args: Any, - **kwargs: Any, - ) -> Never: ... class OffsetSeries(_SeriesSubclassBase[BaseOffset, np.object_]): @overload # type: ignore[override] def __radd__(self, other: Period) -> PeriodSeries: ... @overload def __radd__(self, other: BaseOffset) -> OffsetSeries: ... - def cumprod( # pyrefly: ignore - self, - axis: AxisIndex = ..., - skipna: _bool = ..., - *args: Any, - **kwargs: Any, - ) -> Never: ... class IntervalSeries( _SeriesSubclassBase[Interval[_OrderableT], np.object_], Generic[_OrderableT] diff --git a/tests/series/test_cumul.py b/tests/series/test_cumul.py new file mode 100644 index 000000000..5c1980a46 --- /dev/null +++ b/tests/series/test_cumul.py @@ -0,0 +1,51 @@ +import numpy as np +import pandas as pd +from typing_extensions import assert_type + +from tests import ( + TYPE_CHECKING_INVALID_USAGE, + check, +) + + +def test_cumul_any_float() -> None: + series = pd.DataFrame({"A": [1.0, float("nan"), 2.0]})["A"] + check(assert_type(series.cumprod(), pd.Series), pd.Series, np.floating) + + +def test_cumul_bool() -> None: + series = pd.Series([True, False, True]) + check(assert_type(series.cumprod(), "pd.Series[int]"), pd.Series, np.integer) + + +def test_cumul_int() -> None: + series = pd.Series([3, 1, 2]) + check(assert_type(series.cumprod(), "pd.Series[int]"), pd.Series, np.integer) + + +def test_cumul_float() -> None: + series = pd.Series([3.0, float("nan"), 2.0]) + check(assert_type(series.cumprod(), "pd.Series[float]"), pd.Series, np.floating) + + +def test_cumul_complex() -> None: + series = pd.Series([3j, 3 + 4j, 2j]) + check( + assert_type(series.cumprod(), "pd.Series[complex]"), + pd.Series, + np.complexfloating, + ) + + +def test_cumul_str() -> None: + series = pd.Series(["1", "a", "🐼"]) + if TYPE_CHECKING_INVALID_USAGE: + series.cumprod() # type: ignore[misc] # pyright: ignore[reportAttributeAccessIssue] + + +def test_cumul_ts() -> None: + series = pd.Series(pd.to_datetime(["2025-09-18", "2025-09-18", "2025-09-18"])) + check(assert_type(series, "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp) + + if TYPE_CHECKING_INVALID_USAGE: + series.cumprod() # type: ignore[misc] # pyright: ignore[reportAttributeAccessIssue] diff --git a/tests/series/test_series.py b/tests/series/test_series.py index b6b567fe4..e5b2f5465 100644 --- a/tests/series/test_series.py +++ b/tests/series/test_series.py @@ -3946,24 +3946,18 @@ def test_timedelta_index_cumprod() -> None: offset_series = as_period_series - as_period_series if TYPE_CHECKING_INVALID_USAGE: - assert_type(pd.Series(["a", "b"]).cumprod(), Never) + offset_series.cumprod() # type: ignore[misc] # pyright: ignore[reportAttributeAccessIssue] if TYPE_CHECKING_INVALID_USAGE: - assert_type(offset_series.cumprod(), Never) + pd.Series([pd.Timedelta(0), pd.Timedelta(1)]).cumprod() # type: ignore[misc] # pyright: ignore[reportAttributeAccessIssue] if TYPE_CHECKING_INVALID_USAGE: - assert_type(pd.Series([pd.Timedelta(0), pd.Timedelta(1)]).cumprod(), Never) + pd.Series( # type: ignore[misc] + [pd.Timestamp("2024-04-29"), pd.Timestamp("2034-08-28")] + ).cumprod() # pyright: ignore[reportAttributeAccessIssue] if TYPE_CHECKING_INVALID_USAGE: - assert_type( - pd.Series( - [pd.Timestamp("2024-04-29"), pd.Timestamp("2034-08-28")] - ).cumprod(), - Never, - ) - - if TYPE_CHECKING_INVALID_USAGE: - assert_type(as_period_series.cumprod(), Never) + as_period_series.cumprod() # type: ignore[misc] # pyright: ignore[reportAttributeAccessIssue] def test_series_str_methods() -> None: