From b34d0e39f9671bb91baa45fff9c2c74c4575a88f Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Wed, 17 Sep 2025 22:17:23 +0200 Subject: [PATCH] fix(series): #1372 cumprod --- pandas-stubs/core/series.pyi | 43 +++------------------- pyproject.toml | 4 +- tests/series/test_series.py | 18 +++------ tests/series/window/__init__.py | 0 tests/series/window/test_window_any.py | 11 ++++++ tests/series/window/test_window_complex.py | 15 ++++++++ tests/series/window/test_window_float.py | 11 ++++++ tests/series/window/test_window_int.py | 11 ++++++ tests/series/window/test_window_str.py | 10 +++++ 9 files changed, 71 insertions(+), 52 deletions(-) create mode 100644 tests/series/window/__init__.py create mode 100644 tests/series/window/test_window_any.py create mode 100644 tests/series/window/test_window_complex.py create mode 100644 tests/series/window/test_window_float.py create mode 100644 tests/series/window/test_window_int.py create mode 100644 tests/series/window/test_window_str.py diff --git a/pandas-stubs/core/series.pyi b/pandas-stubs/core/series.pyi index ae838a251..9dd88d0ee 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 @@ -3503,17 +3507,8 @@ class Series(IndexOpsMixin[S1], NDFrame): *args: Any, **kwargs: Any, ) -> Series[S1]: ... - @overload - def cumprod( - self: Series[_str], - axis: AxisIndex = ..., - skipna: _bool = ..., - *args: Any, - **kwargs: Any, - ) -> Never: ... - @overload def cumprod( - self, + self: SupportsGetItem[Scalar, _SupportsMul[S1]], axis: AxisIndex = ..., skipna: _bool = ..., *args: Any, @@ -3900,13 +3895,6 @@ class TimestampSeries(_SeriesSubclassBase[Timestamp, np.datetime64]): **kwargs: Any, ) -> Timedelta: ... def diff(self, periods: int = ...) -> TimedeltaSeries: ... # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] - def cumprod( - self, - axis: AxisIndex = ..., - skipna: _bool = ..., - *args: Any, - **kwargs: Any, - ) -> Never: ... class TimedeltaSeries(_SeriesSubclassBase[Timedelta, np.timedelta64]): # ignores needed because of mypy @@ -4012,39 +4000,18 @@ class TimedeltaSeries(_SeriesSubclassBase[Timedelta, np.timedelta64]): *args: Any, **kwargs: Any, ) -> TimedeltaSeries: ... - def cumprod( - 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( - self, - axis: AxisIndex = ..., - skipna: _bool = ..., - *args: Any, - **kwargs: Any, - ) -> Never: ... class IntervalSeries( _SeriesSubclassBase[Interval[_OrderableT], np.object_], Generic[_OrderableT] diff --git a/pyproject.toml b/pyproject.toml index bb953e2b1..47c13a987 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,13 +36,13 @@ types-pytz = ">= 2022.1.1" numpy = ">= 1.23.5" [tool.poetry.group.dev.dependencies] -mypy = "1.17.1" +mypy = "1.18.1" pandas = "2.3.2" pyarrow = ">=10.0.1" pytest = ">=7.1.2" pyright = ">=1.1.405" ty = ">=0.0.1a20" -pyrefly = ">=0.32.0" +pyrefly = ">=0.33.0" poethepoet = ">=0.16.5" loguru = ">=0.6.0" typing-extensions = ">=4.4.0" diff --git a/tests/series/test_series.py b/tests/series/test_series.py index bbb72ce46..b0dc4f761 100644 --- a/tests/series/test_series.py +++ b/tests/series/test_series.py @@ -3948,24 +3948,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: diff --git a/tests/series/window/__init__.py b/tests/series/window/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/series/window/test_window_any.py b/tests/series/window/test_window_any.py new file mode 100644 index 000000000..f833b57a4 --- /dev/null +++ b/tests/series/window/test_window_any.py @@ -0,0 +1,11 @@ +import numpy as np +import pandas as pd +from typing_extensions import assert_type + +from tests import check + +series = pd.DataFrame({"A": [1.0, float("nan"), 2.0]})["A"] + + +def test_window_any() -> None: + check(assert_type(series.cumprod(), pd.Series), pd.Series, np.floating) diff --git a/tests/series/window/test_window_complex.py b/tests/series/window/test_window_complex.py new file mode 100644 index 000000000..a7192a3ef --- /dev/null +++ b/tests/series/window/test_window_complex.py @@ -0,0 +1,15 @@ +import numpy as np +import pandas as pd +from typing_extensions import assert_type + +from tests import check + +series = pd.Series([3j, 3 + 4j, 2j]) + + +def test_window_complex() -> None: + check( + assert_type(series.cumprod(), "pd.Series[complex]"), + pd.Series, + np.complexfloating, + ) diff --git a/tests/series/window/test_window_float.py b/tests/series/window/test_window_float.py new file mode 100644 index 000000000..9c1d5241c --- /dev/null +++ b/tests/series/window/test_window_float.py @@ -0,0 +1,11 @@ +import numpy as np +import pandas as pd +from typing_extensions import assert_type + +from tests import check + +series = pd.Series([3.0, float("nan"), 2.0]) + + +def test_window_float() -> None: + check(assert_type(series.cumprod(), "pd.Series[float]"), pd.Series, np.floating) diff --git a/tests/series/window/test_window_int.py b/tests/series/window/test_window_int.py new file mode 100644 index 000000000..2dde405cf --- /dev/null +++ b/tests/series/window/test_window_int.py @@ -0,0 +1,11 @@ +import numpy as np +import pandas as pd +from typing_extensions import assert_type + +from tests import check + +series = pd.Series([3, 1, 2]) + + +def test_window_int() -> None: + check(assert_type(series.cumprod(), "pd.Series[int]"), pd.Series, np.integer) diff --git a/tests/series/window/test_window_str.py b/tests/series/window/test_window_str.py new file mode 100644 index 000000000..ba1467b65 --- /dev/null +++ b/tests/series/window/test_window_str.py @@ -0,0 +1,10 @@ +import pandas as pd + +from tests import TYPE_CHECKING_INVALID_USAGE + +series = pd.Series(["1", "a", "🐼"]) + + +def test_window_str() -> None: + if TYPE_CHECKING_INVALID_USAGE: + series.cumprod() # type: ignore[misc] # pyright: ignore[reportAttributeAccessIssue]