diff --git a/pandas-stubs/_libs/tslibs/period.pyi b/pandas-stubs/_libs/tslibs/period.pyi index 7c5671e6e..a43d57407 100644 --- a/pandas-stubs/_libs/tslibs/period.pyi +++ b/pandas-stubs/_libs/tslibs/period.pyi @@ -14,7 +14,6 @@ from pandas import ( ) from pandas.core.series import ( OffsetSeries, - PeriodSeries, ) from typing_extensions import TypeAlias @@ -85,7 +84,9 @@ class Period(PeriodMixin): @overload def __sub__(self, other: PeriodIndex) -> Index: ... @overload - def __sub__(self, other: Series[Timedelta]) -> PeriodSeries: ... + def __sub__( + self, other: Series[Timedelta] + ) -> Series[Period]: ... # pyrefly: ignore[bad-specialization] @overload def __sub__(self, other: TimedeltaIndex) -> PeriodIndex: ... @overload @@ -95,7 +96,9 @@ class Period(PeriodMixin): @overload def __add__(self, other: Index) -> PeriodIndex: ... @overload - def __add__(self, other: OffsetSeries | Series[Timedelta]) -> PeriodSeries: ... + def __add__( + self, other: OffsetSeries | Series[Timedelta] + ) -> Series[Period]: ... # pyrefly: ignore[bad-specialization] # ignore[misc] here because we know all other comparisons # are False, so we use Literal[False] @overload @@ -103,7 +106,7 @@ class Period(PeriodMixin): @overload def __eq__(self, other: Index) -> np_1darray[np.bool]: ... # type: ignore[overload-overlap] @overload - def __eq__(self, other: PeriodSeries) -> Series[bool]: ... # type: ignore[overload-overlap] + def __eq__(self, other: Series[Period]) -> Series[bool]: ... # type: ignore[overload-overlap] @overload def __eq__(self, other: np_ndarray[ShapeT, np.object_]) -> np_ndarray[ShapeT, np.bool]: ... # type: ignore[overload-overlap] @overload @@ -113,7 +116,9 @@ class Period(PeriodMixin): @overload def __ge__(self, other: PeriodIndex) -> np_1darray[np.bool]: ... @overload - def __ge__(self, other: PeriodSeries) -> Series[bool]: ... + def __ge__( + self, other: Series[Period] # pyrefly: ignore[bad-specialization] + ) -> Series[bool]: ... @overload def __ge__( self, other: np_ndarray[ShapeT, np.object_] @@ -123,7 +128,9 @@ class Period(PeriodMixin): @overload def __gt__(self, other: PeriodIndex) -> np_1darray[np.bool]: ... @overload - def __gt__(self, other: PeriodSeries) -> Series[bool]: ... + def __gt__( + self, other: Series[Period] # pyrefly: ignore[bad-specialization] + ) -> Series[bool]: ... @overload def __gt__( self, other: np_ndarray[ShapeT, np.object_] @@ -133,7 +140,9 @@ class Period(PeriodMixin): @overload def __le__(self, other: PeriodIndex) -> np_1darray[np.bool]: ... @overload - def __le__(self, other: PeriodSeries) -> Series[bool]: ... + def __le__( + self, other: Series[Period] # pyrefly: ignore[bad-specialization] + ) -> Series[bool]: ... @overload def __le__( self, other: np_ndarray[ShapeT, np.object_] @@ -143,7 +152,9 @@ class Period(PeriodMixin): @overload def __lt__(self, other: PeriodIndex) -> np_1darray[np.bool]: ... @overload - def __lt__(self, other: PeriodSeries) -> Series[bool]: ... + def __lt__( + self, other: Series[Period] # pyrefly: ignore[bad-specialization] + ) -> Series[bool]: ... @overload def __lt__( self, other: np_ndarray[ShapeT, np.object_] @@ -155,7 +166,7 @@ class Period(PeriodMixin): @overload def __ne__(self, other: Index) -> np_1darray[np.bool]: ... # type: ignore[overload-overlap] @overload - def __ne__(self, other: PeriodSeries) -> Series[bool]: ... # type: ignore[overload-overlap] + def __ne__(self, other: Series[Period]) -> Series[bool]: ... # type: ignore[overload-overlap] @overload def __ne__(self, other: np_ndarray[ShapeT, np.object_]) -> np_ndarray[ShapeT, np.bool]: ... # type: ignore[overload-overlap] @overload diff --git a/pandas-stubs/core/indexes/accessors.pyi b/pandas-stubs/core/indexes/accessors.pyi index 175756f39..a9a5f8a54 100644 --- a/pandas-stubs/core/indexes/accessors.pyi +++ b/pandas-stubs/core/indexes/accessors.pyi @@ -28,13 +28,13 @@ from pandas.core.arrays import ( from pandas.core.base import NoNewAttributesMixin from pandas.core.frame import DataFrame from pandas.core.series import ( - PeriodSeries, Series, ) from typing_extensions import Never from pandas._libs.tslibs import BaseOffset from pandas._libs.tslibs.offsets import DateOffset +from pandas._libs.tslibs.period import Period from pandas._typing import ( S1, TimeAmbiguous, @@ -208,7 +208,7 @@ _DTNormalizeReturnType = TypeVar( ) _DTStrKindReturnType = TypeVar("_DTStrKindReturnType", bound=Series[str] | Index) _DTToPeriodReturnType = TypeVar( - "_DTToPeriodReturnType", bound=PeriodSeries | PeriodIndex + "_DTToPeriodReturnType", bound=Series[Period] | PeriodIndex ) class _DatetimeLikeNoTZMethods( @@ -385,7 +385,7 @@ class CombinedDatetimelikeProperties( str, Series[Timestamp], Series[str], - PeriodSeries, + Series[Period], ], _TimedeltaPropertiesNoRounding[Series[int], Series[float]], _PeriodProperties, @@ -400,7 +400,7 @@ class TimestampProperties( str, Series[Timestamp], Series[str], - PeriodSeries, + Series[Period], ] ): ... @@ -438,6 +438,8 @@ class _dtDescriptor(CombinedDatetimelikeProperties, Generic[S1]): @overload def __get__(self, instance: Series[Never], owner: Any) -> Never: ... @overload + def __get__(self, instance: Series[Period], owner: Any) -> PeriodProperties: ... + @overload def __get__( self, instance: Series[Timestamp], owner: Any ) -> TimestampProperties: ... diff --git a/pandas-stubs/core/series.pyi b/pandas-stubs/core/series.pyi index 2af21e434..2100b4382 100644 --- a/pandas-stubs/core/series.pyi +++ b/pandas-stubs/core/series.pyi @@ -70,10 +70,7 @@ from pandas.core.generic import NDFrame from pandas.core.groupby.generic import SeriesGroupBy from pandas.core.groupby.groupby import BaseGroupBy from pandas.core.indexers import BaseIndexer -from pandas.core.indexes.accessors import ( - PeriodProperties, - _dtDescriptor, -) +from pandas.core.indexes.accessors import _dtDescriptor from pandas.core.indexes.category import CategoricalIndex from pandas.core.indexes.datetimes import DatetimeIndex from pandas.core.indexes.interval import IntervalIndex @@ -363,7 +360,7 @@ class Series(IndexOpsMixin[S1], NDFrame): dtype: PeriodDtype = ..., name: Hashable = ..., copy: bool = ..., - ) -> PeriodSeries: ... + ) -> Series[Period]: ... @overload def __new__( cls, @@ -851,6 +848,8 @@ class Series(IndexOpsMixin[S1], NDFrame): @overload def diff(self: Series[Timedelta], periods: int = ...) -> Series[Timedelta]: ... # type: ignore[overload-overlap] @overload + def diff(self: Series[Period], periods: int = ...) -> OffsetSeries: ... # type: ignore[overload-overlap] + @overload def diff(self, periods: int = ...) -> Series[float]: ... def autocorr(self, lag: int = 1) -> float: ... @overload @@ -1234,8 +1233,7 @@ class Series(IndexOpsMixin[S1], NDFrame): Series[_str], Series, ]: ... - @property - def dt(self) -> _dtDescriptor[S1]: ... + dt = _dtDescriptor() @property def plot(self) -> PlotAccessor: ... sparse = ... @@ -1695,7 +1693,7 @@ class Series(IndexOpsMixin[S1], NDFrame): ), ) -> Series[Timedelta]: ... @overload - def __add__(self: Series[Timedelta], other: Period) -> PeriodSeries: ... + def __add__(self: Series[Timedelta], other: Period) -> Series[Period]: ... @overload def __add__(self: Series[bool], other: bool | Sequence[bool]) -> Series[bool]: ... @overload @@ -1822,7 +1820,7 @@ class Series(IndexOpsMixin[S1], NDFrame): level: Level | None = None, fill_value: float | None = None, axis: int = 0, - ) -> PeriodSeries: ... + ) -> Series[Period]: ... @overload def add( self: Series[bool], @@ -1957,7 +1955,7 @@ class Series(IndexOpsMixin[S1], NDFrame): ), ) -> Series[Timedelta]: ... @overload - def __radd__(self: Series[Timedelta], other: Period) -> PeriodSeries: ... + def __radd__(self: Series[Timedelta], other: Period) -> Series[Period]: ... @overload def __radd__(self: Series[bool], other: bool | Sequence[bool]) -> Series[bool]: ... @overload @@ -2084,7 +2082,7 @@ class Series(IndexOpsMixin[S1], NDFrame): level: Level | None = None, fill_value: float | None = None, axis: int = 0, - ) -> PeriodSeries: ... + ) -> Series[Period]: ... @overload def radd( self: Series[bool], @@ -3201,6 +3199,10 @@ class Series(IndexOpsMixin[S1], NDFrame): ), ) -> Series[Timedelta]: ... @overload + def __sub__( + self: Series[Period], other: Series[Period] | Period + ) -> Series[BaseOffset]: ... + @overload def sub( self: Series[Never], other: complex | NumListLike | Index[T_COMPLEX] | Series[T_COMPLEX], @@ -3372,6 +3374,14 @@ class Series(IndexOpsMixin[S1], NDFrame): axis: int = 0, ) -> Series[Timedelta]: ... @overload + def sub( + self: Series[Period], + other: Period | Sequence[Period] | PeriodIndex | Series[Period], + level: Level | None = None, + fill_value: float | None = None, + axis: int = 0, + ) -> Series[BaseOffset]: ... + @overload def __rsub__( self: Series[Never], other: ( @@ -3502,6 +3512,10 @@ class Series(IndexOpsMixin[S1], NDFrame): ), ) -> Series[Timedelta]: ... @overload + def __rsub__( + self: Series[Period], other: Series[Period] | Period + ) -> Series[BaseOffset]: ... + @overload def rsub( self: Series[Never], other: ( @@ -3682,6 +3696,14 @@ class Series(IndexOpsMixin[S1], NDFrame): axis: int = 0, ) -> Series[Timedelta]: ... @overload + def rsub( + self: Series[Period], + other: Period | Sequence[Period] | PeriodIndex | Series[Period], + level: Level | None = None, + fill_value: float | None = None, + axis: int = 0, + ) -> Series[BaseOffset]: ... + @overload def __truediv__( # type: ignore[overload-overlap] self: Series[Never], other: complex | NumListLike | Index | Series ) -> Series: ... @@ -4623,6 +4645,22 @@ class Series(IndexOpsMixin[S1], NDFrame): **kwargs, ) -> np_1darray[GenericT]: ... @overload + def to_numpy( + self: Series[Period], + dtype: None = None, + copy: bool = False, + na_value: Scalar = ..., + **kwargs, + ) -> np_1darray[np.object_]: ... + @overload + def to_numpy( + self: Series[Period], + dtype: type[np.int64], + copy: bool = False, + na_value: Scalar = ..., + **kwargs, + ) -> np_1darray[np.int64]: ... + @overload def to_numpy( # pyright: ignore[reportIncompatibleMethodOverride] self, dtype: DTypeLike | None = None, @@ -4716,15 +4754,9 @@ class _SeriesSubclassBase(Series[S1], Generic[S1, GenericT_co]): **kwargs, ) -> np_1darray: ... -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] - class OffsetSeries(_SeriesSubclassBase[BaseOffset, np.object_]): @overload # type: ignore[override] - def __radd__(self, other: Period) -> PeriodSeries: ... + def __radd__(self, other: Period) -> Series[Period]: ... @overload def __radd__( # pyright: ignore[reportIncompatibleMethodOverride] self, other: BaseOffset diff --git a/pyproject.toml b/pyproject.toml index 5734743ff..237cffdf3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -65,7 +65,7 @@ scipy = { version = ">=1.9.1", python = "<3.14" } scipy-stubs = ">=1.15.3.0" SQLAlchemy = ">=2.0.39" types-python-dateutil = ">=2.8.19" -beautifulsoup4 = ">=4.12.2" +beautifulsoup4 = "<=4.13.5" html5lib = ">=1.1" python-calamine = ">=0.2.0" diff --git a/tests/series/arithmetic/period/__init__.py b/tests/series/arithmetic/period/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/series/arithmetic/period/test_sub.py b/tests/series/arithmetic/period/test_sub.py new file mode 100644 index 000000000..930141a9c --- /dev/null +++ b/tests/series/arithmetic/period/test_sub.py @@ -0,0 +1,20 @@ +"""Test module for subtraction operation on Series[Period].""" + +import pandas as pd +from typing_extensions import assert_type + +from pandas._libs.tslibs.offsets import BaseOffset + +from tests import check + + +def test_sub() -> None: + """Test sub method for pd.Series[pd.Period].""" + p = pd.Period("2012-1-1", freq="D") + sr = pd.Series([pd.Period("2012-1-1", freq="D")]) + + check(assert_type(sr - sr, "pd.Series[BaseOffset]"), pd.Series, BaseOffset) + check(assert_type(p - sr, "pd.Series[BaseOffset]"), pd.Series, BaseOffset) + check(assert_type(sr - p, "pd.Series[BaseOffset]"), pd.Series, BaseOffset) + check(assert_type(sr.sub(p), "pd.Series[BaseOffset]"), pd.Series, BaseOffset) + check(assert_type(sr.rsub(p), "pd.Series[BaseOffset]"), pd.Series, BaseOffset) diff --git a/tests/test_scalars.py b/tests/test_scalars.py index e537f0d0c..3eb8f7cc4 100644 --- a/tests/test_scalars.py +++ b/tests/test_scalars.py @@ -3,7 +3,6 @@ import datetime import datetime as dt from typing import ( - TYPE_CHECKING, Any, Literal, Optional, @@ -38,15 +37,6 @@ Day, ) -if TYPE_CHECKING: - from pandas.core.series import ( - OffsetSeries, - PeriodSeries, - ) -else: - PeriodSeries: TypeAlias = pd.Series - OffsetSeries: TypeAlias = pd.Series - if not PD_LTE_23: from pandas.errors import Pandas4Warning # type: ignore[attr-defined] # pyright: ignore # isort: skip else: @@ -1861,7 +1851,7 @@ def test_period_add_subtract() -> None: as_td_series = pd.Series(pd.timedelta_range(scale, scale, freq="D")) check(assert_type(as_td_series, "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) as_period_series = pd.Series(as_period_index) - check(assert_type(as_period_series, PeriodSeries), pd.Series, pd.Period) + check(assert_type(as_period_series, "pd.Series[pd.Period]"), pd.Series, pd.Period) as_timedelta_idx = pd.timedelta_range(scale, scale, freq="D") as_nat = pd.NaT @@ -1876,12 +1866,12 @@ def test_period_add_subtract() -> None: # https://github.com/pandas-dev/pandas/issues/50162 check(assert_type(p + offset_index, pd.PeriodIndex), pd.Index) - check(assert_type(p + as_td_series, PeriodSeries), pd.Series, pd.Period) + check(assert_type(p + as_td_series, "pd.Series[pd.Period]"), pd.Series, pd.Period) check(assert_type(p + as_timedelta_idx, pd.PeriodIndex), pd.PeriodIndex) check(assert_type(p + as_nat, NaTType), NaTType) offset_series = as_period_series - as_period_series - check(assert_type(offset_series, OffsetSeries), pd.Series) - check(assert_type(p + offset_series, PeriodSeries), pd.Series, pd.Period) + check(assert_type(offset_series, "pd.Series[BaseOffset]"), pd.Series) + check(assert_type(p + offset_series, "pd.Series[pd.Period]"), pd.Series, pd.Period) check(assert_type(p - as_pd_td, pd.Period), pd.Period) check(assert_type(p - as_dt_td, pd.Period), pd.Period) check(assert_type(p - as_np_td, pd.Period), pd.Period) @@ -1889,7 +1879,7 @@ def test_period_add_subtract() -> None: check(assert_type(p - as_int, pd.Period), pd.Period) check(assert_type(offset_index, pd.Index), pd.Index) check(assert_type(p - as_period, BaseOffset), Day) - check(assert_type(p - as_td_series, PeriodSeries), pd.Series, pd.Period) + check(assert_type(p - as_td_series, "pd.Series[pd.Period]"), pd.Series, pd.Period) check(assert_type(p - as_timedelta_idx, pd.PeriodIndex), pd.PeriodIndex) check(assert_type(p - as_nat, NaTType), NaTType) check(assert_type(p - p.freq, pd.Period), pd.Period) @@ -1912,7 +1902,7 @@ def test_period_add_subtract() -> None: check(assert_type(as_int + p, pd.Period), pd.Period) check(assert_type(p.__radd__(as_int), pd.Period), pd.Period) - check(assert_type(as_td_series + p, PeriodSeries), pd.Series, pd.Period) + check(assert_type(as_td_series + p, "pd.Series[pd.Period]"), pd.Series, pd.Period) check(assert_type(as_timedelta_idx + p, pd.PeriodIndex), pd.PeriodIndex) diff --git a/tests/test_timefuncs.py b/tests/test_timefuncs.py index 354be303b..5980af32f 100644 --- a/tests/test_timefuncs.py +++ b/tests/test_timefuncs.py @@ -50,10 +50,9 @@ ) if TYPE_CHECKING: - from pandas.core.series import ( # noqa: F401 + from pandas.core.series import ( IntervalSeries, OffsetSeries, - PeriodSeries, ) if not PD_LTE_23: @@ -392,7 +391,9 @@ def test_series_dt_accessors() -> None: check(assert_type(s0.dt.tz, Optional[dt.tzinfo]), type(None)) check(assert_type(s0.dt.freq, Optional[str]), str) check(assert_type(s0.dt.isocalendar(), pd.DataFrame), pd.DataFrame) - check(assert_type(s0.dt.to_period("D"), "PeriodSeries"), pd.Series, pd.Period) + check( + assert_type(s0.dt.to_period("D"), "pd.Series[pd.Period]"), pd.Series, pd.Period + ) with pytest_warns_bounded( FutureWarning,