From cbbd3720b025408dc85709b2b84e147f1f7b3273 Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Sun, 13 Jul 2025 08:23:15 +0200 Subject: [PATCH 1/2] refactor: only drop TimestampSeries https://github.com/pandas-dev/pandas-stubs/pull/1273#pullrequestreview-3013726071 --- docs/philosophy.md | 16 +- pandas-stubs/_libs/interval.pyi | 11 +- pandas-stubs/_libs/tslibs/timedeltas.pyi | 7 +- pandas-stubs/_libs/tslibs/timestamps.pyi | 19 +- pandas-stubs/core/indexes/accessors.pyi | 66 +++++- pandas-stubs/core/indexes/datetimes.pyi | 8 +- pandas-stubs/core/indexes/interval.pyi | 3 +- pandas-stubs/core/reshape/tile.pyi | 13 +- pandas-stubs/core/series.pyi | 253 ++++++++++++++++------- pandas-stubs/core/tools/datetimes.pyi | 7 +- pyproject.toml | 2 +- tests/test_frame.py | 14 +- tests/test_pandas.py | 20 +- tests/test_scalars.py | 24 ++- tests/test_series.py | 134 ++++++------ tests/test_timefuncs.py | 183 ++++++++++------ 16 files changed, 494 insertions(+), 286 deletions(-) diff --git a/docs/philosophy.md b/docs/philosophy.md index 853c516e1..c19f1e1b9 100644 --- a/docs/philosophy.md +++ b/docs/philosophy.md @@ -36,6 +36,8 @@ This also allows type checking for operations on series that contain date/time d the following example that creates two series of datetimes with corresponding arithmetic. ```python +import pandas as pd + s1 = pd.Series(pd.to_datetime(["2022-05-01", "2022-06-01"])) reveal_type(s1) s2 = pd.Series(pd.to_datetime(["2022-05-15", "2022-06-15"])) @@ -46,19 +48,21 @@ ssum = s1 + s2 reveal_type(ssum) ``` -The above code (without the `reveal_type()` statements) will raise an `Exception` on the computation of `ssum` because it is +The above code (without the `reveal_type()` statements) will get a `Never` +on the computation of `ssum` because it is inappropriate to add two series containing `Timestamp` values. The types will be revealed as follows: ```text -ttest.py:4: note: Revealed type is "pandas.core.series.TimestampSeries" -ttest.py:6: note: Revealed type is "pandas.core.series.TimestampSeries" +ttest.py:4: note: Revealed type is "pandas.core.series.Series[pandas._libs.tslibs.timestamps.Timestamp]" +ttest.py:6: note: Revealed type is "pandas.core.series.Series[pandas._libs.tslibs.timestamps.Timestamp]" ttest.py:8: note: Revealed type is "pandas.core.series.TimedeltaSeries" -ttest.py:10: note: Revealed type is "builtins.Exception" +ttest.py:9: error: Need type annotation for "ssum" [var-annotated] +ttest.py:10: note: Revealed type is "Never" ``` -The type `TimestampSeries` is the result of creating a series from `pd.to_datetime()`, while -the type `TimedeltaSeries` is the result of subtracting two `TimestampSeries` as well as +The type `Series[Timestamp]` is the result of creating a series from `pd.to_datetime()`, while +the type `TimedeltaSeries` is the result of subtracting two `Series[Timestamp]` as well as the result of `pd.to_timedelta()`. ### Interval is Generic diff --git a/pandas-stubs/_libs/interval.pyi b/pandas-stubs/_libs/interval.pyi index fdb0a398c..613eb06ba 100644 --- a/pandas-stubs/_libs/interval.pyi +++ b/pandas-stubs/_libs/interval.pyi @@ -13,10 +13,7 @@ from pandas import ( Timedelta, Timestamp, ) -from pandas.core.series import ( - TimedeltaSeries, - TimestampSeries, -) +from pandas.core.series import TimedeltaSeries from pandas._typing import ( IntervalClosedType, @@ -174,7 +171,7 @@ class Interval(IntervalMixin, Generic[_OrderableT]): @overload def __gt__( self, - other: Series[int] | Series[float] | TimestampSeries | TimedeltaSeries, + other: Series[int] | Series[float] | Series[Timestamp] | TimedeltaSeries, ) -> Series[bool]: ... @overload def __lt__(self, other: Interval[_OrderableT]) -> bool: ... @@ -183,7 +180,7 @@ class Interval(IntervalMixin, Generic[_OrderableT]): @overload def __lt__( self, - other: Series[int] | Series[float] | TimestampSeries | TimedeltaSeries, + other: Series[int] | Series[float] | Series[Timestamp] | TimedeltaSeries, ) -> Series[bool]: ... @overload def __ge__(self, other: Interval[_OrderableT]) -> bool: ... @@ -192,7 +189,7 @@ class Interval(IntervalMixin, Generic[_OrderableT]): @overload def __ge__( self, - other: Series[int] | Series[float] | TimestampSeries | TimedeltaSeries, + other: Series[int] | Series[float] | Series[Timestamp] | TimedeltaSeries, ) -> Series[bool]: ... @overload def __le__(self, other: Interval[_OrderableT]) -> bool: ... diff --git a/pandas-stubs/_libs/tslibs/timedeltas.pyi b/pandas-stubs/_libs/tslibs/timedeltas.pyi index 01a17e1df..798b87a9d 100644 --- a/pandas-stubs/_libs/tslibs/timedeltas.pyi +++ b/pandas-stubs/_libs/tslibs/timedeltas.pyi @@ -17,10 +17,7 @@ from pandas import ( Series, TimedeltaIndex, ) -from pandas.core.series import ( - TimedeltaSeries, - TimestampSeries, -) +from pandas.core.series import TimedeltaSeries from typing_extensions import ( Self, TypeAlias, @@ -167,7 +164,7 @@ class Timedelta(timedelta): other: TimedeltaSeries, ) -> TimedeltaSeries: ... @overload - def __add__(self, other: TimestampSeries) -> TimestampSeries: ... + def __add__(self, other: Series[Timestamp]) -> Series[Timestamp]: ... @overload def __radd__(self, other: np.datetime64) -> Timestamp: ... @overload diff --git a/pandas-stubs/_libs/tslibs/timestamps.pyi b/pandas-stubs/_libs/tslibs/timestamps.pyi index b986c186a..3b991d7ef 100644 --- a/pandas-stubs/_libs/tslibs/timestamps.pyi +++ b/pandas-stubs/_libs/tslibs/timestamps.pyi @@ -25,7 +25,6 @@ from pandas import ( from pandas.core.series import ( Series, TimedeltaSeries, - TimestampSeries, ) from typing_extensions import ( Never, @@ -172,7 +171,7 @@ class Timestamp(datetime, SupportsIndex): self, other: DatetimeIndex | npt.NDArray[np.datetime64] ) -> np_ndarray_bool: ... @overload - def __le__(self, other: TimestampSeries) -> Series[bool]: ... + def __le__(self, other: Series[Timestamp]) -> Series[bool]: ... @overload # type: ignore[override] def __lt__(self, other: Timestamp | datetime | np.datetime64) -> bool: ... # type: ignore[misc] @overload @@ -180,7 +179,7 @@ class Timestamp(datetime, SupportsIndex): self, other: DatetimeIndex | npt.NDArray[np.datetime64] ) -> np_ndarray_bool: ... @overload - def __lt__(self, other: TimestampSeries) -> Series[bool]: ... + def __lt__(self, other: Series[Timestamp]) -> Series[bool]: ... @overload # type: ignore[override] def __ge__(self, other: Timestamp | datetime | np.datetime64) -> bool: ... # type: ignore[misc] @overload @@ -188,7 +187,7 @@ class Timestamp(datetime, SupportsIndex): self, other: DatetimeIndex | npt.NDArray[np.datetime64] ) -> np_ndarray_bool: ... @overload - def __ge__(self, other: TimestampSeries) -> Series[bool]: ... + def __ge__(self, other: Series[Timestamp]) -> Series[bool]: ... @overload # type: ignore[override] def __gt__(self, other: Timestamp | datetime | np.datetime64) -> bool: ... # type: ignore[misc] @overload @@ -196,7 +195,7 @@ class Timestamp(datetime, SupportsIndex): self, other: DatetimeIndex | npt.NDArray[np.datetime64] ) -> np_ndarray_bool: ... @overload - def __gt__(self, other: TimestampSeries) -> Series[bool]: ... + def __gt__(self, other: Series[Timestamp]) -> Series[bool]: ... # error: Signature of "__add__" incompatible with supertype "date"/"datetime" @overload # type: ignore[override] def __add__( @@ -205,7 +204,7 @@ class Timestamp(datetime, SupportsIndex): @overload def __add__(self, other: timedelta | np.timedelta64 | Tick) -> Self: ... @overload - def __add__(self, other: TimedeltaSeries) -> TimestampSeries: ... + def __add__(self, other: TimedeltaSeries) -> Series[Timestamp]: ... @overload def __add__(self, other: TimedeltaIndex) -> DatetimeIndex: ... @overload @@ -224,9 +223,9 @@ class Timestamp(datetime, SupportsIndex): @overload def __sub__(self, other: TimedeltaIndex) -> DatetimeIndex: ... @overload - def __sub__(self, other: TimedeltaSeries) -> TimestampSeries: ... + def __sub__(self, other: TimedeltaSeries) -> Series[Timestamp]: ... @overload - def __sub__(self, other: TimestampSeries) -> TimedeltaSeries: ... + def __sub__(self, other: Series[Timestamp]) -> TimedeltaSeries: ... @overload def __sub__( self, other: npt.NDArray[np.timedelta64] @@ -234,7 +233,7 @@ class Timestamp(datetime, SupportsIndex): @overload def __eq__(self, other: Timestamp | datetime | np.datetime64) -> bool: ... # type: ignore[overload-overlap] # pyright: ignore[reportOverlappingOverload] @overload - def __eq__(self, other: TimestampSeries) -> Series[bool]: ... # type: ignore[overload-overlap] + def __eq__(self, other: Series[Timestamp]) -> Series[bool]: ... # type: ignore[overload-overlap] @overload def __eq__(self, other: npt.NDArray[np.datetime64] | Index) -> np_ndarray_bool: ... # type: ignore[overload-overlap] @overload @@ -242,7 +241,7 @@ class Timestamp(datetime, SupportsIndex): @overload def __ne__(self, other: Timestamp | datetime | np.datetime64) -> bool: ... # type: ignore[overload-overlap] # pyright: ignore[reportOverlappingOverload] @overload - def __ne__(self, other: TimestampSeries) -> Series[bool]: ... # type: ignore[overload-overlap] + def __ne__(self, other: Series[Timestamp]) -> Series[bool]: ... # type: ignore[overload-overlap] @overload def __ne__(self, other: npt.NDArray[np.datetime64] | Index) -> np_ndarray_bool: ... # type: ignore[overload-overlap] @overload diff --git a/pandas-stubs/core/indexes/accessors.pyi b/pandas-stubs/core/indexes/accessors.pyi index b5db84dba..70ce8e090 100644 --- a/pandas-stubs/core/indexes/accessors.pyi +++ b/pandas-stubs/core/indexes/accessors.pyi @@ -4,9 +4,11 @@ from datetime import ( tzinfo as _tzinfo, ) from typing import ( + Any, Generic, Literal, TypeVar, + overload, ) import numpy as np @@ -17,6 +19,7 @@ from pandas import ( PeriodIndex, Timedelta, TimedeltaIndex, + Timestamp, ) from pandas.core.accessor import PandasDelegate from pandas.core.arrays import ( @@ -29,12 +32,13 @@ from pandas.core.series import ( PeriodSeries, Series, TimedeltaSeries, - TimestampSeries, ) +from typing_extensions import Never from pandas._libs.tslibs import BaseOffset from pandas._libs.tslibs.offsets import DateOffset from pandas._typing import ( + S1, TimeAmbiguous, TimeNonexistent, TimestampConvention, @@ -155,14 +159,13 @@ class _DatetimeLikeOps( ], ): ... -# Ideally, the rounding methods would return TimestampSeries when `Series.dt.method` +# Ideally, the rounding methods would return Series[Timestamp] when `Series.dt.method` # is invoked, but because of how Series.dt is hooked in and that we may not know the # type of the series, we don't know which kind of series was ...ed # in to the dt accessor _DTTimestampTimedeltaReturnType = TypeVar( - "_DTTimestampTimedeltaReturnType", - bound=Series | TimestampSeries | TimedeltaSeries | DatetimeIndex | TimedeltaIndex, + "_DTTimestampTimedeltaReturnType", bound=Series | DatetimeIndex | TimedeltaIndex ) class _DatetimeRoundingMethods(Generic[_DTTimestampTimedeltaReturnType]): @@ -198,7 +201,7 @@ class _DatetimeRoundingMethods(Generic[_DTTimestampTimedeltaReturnType]): ) -> _DTTimestampTimedeltaReturnType: ... _DTNormalizeReturnType = TypeVar( - "_DTNormalizeReturnType", TimestampSeries, DatetimeIndex + "_DTNormalizeReturnType", Series[Timestamp], DatetimeIndex ) _DTStrKindReturnType = TypeVar("_DTStrKindReturnType", bound=Series[str] | Index) _DTToPeriodReturnType = TypeVar( @@ -320,7 +323,7 @@ class TimedeltaProperties( def as_unit(self, unit: TimeUnit) -> TimedeltaSeries: ... _PeriodDTReturnTypes = TypeVar( - "_PeriodDTReturnTypes", bound=TimestampSeries | DatetimeIndex + "_PeriodDTReturnTypes", bound=Series[Timestamp] | DatetimeIndex ) _PeriodIntReturnTypes = TypeVar("_PeriodIntReturnTypes", bound=Series[int] | Index[int]) _PeriodStrReturnTypes = TypeVar("_PeriodStrReturnTypes", bound=Series[str] | Index) @@ -363,7 +366,7 @@ class PeriodIndexFieldOps( class PeriodProperties( Properties, _PeriodProperties[ - TimestampSeries, Series[int], Series[str], DatetimeArray, PeriodArray + Series[Timestamp], Series[int], Series[str], DatetimeArray, PeriodArray ], _DatetimeFieldOps[Series[int]], _IsLeapYearProperty, @@ -377,7 +380,7 @@ class CombinedDatetimelikeProperties( Series[dt.date], Series[dt.time], str, - TimestampSeries, + Series[Timestamp], Series[str], PeriodSeries, ], @@ -388,11 +391,11 @@ class TimestampProperties( DatetimeProperties[ Series[int], Series[bool], - TimestampSeries, + Series[Timestamp], Series[dt.date], Series[dt.time], str, - TimestampSeries, + Series[Timestamp], Series[str], PeriodSeries, ] @@ -427,3 +430,46 @@ class TimedeltaIndexProperties( _TimedeltaPropertiesNoRounding[Index, Index], _DatetimeRoundingMethods[TimedeltaIndex], ): ... + +class _dtDescriptor(CombinedDatetimelikeProperties, Generic[S1]): + @overload + def __get__(self, instance: Series[Never], owner: Any) -> Never: ... + @overload + def __get__( + self, instance: Series[Timestamp], owner: Any + ) -> TimestampProperties: ... + @overload + def __get__( + self, instance: Series[S1], owner: Any + ) -> CombinedDatetimelikeProperties: ... + def round( + self, + freq: str | BaseOffset | None, + ambiguous: Literal["raise", "infer", "NaT"] | bool | np_ndarray_bool = ..., + nonexistent: ( + Literal["shift_forward", "shift_backward", "NaT", "raise"] + | timedelta + | Timedelta + ) = ..., + ) -> Series[S1]: ... + def floor( + self, + freq: str | BaseOffset | None, + ambiguous: Literal["raise", "infer", "NaT"] | bool | np_ndarray_bool = ..., + nonexistent: ( + Literal["shift_forward", "shift_backward", "NaT", "raise"] + | timedelta + | Timedelta + ) = ..., + ) -> Series[S1]: ... + def ceil( + self, + freq: str | BaseOffset | None, + ambiguous: Literal["raise", "infer", "NaT"] | bool | np_ndarray_bool = ..., + nonexistent: ( + Literal["shift_forward", "shift_backward", "NaT", "raise"] + | timedelta + | Timedelta + ) = ..., + ) -> Series[S1]: ... + def as_unit(self, unit: TimeUnit) -> Series[S1]: ... diff --git a/pandas-stubs/core/indexes/datetimes.pyi b/pandas-stubs/core/indexes/datetimes.pyi index 40a7102cd..589390856 100644 --- a/pandas-stubs/core/indexes/datetimes.pyi +++ b/pandas-stubs/core/indexes/datetimes.pyi @@ -23,8 +23,8 @@ from pandas import ( from pandas.core.indexes.accessors import DatetimeIndexProperties from pandas.core.indexes.datetimelike import DatetimeTimedeltaMixin from pandas.core.series import ( + Series, TimedeltaSeries, - TimestampSeries, ) from typing_extensions import Self @@ -60,13 +60,13 @@ class DatetimeIndex(DatetimeTimedeltaMixin[Timestamp], DatetimeIndexProperties): # various ignores needed for mypy, as we do want to restrict what can be used in # arithmetic for these types @overload - def __add__(self, other: TimedeltaSeries) -> TimestampSeries: ... + def __add__(self, other: TimedeltaSeries) -> Series[Timestamp]: ... @overload def __add__( self, other: timedelta | Timedelta | TimedeltaIndex | BaseOffset ) -> DatetimeIndex: ... @overload - def __sub__(self, other: TimedeltaSeries) -> TimestampSeries: ... + def __sub__(self, other: TimedeltaSeries) -> Series[Timestamp]: ... @overload def __sub__( self, other: timedelta | Timedelta | TimedeltaIndex | BaseOffset @@ -76,7 +76,7 @@ class DatetimeIndex(DatetimeTimedeltaMixin[Timestamp], DatetimeIndexProperties): self, other: datetime | Timestamp | DatetimeIndex ) -> TimedeltaIndex: ... @final - def to_series(self, index=..., name: Hashable = ...) -> TimestampSeries: ... + def to_series(self, index=..., name: Hashable = ...) -> Series[Timestamp]: ... def snap(self, freq: str = ...): ... def slice_indexer(self, start=..., end=..., step=...): ... def searchsorted(self, value, side: str = ..., sorter=...): ... diff --git a/pandas-stubs/core/indexes/interval.pyi b/pandas-stubs/core/indexes/interval.pyi index 026c4f2af..cc53b668c 100644 --- a/pandas-stubs/core/indexes/interval.pyi +++ b/pandas-stubs/core/indexes/interval.pyi @@ -15,7 +15,6 @@ from pandas import Index from pandas.core.indexes.extension import ExtensionIndex from pandas.core.series import ( TimedeltaSeries, - TimestampSeries, ) from typing_extensions import TypeAlias @@ -53,7 +52,7 @@ _EdgesFloat: TypeAlias = ( _EdgesTimestamp: TypeAlias = ( Sequence[DatetimeLike] | npt.NDArray[np.datetime64] - | TimestampSeries + | pd.Series[pd.Timestamp] | pd.DatetimeIndex ) _EdgesTimedelta: TypeAlias = ( diff --git a/pandas-stubs/core/reshape/tile.pyi b/pandas-stubs/core/reshape/tile.pyi index 336426cc9..da7d83f16 100644 --- a/pandas-stubs/core/reshape/tile.pyi +++ b/pandas-stubs/core/reshape/tile.pyi @@ -12,10 +12,9 @@ from pandas import ( Index, Interval, IntervalIndex, - Series, Timestamp, ) -from pandas.core.series import TimestampSeries +from pandas.core.series import Series from pandas._typing import ( IntervalT, @@ -51,10 +50,10 @@ def cut( ) -> tuple[npt.NDArray[np.intp], IntervalIndex[IntervalT]]: ... @overload def cut( # pyright: ignore[reportOverlappingOverload] - x: TimestampSeries, + x: Series[Timestamp], bins: ( int - | TimestampSeries + | Series[Timestamp] | DatetimeIndex | Sequence[Timestamp] | Sequence[np.datetime64] @@ -70,7 +69,7 @@ def cut( # pyright: ignore[reportOverlappingOverload] ) -> tuple[Series, DatetimeIndex]: ... @overload def cut( - x: TimestampSeries, + x: Series[Timestamp], bins: IntervalIndex[Interval[Timestamp]], right: bool = ..., labels: Sequence[Label] | None = ..., @@ -156,10 +155,10 @@ def cut( ) -> npt.NDArray[np.intp]: ... @overload def cut( - x: TimestampSeries, + x: Series[Timestamp], bins: ( int - | TimestampSeries + | Series[Timestamp] | DatetimeIndex | Sequence[Timestamp] | Sequence[np.datetime64] diff --git a/pandas-stubs/core/series.pyi b/pandas-stubs/core/series.pyi index 0fac9a3de..68e9fccb0 100644 --- a/pandas-stubs/core/series.pyi +++ b/pandas-stubs/core/series.pyi @@ -25,6 +25,7 @@ from typing import ( Generic, Literal, NoReturn, + TypeVar, final, overload, ) @@ -59,10 +60,9 @@ 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 ( - CombinedDatetimelikeProperties, PeriodProperties, TimedeltaProperties, - TimestampProperties, + _dtDescriptor, ) from pandas.core.indexes.category import CategoricalIndex from pandas.core.indexes.datetimes import DatetimeIndex @@ -184,6 +184,30 @@ from pandas.core.dtypes.dtypes import CategoricalDtype from pandas.plotting import PlotAccessor +_T_FLOAT = TypeVar("_T_FLOAT", bound=float) +_T_COMPLEX = TypeVar("_T_COMPLEX", bound=complex) + +_scalar_timestamp: TypeAlias = datetime | np.datetime64 | Timestamp +_vector_timestamp: TypeAlias = ( + Sequence[datetime] + | Sequence[np.datetime64] + | Sequence[Timestamp] + | np.typing.NDArray[np.datetime64] + | DatetimeIndex +) +_nonseries_timestamp: TypeAlias = _scalar_timestamp | _vector_timestamp + +_scalar_timedelta: TypeAlias = timedelta | np.timedelta64 | BaseOffset | Timedelta +_vector_timedelta: TypeAlias = ( + Sequence[timedelta] + | Sequence[np.timedelta64] + | Sequence[Timedelta] + | np.typing.NDArray[np.timedelta64] + | TimedeltaIndex +) +_nonseries_timedelta: TypeAlias = _scalar_timedelta | _vector_timedelta +_T_TIMESTAMP = TypeVar("_T_TIMESTAMP", bound=Timestamp) + class _iLocIndexerSeries(_iLocIndexer, Generic[S1]): # get item @overload @@ -301,7 +325,7 @@ class Series(IndexOpsMixin[S1], NDFrame): dtype: TimestampDtypeArg = ..., name: Hashable = ..., copy: bool = ..., - ) -> TimestampSeries: ... + ) -> Series[Timestamp]: ... @overload def __new__( cls, @@ -311,7 +335,7 @@ class Series(IndexOpsMixin[S1], NDFrame): dtype: TimestampDtypeArg, name: Hashable = ..., copy: bool = ..., - ) -> TimestampSeries: ... + ) -> Series[Timestamp]: ... @overload def __new__( cls, @@ -752,6 +776,13 @@ class Series(IndexOpsMixin[S1], NDFrame): @overload def count(self, level: Hashable) -> Series[S1]: ... def mode(self, dropna=...) -> Series[S1]: ... + @overload + def unique(self: Series[Never]) -> np.ndarray: ... # type: ignore[overload-overlap] + @overload + def unique(self: Series[Timestamp]) -> DatetimeArray: ... # type: ignore[overload-overlap] + @overload + def unique(self: Series[Timedelta]) -> TimedeltaArray: ... # type: ignore[overload-overlap] + @overload def unique(self) -> np.ndarray: ... @overload def drop_duplicates( @@ -809,6 +840,8 @@ class Series(IndexOpsMixin[S1], NDFrame): @overload def diff(self: Series[_str], periods: int = ...) -> Never: ... @overload + def diff(self: Series[Timestamp], periods: int = ...) -> TimedeltaSeries: ... # type: ignore[overload-overlap] + @overload def diff(self, periods: int = ...) -> Series[float]: ... def autocorr(self, lag: int = ...) -> float: ... @overload @@ -1191,7 +1224,7 @@ class Series(IndexOpsMixin[S1], NDFrame): Series[type[object]], ]: ... @property - def dt(self) -> CombinedDatetimelikeProperties: ... + def dt(self) -> _dtDescriptor[S1]: ... @property def plot(self) -> PlotAccessor: ... sparse = ... @@ -1311,7 +1344,7 @@ class Series(IndexOpsMixin[S1], NDFrame): dtype: TimestampDtypeArg, copy: _bool = ..., errors: IgnoreRaise = ..., - ) -> TimestampSeries: ... + ) -> Series[Timestamp]: ... @overload def astype( self, @@ -1613,6 +1646,26 @@ class Series(IndexOpsMixin[S1], NDFrame): # just failed to generate these so I couldn't match # them up. @overload + def __add__( + self: Series[Never], + other: num | _str | timedelta | Timedelta | _ListLike | Series | np.timedelta64, + ) -> Series: ... + @overload + def __add__( + self: Series[Timestamp], other: _nonseries_timestamp | Series[Timestamp] + ) -> Never: ... + @overload + def __add__( + self: Series[Timestamp], + other: _nonseries_timedelta | Series[Timedelta] | TimedeltaSeries, + ) -> Series[Timestamp]: ... + @overload + def __add__(self: Series[Timedelta], other: Period) -> PeriodSeries: ... + @overload + def __add__( + self: Series[Timedelta], other: _nonseries_timestamp | Series[Timestamp] + ) -> Series[Timestamp]: ... + @overload def __add__(self, other: S1 | Self) -> Self: ... @overload def __add__( @@ -1629,6 +1682,16 @@ class Series(IndexOpsMixin[S1], NDFrame): # def __array__(self, dtype: Optional[_bool] = ...) -> _np_ndarray def __div__(self, other: num | _ListLike | Series[S1]) -> Series[S1]: ... def __eq__(self, other: object) -> Series[_bool]: ... # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] + @overload + def __floordiv__( + self: Series[Timedelta], other: float | Sequence[float] + ) -> TimedeltaSeries: ... + @overload + def __floordiv__( + self: Series[Timedelta], + other: _nonseries_timedelta | Series[Timedelta], + ) -> Series[int]: ... + @overload def __floordiv__(self, other: num | _ListLike | Series[S1]) -> Series[int]: ... def __ge__( # type: ignore[override] self, other: S1 | _ListLike | Series[S1] | datetime | timedelta | date @@ -1643,8 +1706,27 @@ class Series(IndexOpsMixin[S1], NDFrame): self, other: S1 | _ListLike | Series[S1] | datetime | timedelta | date ) -> Series[_bool]: ... @overload + def __mul__(self: Series[Never], other: num | _ListLike | Series) -> Series: ... + @overload + def __mul__(self, other: Series[Never]) -> Series: ... # type: ignore[overload-overlap] + @overload def __mul__( - self, other: timedelta | Timedelta | TimedeltaSeries | np.timedelta64 + self: Series[int], other: _T_COMPLEX | Series[_T_COMPLEX] + ) -> Series[_T_COMPLEX]: ... + @overload + def __mul__( + self: Series[Timestamp], + other: ( + num | Sequence[num] | Series[int] | Series[float] | float | Sequence[float] + ), + ) -> Series[Timestamp]: ... + @overload + def __mul__( + self: Series[Timestamp], other: _nonseries_timedelta | TimedeltaSeries + ) -> Never: ... + @overload + def __mul__( + self: Series[_T_FLOAT], other: _nonseries_timedelta | TimedeltaSeries ) -> TimedeltaSeries: ... @overload def __mul__(self, other: num | _ListLike | Series) -> Series: ... @@ -1659,6 +1741,18 @@ class Series(IndexOpsMixin[S1], NDFrame): @overload def __or__(self, other: int | np_ndarray_anyint | Series[int]) -> Series[int]: ... @overload + def __radd__( + self: Series[Never], other: num | _str | _ListLike | Series + ) -> Series: ... + @overload + def __radd__( + self: Series[Timestamp], other: _nonseries_timedelta | Series[Timedelta] + ) -> Series[Timestamp]: ... + @overload + def __radd__( + self: Series[Timedelta], other: datetime | Timestamp | Series[Timestamp] + ) -> Series[Timestamp]: ... + @overload def __radd__(self, other: S1 | Series[S1]) -> Self: ... @overload def __radd__(self, other: num | _str | _ListLike | Series) -> Series: ... @@ -1671,6 +1765,11 @@ class Series(IndexOpsMixin[S1], NDFrame): def __rand__(self, other: int | np_ndarray_anyint | Series[int]) -> Series[int]: ... def __rdiv__(self, other: num | _ListLike | Series[S1]) -> Series[S1]: ... def __rdivmod__(self, other: num | _ListLike | Series[S1]) -> Series[S1]: ... # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] + @overload + def __rfloordiv__( + self: Series[Timedelta], other: _nonseries_timedelta | Series[Timedelta] + ) -> Series[int]: ... + @overload def __rfloordiv__(self, other: num | _ListLike | Series[S1]) -> Series[S1]: ... def __rmod__(self, other: num | _ListLike | Series[S1]) -> Series[S1]: ... @overload @@ -1689,6 +1788,11 @@ class Series(IndexOpsMixin[S1], NDFrame): @overload def __ror__(self, other: int | np_ndarray_anyint | Series[int]) -> Series[int]: ... def __rsub__(self, other: num | _ListLike | Series[S1]) -> Series: ... + @overload + def __rtruediv__( + self: Series[Timedelta], other: _nonseries_timedelta | Series[Timedelta] + ) -> Series[float]: ... + @overload def __rtruediv__(self, other: num | _ListLike | Series[S1] | Path) -> Series: ... # ignore needed for mypy as we want different results based on the arguments @overload # type: ignore[override] @@ -1698,21 +1802,33 @@ class Series(IndexOpsMixin[S1], NDFrame): @overload def __rxor__(self, other: int | np_ndarray_anyint | Series[int]) -> Series[int]: ... @overload - def __sub__( - self: Series[Timestamp], - other: Timedelta | TimedeltaSeries | TimedeltaIndex | np.timedelta64, - ) -> TimestampSeries: ... + def __sub__(self: Series[Never], other: num | _ListLike | Series) -> Series: ... @overload def __sub__( - self: Series[Timedelta], - other: Timedelta | TimedeltaSeries | TimedeltaIndex | np.timedelta64, - ) -> TimedeltaSeries: ... + self: Series[Timestamp], other: _nonseries_timedelta | TimedeltaSeries + ) -> Series[Timestamp]: ... @overload def __sub__( - self, other: Timestamp | datetime | TimestampSeries + self: Series[Timestamp], other: _nonseries_timestamp | Series[_T_TIMESTAMP] ) -> TimedeltaSeries: ... @overload + def __sub__(self, other: S1 | Self) -> Self: ... + @overload def __sub__(self, other: num | _ListLike | Series) -> Series: ... + @overload + def __truediv__( + self: Series[Never], other: num | _ListLike | Series[S1] | Path + ) -> Series: ... + @overload + def __truediv__( + self: Series[Timestamp], + other: float | Series[int] | Series[float] | Sequence[float], + ) -> Series[Timestamp]: ... + @overload + def __truediv__( + self: Series[Timedelta], other: _nonseries_timedelta | Series[Timedelta] + ) -> Series[float]: ... + @overload def __truediv__(self, other: num | _ListLike | Series[S1] | Path) -> Series: ... # ignore needed for mypy as we want different results based on the arguments @overload # type: ignore[override] @@ -1782,6 +1898,14 @@ class Series(IndexOpsMixin[S1], NDFrame): **kwargs: Any, ) -> Never: ... @overload + def cumprod( + self: Series[Timestamp], + axis: AxisIndex = ..., + skipna: _bool = ..., + *args: Any, + **kwargs: Any, + ) -> Never: ... + @overload def cumprod( self, axis: AxisIndex = ..., @@ -1895,6 +2019,25 @@ class Series(IndexOpsMixin[S1], NDFrame): numeric_only: _bool = ..., **kwargs: Any, ) -> S1: ... + @overload + def mean( + self: Series[Never], + axis: AxisIndex | None = ..., + skipna: _bool = ..., + level: None = ..., + numeric_only: _bool = ..., + **kwargs: Any, + ) -> float: ... + @overload + def mean( + self: Series[Timestamp], + axis: AxisIndex | None = ..., + skipna: _bool = ..., + level: None = ..., + numeric_only: _bool = ..., + **kwargs: Any, + ) -> Timestamp: ... + @overload def mean( self, axis: AxisIndex | None = ..., @@ -1910,7 +2053,7 @@ class Series(IndexOpsMixin[S1], NDFrame): level: None = ..., numeric_only: _bool = ..., **kwargs: Any, - ) -> float: ... + ) -> S1: ... def min( self, axis: AxisIndex | None = ..., @@ -2091,6 +2234,17 @@ class Series(IndexOpsMixin[S1], NDFrame): numeric_only: _bool = ..., **kwargs: Any, ) -> Scalar: ... + @overload + def std( + self: Series[Timestamp], + axis: AxisIndex | None = ..., + skipna: _bool | None = ..., + level: None = ..., + ddof: int = ..., + numeric_only: _bool = ..., + **kwargs: Any, + ) -> Timedelta: ... + @overload def std( self, axis: AxisIndex | None = ..., @@ -2224,72 +2378,19 @@ class Series(IndexOpsMixin[S1], NDFrame): @final def __bool__(self) -> NoReturn: ... -class TimestampSeries(Series[Timestamp]): - @property - def dt(self) -> TimestampProperties: ... # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] - def __add__(self, other: TimedeltaSeries | np.timedelta64 | timedelta | BaseOffset) -> TimestampSeries: ... # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] - def __radd__(self, other: TimedeltaSeries | np.timedelta64 | timedelta) -> TimestampSeries: ... # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] - @overload # type: ignore[override] - def __sub__( - self, other: Timestamp | datetime | TimestampSeries - ) -> TimedeltaSeries: ... - @overload - def __sub__( # pyright: ignore[reportIncompatibleMethodOverride] - self, - other: ( - timedelta | TimedeltaSeries | TimedeltaIndex | np.timedelta64 | BaseOffset - ), - ) -> TimestampSeries: ... - def __mul__(self, other: float | Series[int] | Series[float] | Sequence[float]) -> TimestampSeries: ... # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] - def __truediv__(self, other: float | Series[int] | Series[float] | Sequence[float]) -> TimestampSeries: ... # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] - def unique(self) -> DatetimeArray: ... # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] - def mean( # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] - self, - axis: AxisIndex | None = ..., - skipna: _bool = ..., - level: None = ..., - numeric_only: _bool = ..., - **kwargs: Any, - ) -> Timestamp: ... - def median( # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] - self, - axis: AxisIndex | None = ..., - skipna: _bool = ..., - level: None = ..., - numeric_only: _bool = ..., - **kwargs: Any, - ) -> Timestamp: ... - def std( # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] - self, - axis: AxisIndex | None = ..., - skipna: _bool | None = ..., - level: None = ..., - ddof: int = ..., - numeric_only: _bool = ..., - **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(Series[Timedelta]): # ignores needed because of mypy @overload # type: ignore[override] def __add__(self, other: Period) -> PeriodSeries: ... @overload def __add__( - self, other: datetime | Timestamp | TimestampSeries | DatetimeIndex - ) -> TimestampSeries: ... + self, other: datetime | Timestamp | Series[Timestamp] | DatetimeIndex + ) -> Series[Timestamp]: ... @overload def __add__( # pyright: ignore[reportIncompatibleMethodOverride] self, other: timedelta | Timedelta | np.timedelta64 ) -> TimedeltaSeries: ... - def __radd__(self, other: datetime | Timestamp | TimestampSeries) -> TimestampSeries: ... # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] + def __radd__(self, other: datetime | Timestamp | Series[Timestamp]) -> Series[Timestamp]: ... # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] def __mul__( # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] self, other: num | Sequence[num] | Series[int] | Series[float] ) -> TimedeltaSeries: ... @@ -2356,7 +2457,7 @@ class TimedeltaSeries(Series[Timedelta]): numeric_only: _bool = ..., **kwargs: Any, ) -> Timedelta: ... - def median( # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] + def median( self, axis: AxisIndex | None = ..., skipna: _bool = ..., @@ -2381,7 +2482,7 @@ class TimedeltaSeries(Series[Timedelta]): *args: Any, **kwargs: Any, ) -> TimedeltaSeries: ... - def cumprod( + def cumprod( # pyrefly: ignore self, axis: AxisIndex = ..., skipna: _bool = ..., @@ -2409,7 +2510,7 @@ class OffsetSeries(Series[BaseOffset]): def __radd__( # pyright: ignore[reportIncompatibleMethodOverride] self, other: BaseOffset ) -> OffsetSeries: ... - def cumprod( + def cumprod( # pyrefly: ignore self, axis: AxisIndex = ..., skipna: _bool = ..., @@ -2420,4 +2521,4 @@ class OffsetSeries(Series[BaseOffset]): class IntervalSeries(Series[Interval[_OrderableT]], Generic[_OrderableT]): @property def array(self) -> IntervalArray: ... - def diff(self, periods: int = ...) -> Never: ... + def diff(self, periods: int = ...) -> Never: ... # pyrefly: ignore diff --git a/pandas-stubs/core/tools/datetimes.pyi b/pandas-stubs/core/tools/datetimes.pyi index 6d45eef4d..f040023bf 100644 --- a/pandas-stubs/core/tools/datetimes.pyi +++ b/pandas-stubs/core/tools/datetimes.pyi @@ -16,10 +16,7 @@ from pandas import ( ) from pandas.core.arrays import ExtensionArray from pandas.core.indexes.datetimes import DatetimeIndex -from pandas.core.series import ( - Series, - TimestampSeries, -) +from pandas.core.series import Series from typing_extensions import TypeAlias from pandas._libs.tslibs import NaTType @@ -94,7 +91,7 @@ def to_datetime( unit: str | None = ..., origin: Literal["julian", "unix"] | TimestampConvertibleTypes = ..., cache: bool = ..., -) -> TimestampSeries: ... +) -> Series[Timestamp]: ... @overload def to_datetime( arg: ( diff --git a/pyproject.toml b/pyproject.toml index 71400163a..d916bfa93 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,7 +39,7 @@ mypy = "1.17.0" pandas = "2.3.0" pyarrow = ">=10.0.1" pytest = ">=7.1.2" -pyright = ">=1.1.400" +pyright = ">=1.1.403" ty = "^0.0.1a8" pyrefly = "^0.21.0" poethepoet = ">=0.16.5" diff --git a/tests/test_frame.py b/tests/test_frame.py index ffd85d609..ac711045f 100644 --- a/tests/test_frame.py +++ b/tests/test_frame.py @@ -31,7 +31,6 @@ import numpy as np import numpy.typing as npt import pandas as pd -from pandas import Timestamp from pandas.api.typing import NAType from pandas.core.resample import ( DatetimeIndexResampler, @@ -63,10 +62,8 @@ if TYPE_CHECKING: from pandas.core.frame import _PandasNamedTuple - from pandas.core.series import TimestampSeries else: _PandasNamedTuple: TypeAlias = tuple - TimestampSeries: TypeAlias = pd.Series DF = pd.DataFrame(data={"col1": [1, 2], "col2": [3, 4]}) @@ -2535,9 +2532,9 @@ def test_types_regressions() -> None: sseries + pd.Timedelta(1, "d") check( - assert_type(sseries + pd.Timedelta(1, "D"), TimestampSeries), + assert_type(sseries + pd.Timedelta(1, "D"), "pd.Series[pd.Timestamp]"), pd.Series, - Timestamp, + pd.Timestamp, ) # https://github.com/microsoft/pylance-release/issues/2133 @@ -2819,9 +2816,9 @@ def test_sum_get_add() -> None: summer = df.sum(axis=1) check(assert_type(summer, pd.Series), pd.Series) - check(assert_type(s + summer, pd.Series), pd.Series) - check(assert_type(s + df["y"], pd.Series), pd.Series) - check(assert_type(summer + summer, pd.Series), pd.Series) + check(assert_type(s + summer, pd.Series), pd.Series) # type: ignore[assert-type] + check(assert_type(s + df["y"], pd.Series), pd.Series) # type: ignore[assert-type] + check(assert_type(summer + summer, pd.Series), pd.Series) # type: ignore[assert-type] def test_getset_untyped() -> None: @@ -3469,6 +3466,7 @@ def test_groupby_apply() -> None: df = pd.DataFrame({"col1": [1, 2, 3], "col2": [4, 5, 6]}) def sum_mean(x: pd.DataFrame) -> float: + x.sum() return x.sum().mean() with pytest_warns_bounded( diff --git a/tests/test_pandas.py b/tests/test_pandas.py index 068254c3a..1807a3c50 100644 --- a/tests/test_pandas.py +++ b/tests/test_pandas.py @@ -23,7 +23,6 @@ import pytest from typing_extensions import ( Never, - TypeAlias, assert_type, ) @@ -36,18 +35,19 @@ pytest_warns_bounded, ) -if TYPE_CHECKING: - from pandas.core.series import TimestampSeries -else: - TimestampSeries: TypeAlias = pd.Series - def test_types_to_datetime() -> None: df = pd.DataFrame({"year": [2015, 2016], "month": [2, 3], "day": [4, 5]}) - check(assert_type(pd.to_datetime(df), TimestampSeries), pd.Series, pd.Timestamp) + check( + assert_type(pd.to_datetime(df), "pd.Series[pd.Timestamp]"), + pd.Series, + pd.Timestamp, + ) check( - assert_type(pd.to_datetime(df, unit="s", origin="unix"), TimestampSeries), + assert_type( + pd.to_datetime(df, unit="s", origin="unix"), "pd.Series[pd.Timestamp]" + ), pd.Series, pd.Timestamp, ) @@ -56,7 +56,7 @@ def test_types_to_datetime() -> None: pd.to_datetime( df, unit="ns", dayfirst=True, utc=False, format="%M:%D", exact=False ), - TimestampSeries, + "pd.Series[pd.Timestamp]", ), pd.Series, pd.Timestamp, @@ -92,7 +92,7 @@ def test_types_to_datetime() -> None: check( assert_type( pd.to_datetime({"year": [2015, 2016], "month": [2, 3], "day": [4, 5]}), - TimestampSeries, + "pd.Series[pd.Timestamp]", ), pd.Series, pd.Timestamp, diff --git a/tests/test_scalars.py b/tests/test_scalars.py index bfc78ae65..0a782ddbd 100644 --- a/tests/test_scalars.py +++ b/tests/test_scalars.py @@ -39,12 +39,10 @@ OffsetSeries, PeriodSeries, TimedeltaSeries, - TimestampSeries, ) else: TimedeltaSeries: TypeAlias = pd.Series - TimestampSeries: TypeAlias = pd.Series PeriodSeries: TypeAlias = pd.Series OffsetSeries: TypeAlias = pd.Series @@ -1215,10 +1213,14 @@ def test_timestamp_add_sub() -> None: check(assert_type(as_timedelta_index + ts, pd.DatetimeIndex), pd.DatetimeIndex) check( - assert_type(ts + as_timedelta_series, TimestampSeries), pd.Series, pd.Timestamp + assert_type(ts + as_timedelta_series, "pd.Series[pd.Timestamp]"), + pd.Series, + pd.Timestamp, ) check( - assert_type(as_timedelta_series + ts, TimestampSeries), pd.Series, pd.Timestamp + assert_type(as_timedelta_series + ts, "pd.Series[pd.Timestamp]"), + pd.Series, + pd.Timestamp, ) check( @@ -1241,7 +1243,9 @@ def test_timestamp_add_sub() -> None: check(assert_type(ts - as_offset, pd.Timestamp), pd.Timestamp) check(assert_type(ts - as_timedelta_index, pd.DatetimeIndex), pd.DatetimeIndex) check( - assert_type(ts - as_timedelta_series, TimestampSeries), pd.Series, pd.Timestamp + assert_type(ts - as_timedelta_series, "pd.Series[pd.Timestamp]"), + pd.Series, + pd.Timestamp, ) check( assert_type(ts - as_np_ndarray_td64, npt.NDArray[np.datetime64]), @@ -1264,9 +1268,13 @@ def test_timestamp_cmp() -> None: # DatetimeIndex, but the type checker detects it to be UnknownIndex. c_unknown_index = pd.DataFrame({"a": [1]}, index=c_datetimeindex).index c_np_ndarray_dt64 = np_dt64_arr - c_series_dt64: TimestampSeries = pd.Series([1, 2, 3], dtype="datetime64[ns]") + c_series_dt64 = pd.Series([1, 2, 3], dtype="datetime64[ns]") c_series_timestamp = pd.Series(pd.DatetimeIndex(["2000-1-1"])) - check(assert_type(c_series_timestamp, TimestampSeries), pd.Series, pd.Timestamp) + check( + assert_type(c_series_timestamp, "pd.Series[pd.Timestamp]"), + pd.Series, + pd.Timestamp, + ) # Use xor to ensure one is True and the other is False # Correctness ensures since tested to be bools gt = check(assert_type(ts > c_timestamp, bool), bool) @@ -1725,7 +1733,7 @@ def test_types_timestamp_series_comparisons() -> None: data = pd.date_range("2022-01-01", "2022-01-31", freq="D") s = pd.Series(data) ts2 = pd.Timestamp("2022-01-15") - check(assert_type(s, TimestampSeries), pd.Series, pd.Timestamp) + check(assert_type(s, "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp) check(assert_type(ts2 <= s, "pd.Series[bool]"), pd.Series, np.bool_) check(assert_type(ts2 >= s, "pd.Series[bool]"), pd.Series, np.bool_) check(assert_type(ts2 < s, "pd.Series[bool]"), pd.Series, np.bool_) diff --git a/tests/test_series.py b/tests/test_series.py index 1e48aae75..f542c1bd2 100644 --- a/tests/test_series.py +++ b/tests/test_series.py @@ -68,7 +68,6 @@ from pandas.core.series import ( OffsetSeries, TimedeltaSeries, - TimestampSeries, ) from tests import ( @@ -89,7 +88,6 @@ else: TimedeltaSeries: TypeAlias = pd.Series - TimestampSeries: TypeAlias = pd.Series OffsetSeries: TypeAlias = pd.Series @@ -811,13 +809,11 @@ def test_types_element_wise_arithmetic() -> None: check(assert_type(s + s2, "pd.Series[int]"), pd.Series, np.integer) check(assert_type(s.add(s2, fill_value=0), "pd.Series[int]"), pd.Series, np.integer) - # TODO this one below should type pd.Series[int] - check(assert_type(s - s2, pd.Series), pd.Series, np.integer) + check(assert_type(s - s2, "pd.Series[int]"), pd.Series, np.integer) check(assert_type(s.sub(s2, fill_value=0), "pd.Series[int]"), pd.Series, np.integer) - # TODO these two below should type pd.Series[int] - # check(assert_type(s * s2, "pd.Series[int]"), pd.Series, np.integer ) - check(assert_type(s * s2, pd.Series), pd.Series, np.integer) + check(assert_type(s * s2, "pd.Series[int]"), pd.Series, np.integer) + # TODO this below should type pd.Series[int] # check(assert_type(s.mul(s2, fill_value=0), "pd.Series[int]"), pd.Series, np.integer) check(assert_type(s.mul(s2, fill_value=0), pd.Series), pd.Series, np.integer) @@ -1599,8 +1595,8 @@ def test_series_min_max_sub_axis() -> None: ss = s1 - s2 sm = s1 * s2 sd = s1 / s2 - check(assert_type(sa, pd.Series), pd.Series) - check(assert_type(ss, pd.Series), pd.Series) + check(assert_type(sa, pd.Series), pd.Series) # type: ignore[assert-type] + check(assert_type(ss, pd.Series), pd.Series) # type: ignore[assert-type] check(assert_type(sm, pd.Series), pd.Series) check(assert_type(sd, pd.Series), pd.Series) @@ -1636,11 +1632,11 @@ def test_series_multiindex_getitem() -> None: def test_series_mul() -> None: s = pd.Series([1, 2, 3]) sm = s * 4 - check(assert_type(sm, pd.Series), pd.Series) + check(assert_type(sm, "pd.Series[int]"), pd.Series, np.integer) ss = s - 4 - check(assert_type(ss, pd.Series), pd.Series) + check(assert_type(ss, "pd.Series[int]"), pd.Series, np.integer) sm2 = s * s - check(assert_type(sm2, pd.Series), pd.Series) + check(assert_type(sm2, "pd.Series[int]"), pd.Series, np.integer) sp = s + 4 check(assert_type(sp, "pd.Series[int]"), pd.Series, np.integer) @@ -2783,64 +2779,64 @@ def test_astype_timestamp(cast_arg: TimestampDtypeArg, target_type: type) -> Non if cast_arg in ("date32[pyarrow]", "date64[pyarrow]"): x = pd.Series(pd.date_range("2000-01-01", "2000-02-01")) - check(x.astype(cast_arg), TimestampSeries, target_type) + check(x.astype(cast_arg), pd.Series, target_type) else: - check(s.astype(cast_arg), TimestampSeries, target_type) + check(s.astype(cast_arg), pd.Series, target_type) if TYPE_CHECKING: # numpy datetime64 - assert_type(s.astype("datetime64[Y]"), TimestampSeries) - assert_type(s.astype("datetime64[M]"), TimestampSeries) - assert_type(s.astype("datetime64[W]"), TimestampSeries) - assert_type(s.astype("datetime64[D]"), TimestampSeries) - assert_type(s.astype("datetime64[h]"), TimestampSeries) - assert_type(s.astype("datetime64[m]"), TimestampSeries) - assert_type(s.astype("datetime64[s]"), TimestampSeries) - assert_type(s.astype("datetime64[ms]"), TimestampSeries) - assert_type(s.astype("datetime64[us]"), TimestampSeries) - assert_type(s.astype("datetime64[μs]"), TimestampSeries) - assert_type(s.astype("datetime64[ns]"), TimestampSeries) - assert_type(s.astype("datetime64[ps]"), TimestampSeries) - assert_type(s.astype("datetime64[fs]"), TimestampSeries) - assert_type(s.astype("datetime64[as]"), TimestampSeries) + assert_type(s.astype("datetime64[Y]"), "pd.Series[pd.Timestamp]") + assert_type(s.astype("datetime64[M]"), "pd.Series[pd.Timestamp]") + assert_type(s.astype("datetime64[W]"), "pd.Series[pd.Timestamp]") + assert_type(s.astype("datetime64[D]"), "pd.Series[pd.Timestamp]") + assert_type(s.astype("datetime64[h]"), "pd.Series[pd.Timestamp]") + assert_type(s.astype("datetime64[m]"), "pd.Series[pd.Timestamp]") + assert_type(s.astype("datetime64[s]"), "pd.Series[pd.Timestamp]") + assert_type(s.astype("datetime64[ms]"), "pd.Series[pd.Timestamp]") + assert_type(s.astype("datetime64[us]"), "pd.Series[pd.Timestamp]") + assert_type(s.astype("datetime64[μs]"), "pd.Series[pd.Timestamp]") + assert_type(s.astype("datetime64[ns]"), "pd.Series[pd.Timestamp]") + assert_type(s.astype("datetime64[ps]"), "pd.Series[pd.Timestamp]") + assert_type(s.astype("datetime64[fs]"), "pd.Series[pd.Timestamp]") + assert_type(s.astype("datetime64[as]"), "pd.Series[pd.Timestamp]") # numpy datetime64 type codes - assert_type(s.astype("M8[Y]"), TimestampSeries) - assert_type(s.astype("M8[M]"), TimestampSeries) - assert_type(s.astype("M8[W]"), TimestampSeries) - assert_type(s.astype("M8[D]"), TimestampSeries) - assert_type(s.astype("M8[h]"), TimestampSeries) - assert_type(s.astype("M8[m]"), TimestampSeries) - assert_type(s.astype("M8[s]"), TimestampSeries) - assert_type(s.astype("M8[ms]"), TimestampSeries) - assert_type(s.astype("M8[us]"), TimestampSeries) - assert_type(s.astype("M8[μs]"), TimestampSeries) - assert_type(s.astype("M8[ns]"), TimestampSeries) - assert_type(s.astype("M8[ps]"), TimestampSeries) - assert_type(s.astype("M8[fs]"), TimestampSeries) - assert_type(s.astype("M8[as]"), TimestampSeries) + assert_type(s.astype("M8[Y]"), "pd.Series[pd.Timestamp]") + assert_type(s.astype("M8[M]"), "pd.Series[pd.Timestamp]") + assert_type(s.astype("M8[W]"), "pd.Series[pd.Timestamp]") + assert_type(s.astype("M8[D]"), "pd.Series[pd.Timestamp]") + assert_type(s.astype("M8[h]"), "pd.Series[pd.Timestamp]") + assert_type(s.astype("M8[m]"), "pd.Series[pd.Timestamp]") + assert_type(s.astype("M8[s]"), "pd.Series[pd.Timestamp]") + assert_type(s.astype("M8[ms]"), "pd.Series[pd.Timestamp]") + assert_type(s.astype("M8[us]"), "pd.Series[pd.Timestamp]") + assert_type(s.astype("M8[μs]"), "pd.Series[pd.Timestamp]") + assert_type(s.astype("M8[ns]"), "pd.Series[pd.Timestamp]") + assert_type(s.astype("M8[ps]"), "pd.Series[pd.Timestamp]") + assert_type(s.astype("M8[fs]"), "pd.Series[pd.Timestamp]") + assert_type(s.astype("M8[as]"), "pd.Series[pd.Timestamp]") # numpy datetime64 type codes - assert_type(s.astype(" None: def test_timedeltaseries_operators() -> None: series = pd.Series([pd.Timedelta(days=1)]) check( - assert_type(series + datetime.datetime.now(), TimestampSeries), + assert_type(series + datetime.datetime.now(), "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp, ) @@ -3355,7 +3351,7 @@ def test_timedeltaseries_operators() -> None: pd.Timedelta, ) check( - assert_type(datetime.datetime.now() + series, TimestampSeries), + assert_type(datetime.datetime.now() + series, "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp, ) @@ -3369,13 +3365,13 @@ def test_timedeltaseries_operators() -> None: def test_timestamp_series() -> None: series = pd.Series([pd.Timestamp(2024, 4, 4)]) check( - assert_type(series + YearEnd(0), TimestampSeries), - TimestampSeries, + assert_type(series + YearEnd(0), "pd.Series[pd.Timestamp]"), + pd.Series, pd.Timestamp, ) check( - assert_type(series - YearEnd(0), TimestampSeries), - TimestampSeries, + assert_type(series - YearEnd(0), "pd.Series[pd.Timestamp]"), + pd.Series, pd.Timestamp, ) @@ -3905,7 +3901,7 @@ def test_cumsum_timedelta() -> None: s = pd.Series(pd.to_timedelta([1, 2, 3], "h")) check(assert_type(s.cumsum(), "TimedeltaSeries"), pd.Series, pd.Timedelta) check( - assert_type(pd.Timestamp(0) + s.cumsum(), "TimestampSeries"), + assert_type(pd.Timestamp(0) + s.cumsum(), "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp, ) diff --git a/tests/test_timefuncs.py b/tests/test_timefuncs.py index 4843c5139..67de2b7ae 100644 --- a/tests/test_timefuncs.py +++ b/tests/test_timefuncs.py @@ -51,7 +51,6 @@ if TYPE_CHECKING: from pandas.core.series import PeriodSeries # noqa: F401 from pandas.core.series import TimedeltaSeries # noqa: F401 - from pandas.core.series import TimestampSeries # noqa: F401 from tests import np_ndarray_bool @@ -125,7 +124,7 @@ def test_types_timestamp_series_comparisons() -> None: data = pd.date_range("2022-01-01", "2022-01-31", freq="D") s = pd.Series(data) ts2 = pd.Timestamp("2022-01-15") - check(assert_type(s, "TimestampSeries"), pd.Series, pd.Timestamp) + check(assert_type(s, "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp) check(assert_type(ts2 <= s, "pd.Series[bool]"), pd.Series, np.bool_) check(assert_type(ts2 >= s, "pd.Series[bool]"), pd.Series, np.bool_) check(assert_type(ts2 < s, "pd.Series[bool]"), pd.Series, np.bool_) @@ -216,7 +215,7 @@ def test_datetimeindex_plus_timedelta() -> None: check( assert_type( pd.Series([pd.Timestamp("2022-03-05"), pd.Timestamp("2022-03-06")]), - "TimestampSeries", + "pd.Series[pd.Timestamp]", ), pd.Series, pd.Timestamp, @@ -225,13 +224,13 @@ def test_datetimeindex_plus_timedelta() -> None: td_s = pd.to_timedelta(pd.Series([10, 20]), "minutes") dti_td_s = dti + td_s check( - assert_type(dti_td_s, "TimestampSeries"), + 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, "TimestampSeries"), + assert_type(td_dti_s, "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp, ) @@ -251,7 +250,7 @@ def test_datetimeindex_minus_timedelta() -> None: check( assert_type( pd.Series([pd.Timestamp("2022-03-05"), pd.Timestamp("2022-03-06")]), - "TimestampSeries", + "pd.Series[pd.Timestamp]", ), pd.Series, pd.Timestamp, @@ -260,7 +259,7 @@ def test_datetimeindex_minus_timedelta() -> None: td_s = pd.to_timedelta(pd.Series([10, 20]), "minutes") dti_td_s = dti - td_s check( - assert_type(dti_td_s, "TimestampSeries"), + assert_type(dti_td_s, "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp, ) @@ -277,7 +276,7 @@ def test_timestamp_plus_timedelta_series() -> None: check( assert_type( pd.Series([pd.Timestamp("2022-03-05"), pd.Timestamp("2022-03-06")]), - "TimestampSeries", + "pd.Series[pd.Timestamp]", ), pd.Series, pd.Timestamp, @@ -285,9 +284,9 @@ def test_timestamp_plus_timedelta_series() -> None: ts = pd.Timestamp("2022-03-05") td = pd.to_timedelta(pd.Series([10, 20]), "minutes") r3 = td + ts - check(assert_type(r3, "TimestampSeries"), pd.Series, pd.Timestamp) + check(assert_type(r3, "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp) r4 = ts + td - check(assert_type(r4, "TimestampSeries"), pd.Series, pd.Timestamp) + check(assert_type(r4, "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp) def test_timedelta_series_mult() -> None: @@ -321,9 +320,9 @@ 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: pd.Series = s1 + s2 # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + ssum = s1 + s2 # type: ignore[var-annotated] ts = pd.Timestamp("2022-06-30") - tsum: pd.Series = s1 + ts # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + tsum = s1 + ts # type: ignore[var-annotated] def test_dtindex_tzinfo() -> None: @@ -378,7 +377,9 @@ def test_series_dt_accessors() -> None: i0 = pd.date_range(start="2022-06-01", periods=10) check(assert_type(i0, pd.DatetimeIndex), pd.DatetimeIndex, pd.Timestamp) - check(assert_type(i0.to_series(), "TimestampSeries"), pd.Series, pd.Timestamp) + check( + assert_type(i0.to_series(), "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp + ) s0 = pd.Series(i0) @@ -425,94 +426,125 @@ def test_series_dt_accessors() -> None: ) s0_local = s0.dt.tz_localize("UTC") check( - assert_type(s0_local, "TimestampSeries"), + assert_type(s0_local, "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp, ) check( - assert_type(s0.dt.tz_localize(None), "TimestampSeries"), pd.Series, pd.Timestamp + assert_type(s0.dt.tz_localize(None), "pd.Series[pd.Timestamp]"), + pd.Series, + pd.Timestamp, ) check( assert_type( s0.dt.tz_localize(pytz.UTC, nonexistent=dt.timedelta(0)), - "TimestampSeries", + "pd.Series[pd.Timestamp]", ), pd.Series, pd.Timestamp, ) check( - assert_type(s0.dt.tz_localize(pytz.timezone("US/Eastern")), "TimestampSeries"), + assert_type( + s0.dt.tz_localize(pytz.timezone("US/Eastern")), "pd.Series[pd.Timestamp]" + ), pd.Series, pd.Timestamp, ) check( - assert_type(s0_local.dt.tz_convert("EST"), "TimestampSeries"), + assert_type(s0_local.dt.tz_convert("EST"), "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp, ) check( - assert_type(s0_local.dt.tz_convert(None), "TimestampSeries"), + assert_type(s0_local.dt.tz_convert(None), "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp, ) check( - assert_type(s0_local.dt.tz_convert(pytz.UTC), "TimestampSeries"), + assert_type(s0_local.dt.tz_convert(pytz.UTC), "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp, ) check( - assert_type(s0_local.dt.tz_convert(1), "TimestampSeries"), + assert_type(s0_local.dt.tz_convert(1), "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp, ) check( assert_type( - s0_local.dt.tz_convert(pytz.timezone("US/Eastern")), "TimestampSeries" + s0_local.dt.tz_convert(pytz.timezone("US/Eastern")), + "pd.Series[pd.Timestamp]", ), pd.Series, pd.Timestamp, ) check(assert_type(s0.dt.tz, Optional[dt.tzinfo]), type(None)) check(assert_type(s0_local.dt.tz, Optional[dt.tzinfo]), dt.tzinfo) - check(assert_type(s0.dt.normalize(), "TimestampSeries"), pd.Series, pd.Timestamp) + check( + assert_type(s0.dt.normalize(), "pd.Series[pd.Timestamp]"), + pd.Series, + pd.Timestamp, + ) check(assert_type(s0.dt.strftime("%Y"), "pd.Series[str]"), pd.Series, str) check( - assert_type(s0.dt.round("D", nonexistent=dt.timedelta(1)), "TimestampSeries"), + assert_type( + s0.dt.round("D", nonexistent=dt.timedelta(1)), "pd.Series[pd.Timestamp]" + ), pd.Series, pd.Timestamp, ) check( - assert_type(s0.dt.round("D", ambiguous=False), "TimestampSeries"), + assert_type(s0.dt.round("D", ambiguous=False), "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp, ) check( - assert_type(s0.dt.floor("D", nonexistent=dt.timedelta(1)), "TimestampSeries"), + assert_type( + s0.dt.floor("D", nonexistent=dt.timedelta(1)), "pd.Series[pd.Timestamp]" + ), pd.Series, pd.Timestamp, ) check( - assert_type(s0.dt.floor("D", ambiguous=False), "TimestampSeries"), + assert_type(s0.dt.floor("D", ambiguous=False), "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp, ) check( - assert_type(s0.dt.ceil("D", nonexistent=dt.timedelta(1)), "TimestampSeries"), + assert_type( + s0.dt.ceil("D", nonexistent=dt.timedelta(1)), "pd.Series[pd.Timestamp]" + ), pd.Series, pd.Timestamp, ) check( - assert_type(s0.dt.ceil("D", ambiguous=False), "TimestampSeries"), + assert_type(s0.dt.ceil("D", ambiguous=False), "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp, ) check(assert_type(s0.dt.month_name(), "pd.Series[str]"), pd.Series, str) check(assert_type(s0.dt.day_name(), "pd.Series[str]"), pd.Series, str) check(assert_type(s0.dt.unit, TimeUnit), str) - check(assert_type(s0.dt.as_unit("s"), "TimestampSeries"), pd.Series, pd.Timestamp) - check(assert_type(s0.dt.as_unit("ms"), "TimestampSeries"), pd.Series, pd.Timestamp) - check(assert_type(s0.dt.as_unit("us"), "TimestampSeries"), pd.Series, pd.Timestamp) - check(assert_type(s0.dt.as_unit("ns"), "TimestampSeries"), pd.Series, pd.Timestamp) + check( + assert_type(s0.dt.as_unit("s"), "pd.Series[pd.Timestamp]"), + pd.Series, + pd.Timestamp, + ) + check( + assert_type(s0.dt.as_unit("ms"), "pd.Series[pd.Timestamp]"), + pd.Series, + pd.Timestamp, + ) + check( + assert_type(s0.dt.as_unit("us"), "pd.Series[pd.Timestamp]"), + pd.Series, + pd.Timestamp, + ) + check( + assert_type(s0.dt.as_unit("ns"), "pd.Series[pd.Timestamp]"), + pd.Series, + pd.Timestamp, + ) i1 = pd.period_range(start="2022-06-01", periods=10) @@ -523,8 +555,14 @@ def test_series_dt_accessors() -> None: s1 = pd.Series(i1) check(assert_type(s1.dt.qyear, "pd.Series[int]"), pd.Series, np.integer) - check(assert_type(s1.dt.start_time, "TimestampSeries"), pd.Series, pd.Timestamp) - check(assert_type(s1.dt.end_time, "TimestampSeries"), pd.Series, pd.Timestamp) + check( + assert_type(s1.dt.start_time, "pd.Series[pd.Timestamp]"), + pd.Series, + pd.Timestamp, + ) + check( + assert_type(s1.dt.end_time, "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp + ) i2 = pd.timedelta_range(start="1 day", periods=10) check(assert_type(i2, pd.TimedeltaIndex), pd.TimedeltaIndex) @@ -551,18 +589,31 @@ def test_series_dt_accessors() -> None: check(assert_type(s2.dt.as_unit("us"), "TimedeltaSeries"), pd.Series, pd.Timedelta) check(assert_type(s2.dt.as_unit("ns"), "TimedeltaSeries"), pd.Series, pd.Timedelta) - # Checks for general Series other than TimestampSeries and TimedeltaSeries + # Checks for general Series other than Series[Timestamp] and TimedeltaSeries - s4 = cast( - "pd.Series[pd.Timestamp]", - pd.Series([pd.Timestamp("2024-01-01"), pd.Timestamp("2024-01-02")]), - ) + s4 = pd.Series([pd.Timestamp("2024-01-01"), pd.Timestamp("2024-01-02")]) check(assert_type(s4.dt.unit, TimeUnit), str) - check(assert_type(s4.dt.as_unit("s"), pd.Series), pd.Series, pd.Timestamp) - check(assert_type(s4.dt.as_unit("ms"), pd.Series), pd.Series, pd.Timestamp) - check(assert_type(s4.dt.as_unit("us"), pd.Series), pd.Series, pd.Timestamp) - check(assert_type(s4.dt.as_unit("ns"), pd.Series), pd.Series, pd.Timestamp) + check( + assert_type(s4.dt.as_unit("s"), "pd.Series[pd.Timestamp]"), + pd.Series, + pd.Timestamp, + ) + check( + assert_type(s4.dt.as_unit("ms"), "pd.Series[pd.Timestamp]"), + pd.Series, + pd.Timestamp, + ) + check( + assert_type(s4.dt.as_unit("us"), "pd.Series[pd.Timestamp]"), + pd.Series, + pd.Timestamp, + ) + check( + assert_type(s4.dt.as_unit("ns"), "pd.Series[pd.Timestamp]"), + pd.Series, + pd.Timestamp, + ) s5 = cast( "pd.Series[pd.Timedelta]", @@ -570,10 +621,26 @@ def test_series_dt_accessors() -> None: ) check(assert_type(s5.dt.unit, TimeUnit), str) - check(assert_type(s5.dt.as_unit("s"), pd.Series), pd.Series, pd.Timedelta) - check(assert_type(s5.dt.as_unit("ms"), pd.Series), pd.Series, pd.Timedelta) - check(assert_type(s5.dt.as_unit("us"), pd.Series), pd.Series, pd.Timedelta) - check(assert_type(s5.dt.as_unit("ns"), pd.Series), pd.Series, pd.Timedelta) + check( + assert_type(s5.dt.as_unit("s"), "pd.Series[pd.Timedelta]"), # type: ignore[assert-type] + pd.Series, + pd.Timedelta, + ) + check( + assert_type(s5.dt.as_unit("ms"), "pd.Series[pd.Timedelta]"), # type: ignore[assert-type] + pd.Series, + pd.Timedelta, + ) + check( + assert_type(s5.dt.as_unit("us"), "pd.Series[pd.Timedelta]"), # type: ignore[assert-type] + pd.Series, + pd.Timedelta, + ) + check( + assert_type(s5.dt.as_unit("ns"), "pd.Series[pd.Timedelta]"), # type: ignore[assert-type] + pd.Series, + pd.Timedelta, + ) def test_datetimeindex_accessors() -> None: @@ -1068,7 +1135,7 @@ def test_to_datetime_scalar_extended() -> None: def test_to_datetime_series() -> None: s = pd.Series(["2000-01-01", "2000-01-02"]) - check(assert_type(pd.to_datetime(s), "TimestampSeries"), pd.Series) + check(assert_type(pd.to_datetime(s), "pd.Series[pd.Timestamp]"), pd.Series) d: FulldatetimeDict = { "year": [2000, 2000, 2000], "month": [1, 1, 1], @@ -1089,9 +1156,9 @@ def test_to_datetime_series() -> None: "us": [1, 1, 1], "ns": [1, 1, 1], } - check(assert_type(pd.to_datetime(df), "TimestampSeries"), pd.Series) - check(assert_type(pd.to_datetime(d), "TimestampSeries"), pd.Series) - check(assert_type(pd.to_datetime(d_ex), "TimestampSeries"), pd.Series) + check(assert_type(pd.to_datetime(df), "pd.Series[pd.Timestamp]"), pd.Series) + check(assert_type(pd.to_datetime(d), "pd.Series[pd.Timestamp]"), pd.Series) + check(assert_type(pd.to_datetime(d_ex), "pd.Series[pd.Timestamp]"), pd.Series) def test_to_datetime_array() -> None: @@ -1334,19 +1401,19 @@ def test_timedelta64_and_arithmatic_operator() -> None: s3 = s2 - s1 check(assert_type(s3, "TimedeltaSeries"), pd.Series, pd.Timedelta) td1 = pd.Timedelta(1, "D") - check(assert_type(s2 - td1, "TimestampSeries"), pd.Series, pd.Timestamp) + check(assert_type(s2 - td1, "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp) # GH 758 s4 = s1.astype(object) - check(assert_type(s4 - td1, "TimestampSeries"), pd.Series, pd.Timestamp) + check(assert_type(s4 - td1, "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp) td = np.timedelta64(1, "D") - check(assert_type((s1 - td), "TimestampSeries"), pd.Series, pd.Timestamp) - check(assert_type((s1 + td), "TimestampSeries"), pd.Series, pd.Timestamp) + check(assert_type((s1 - td), "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp) + check(assert_type((s1 + td), "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp) check(assert_type((s3 - td), "TimedeltaSeries"), pd.Series, pd.Timedelta) check(assert_type((s3 + td), "TimedeltaSeries"), pd.Series, pd.Timedelta) check(assert_type((s3 / td), "pd.Series[float]"), pd.Series, float) if TYPE_CHECKING_INVALID_USAGE: - r1 = s1 * td # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + r1 = s1 * td # type: ignore[var-annotated] r2 = s1 / td # type: ignore[operator] # pyright: ignore[reportOperatorIssue] r3 = s3 * td # type: ignore[operator] # pyright: ignore[reportOperatorIssue] @@ -1355,7 +1422,7 @@ def test_timedeltaseries_add_timestampseries() -> None: tds = pd.Series(pd.timedelta_range(start="1 day", periods=10)) tss = pd.Series(pd.date_range(start="2012-01-01", periods=10, freq="W-MON")) plus = tds + tss - check(assert_type(plus, "TimestampSeries"), pd.Series, pd.Timestamp) + check(assert_type(plus, "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp) def test_mean_median_std() -> None: From 3df8ea0df3bc46284ed36d05c0c9ef909f7611e6 Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Sat, 12 Jul 2025 23:25:32 +0200 Subject: [PATCH 2/2] refactor: also remove TimedeltaSeries --- docs/philosophy.md | 4 +- pandas-stubs/_libs/interval.pyi | 7 +- pandas-stubs/_libs/tslibs/period.pyi | 7 +- pandas-stubs/_libs/tslibs/timedeltas.pyi | 48 +++--- pandas-stubs/_libs/tslibs/timestamps.pyi | 11 +- pandas-stubs/core/indexes/accessors.pyi | 9 +- pandas-stubs/core/indexes/datetimes.pyi | 9 +- pandas-stubs/core/indexes/interval.pyi | 5 +- pandas-stubs/core/indexes/timedeltas.pyi | 4 +- pandas-stubs/core/series.pyi | 209 ++++++----------------- pandas-stubs/core/tools/timedeltas.pyi | 7 +- tests/test_scalars.py | 84 +++++++-- tests/test_series.py | 147 ++++++++-------- tests/test_timefuncs.py | 129 ++++++-------- 14 files changed, 293 insertions(+), 387 deletions(-) diff --git a/docs/philosophy.md b/docs/philosophy.md index c19f1e1b9..e9d58f5e4 100644 --- a/docs/philosophy.md +++ b/docs/philosophy.md @@ -56,13 +56,13 @@ revealed as follows: ```text ttest.py:4: note: Revealed type is "pandas.core.series.Series[pandas._libs.tslibs.timestamps.Timestamp]" ttest.py:6: note: Revealed type is "pandas.core.series.Series[pandas._libs.tslibs.timestamps.Timestamp]" -ttest.py:8: note: Revealed type is "pandas.core.series.TimedeltaSeries" +ttest.py:8: note: Revealed type is "pandas.core.series.Series[pandas._libs.tslibs.timedeltas.Timedelta]" ttest.py:9: error: Need type annotation for "ssum" [var-annotated] ttest.py:10: note: Revealed type is "Never" ``` The type `Series[Timestamp]` is the result of creating a series from `pd.to_datetime()`, while -the type `TimedeltaSeries` is the result of subtracting two `Series[Timestamp]` as well as +the type `Series[Timedelta]` is the result of subtracting two `Series[Timestamp]` as well as the result of `pd.to_timedelta()`. ### Interval is Generic diff --git a/pandas-stubs/_libs/interval.pyi b/pandas-stubs/_libs/interval.pyi index 613eb06ba..ad1327bad 100644 --- a/pandas-stubs/_libs/interval.pyi +++ b/pandas-stubs/_libs/interval.pyi @@ -13,7 +13,6 @@ from pandas import ( Timedelta, Timestamp, ) -from pandas.core.series import TimedeltaSeries from pandas._typing import ( IntervalClosedType, @@ -171,7 +170,7 @@ class Interval(IntervalMixin, Generic[_OrderableT]): @overload def __gt__( self, - other: Series[int] | Series[float] | Series[Timestamp] | TimedeltaSeries, + other: Series[int] | Series[float] | Series[Timestamp] | Series[Timedelta], ) -> Series[bool]: ... @overload def __lt__(self, other: Interval[_OrderableT]) -> bool: ... @@ -180,7 +179,7 @@ class Interval(IntervalMixin, Generic[_OrderableT]): @overload def __lt__( self, - other: Series[int] | Series[float] | Series[Timestamp] | TimedeltaSeries, + other: Series[int] | Series[float] | Series[Timestamp] | Series[Timedelta], ) -> Series[bool]: ... @overload def __ge__(self, other: Interval[_OrderableT]) -> bool: ... @@ -189,7 +188,7 @@ class Interval(IntervalMixin, Generic[_OrderableT]): @overload def __ge__( self, - other: Series[int] | Series[float] | Series[Timestamp] | TimedeltaSeries, + other: Series[int] | Series[float] | Series[Timestamp] | Series[Timedelta], ) -> Series[bool]: ... @overload def __le__(self, other: Interval[_OrderableT]) -> bool: ... diff --git a/pandas-stubs/_libs/tslibs/period.pyi b/pandas-stubs/_libs/tslibs/period.pyi index cf93f3664..353c4083a 100644 --- a/pandas-stubs/_libs/tslibs/period.pyi +++ b/pandas-stubs/_libs/tslibs/period.pyi @@ -15,7 +15,6 @@ from pandas import ( from pandas.core.series import ( OffsetSeries, PeriodSeries, - TimedeltaSeries, ) from typing_extensions import TypeAlias @@ -82,7 +81,7 @@ class Period(PeriodMixin): @overload def __sub__(self, other: PeriodIndex) -> Index: ... @overload - def __sub__(self, other: TimedeltaSeries) -> PeriodSeries: ... + def __sub__(self, other: Series[Timedelta]) -> PeriodSeries: ... @overload def __sub__(self, other: TimedeltaIndex) -> PeriodIndex: ... @overload @@ -92,7 +91,7 @@ class Period(PeriodMixin): @overload def __add__(self, other: Index) -> PeriodIndex: ... @overload - def __add__(self, other: OffsetSeries | TimedeltaSeries) -> PeriodSeries: ... + def __add__(self, other: OffsetSeries | Series[Timedelta]) -> PeriodSeries: ... # ignore[misc] here because we know all other comparisons # are False, so we use Literal[False] @overload @@ -148,7 +147,7 @@ class Period(PeriodMixin): @overload def __radd__(self, other: Index) -> Index: ... @overload - def __radd__(self, other: TimedeltaSeries) -> PeriodSeries: ... + def __radd__(self, other: Series[Timedelta]) -> PeriodSeries: ... @overload def __radd__(self, other: NaTType) -> NaTType: ... @property diff --git a/pandas-stubs/_libs/tslibs/timedeltas.pyi b/pandas-stubs/_libs/tslibs/timedeltas.pyi index 798b87a9d..d43b5fba3 100644 --- a/pandas-stubs/_libs/tslibs/timedeltas.pyi +++ b/pandas-stubs/_libs/tslibs/timedeltas.pyi @@ -17,7 +17,6 @@ from pandas import ( Series, TimedeltaIndex, ) -from pandas.core.series import TimedeltaSeries from typing_extensions import ( Self, TypeAlias, @@ -159,10 +158,7 @@ class Timedelta(timedelta): @overload def __add__(self, other: pd.TimedeltaIndex) -> pd.TimedeltaIndex: ... @overload - def __add__( - self, - other: TimedeltaSeries, - ) -> TimedeltaSeries: ... + def __add__(self, other: Series[Timedelta]) -> Series[Timedelta]: ... @overload def __add__(self, other: Series[Timestamp]) -> Series[Timestamp]: ... @overload @@ -195,9 +191,7 @@ class Timedelta(timedelta): @overload def __sub__(self, other: pd.TimedeltaIndex) -> TimedeltaIndex: ... @overload - def __sub__( - self, other: TimedeltaSeries | Series[pd.Timedelta] - ) -> TimedeltaSeries: ... + def __sub__(self, other: Series[pd.Timedelta]) -> Series[pd.Timedelta]: ... @overload def __rsub__(self, other: timedelta | Timedelta | np.timedelta64) -> Timedelta: ... @overload @@ -231,9 +225,9 @@ class Timedelta(timedelta): self, other: npt.NDArray[np.integer] | npt.NDArray[np.floating] ) -> npt.NDArray[np.timedelta64]: ... @overload - def __mul__(self, other: Series[int]) -> TimedeltaSeries: ... + def __mul__(self, other: Series[int]) -> Series[Timedelta]: ... @overload - def __mul__(self, other: Series[float]) -> TimedeltaSeries: ... + def __mul__(self, other: Series[float]) -> Series[Timedelta]: ... @overload def __mul__(self, other: Index[int] | Index[float]) -> TimedeltaIndex: ... @overload @@ -243,9 +237,9 @@ class Timedelta(timedelta): self, other: npt.NDArray[np.floating] | npt.NDArray[np.integer] ) -> npt.NDArray[np.timedelta64]: ... @overload - def __rmul__(self, other: Series[int]) -> TimedeltaSeries: ... + def __rmul__(self, other: Series[int]) -> Series[Timedelta]: ... @overload - def __rmul__(self, other: Series[float]) -> TimedeltaSeries: ... + 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: ... @@ -266,11 +260,11 @@ class Timedelta(timedelta): @overload def __floordiv__(self, other: Index[int] | Index[float]) -> TimedeltaIndex: ... @overload - def __floordiv__(self, other: Series[int]) -> TimedeltaSeries: ... + def __floordiv__(self, other: Series[int]) -> Series[Timedelta]: ... @overload - def __floordiv__(self, other: Series[float]) -> TimedeltaSeries: ... + def __floordiv__(self, other: Series[float]) -> Series[Timedelta]: ... @overload - def __floordiv__(self, other: TimedeltaSeries) -> Series[int]: ... + def __floordiv__(self, other: Series[Timedelta]) -> Series[int]: ... @overload def __floordiv__(self, other: NaTType | None) -> float: ... @overload @@ -291,11 +285,11 @@ class Timedelta(timedelta): self, other: npt.NDArray[np.integer] | npt.NDArray[np.floating] ) -> npt.NDArray[np.timedelta64]: ... @overload - def __truediv__(self, other: TimedeltaSeries) -> Series[float]: ... + def __truediv__(self, other: Series[Timedelta]) -> Series[float]: ... @overload - def __truediv__(self, other: Series[int]) -> TimedeltaSeries: ... + def __truediv__(self, other: Series[int]) -> Series[Timedelta]: ... @overload - def __truediv__(self, other: Series[float]) -> TimedeltaSeries: ... + def __truediv__(self, other: Series[float]) -> Series[Timedelta]: ... @overload def __truediv__(self, other: Index[int] | Index[float]) -> TimedeltaIndex: ... def __rtruediv__(self, other: timedelta | Timedelta | NaTType) -> float: ... @@ -303,7 +297,7 @@ class Timedelta(timedelta): @overload def __eq__(self, other: timedelta | Timedelta | np.timedelta64) -> bool: ... # type: ignore[overload-overlap] # pyright: ignore[reportOverlappingOverload] @overload - def __eq__(self, other: TimedeltaSeries | Series[pd.Timedelta]) -> Series[bool]: ... # type: ignore[overload-overlap] + def __eq__(self, other: Series[Timedelta]) -> Series[bool]: ... # type: ignore[overload-overlap] @overload def __eq__( # type: ignore[overload-overlap] self, other: TimedeltaIndex | npt.NDArray[np.timedelta64] @@ -314,7 +308,7 @@ class Timedelta(timedelta): @overload def __ne__(self, other: timedelta | Timedelta | np.timedelta64) -> bool: ... # type: ignore[overload-overlap] # pyright: ignore[reportOverlappingOverload] @overload - def __ne__(self, other: TimedeltaSeries | Series[pd.Timedelta]) -> Series[bool]: ... # type: ignore[overload-overlap] + def __ne__(self, other: Series[pd.Timedelta]) -> Series[bool]: ... # type: ignore[overload-overlap] @overload def __ne__( # type: ignore[overload-overlap] self, other: TimedeltaIndex | npt.NDArray[np.timedelta64] @@ -327,7 +321,7 @@ class Timedelta(timedelta): @overload def __mod__(self, other: float) -> Timedelta: ... @overload - def __mod__(self, other: Series[int] | Series[float]) -> TimedeltaSeries: ... + def __mod__(self, other: Series[int] | Series[float]) -> Series[Timedelta]: ... @overload def __mod__(self, other: Index[int] | Index[float]) -> TimedeltaIndex: ... @overload @@ -336,8 +330,8 @@ class Timedelta(timedelta): ) -> npt.NDArray[np.timedelta64]: ... @overload def __mod__( - self, other: Series[int] | Series[float] | TimedeltaSeries - ) -> TimedeltaSeries: ... + self, other: Series[int] | Series[float] | Series[Timedelta] + ) -> Series[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 @@ -349,7 +343,7 @@ class Timedelta(timedelta): self, other: TimedeltaIndex | npt.NDArray[np.timedelta64] ) -> npt.NDArray[np.bool_]: ... @overload - def __le__(self, other: TimedeltaSeries | Series[pd.Timedelta]) -> Series[bool]: ... + def __le__(self, other: Series[pd.Timedelta]) -> Series[bool]: ... # Override due to more types supported than dt.timedelta @overload # type: ignore[override] def __lt__(self, other: timedelta | Timedelta | np.timedelta64) -> bool: ... # type: ignore[misc] @@ -358,7 +352,7 @@ class Timedelta(timedelta): self, other: TimedeltaIndex | npt.NDArray[np.timedelta64] ) -> npt.NDArray[np.bool_]: ... @overload - def __lt__(self, other: TimedeltaSeries | Series[pd.Timedelta]) -> Series[bool]: ... + def __lt__(self, other: Series[pd.Timedelta]) -> Series[bool]: ... # Override due to more types supported than dt.timedelta @overload # type: ignore[override] def __ge__(self, other: timedelta | Timedelta | np.timedelta64) -> bool: ... # type: ignore[misc] @@ -367,7 +361,7 @@ class Timedelta(timedelta): self, other: TimedeltaIndex | npt.NDArray[np.timedelta64] ) -> npt.NDArray[np.bool_]: ... @overload - def __ge__(self, other: TimedeltaSeries | Series[pd.Timedelta]) -> Series[bool]: ... + def __ge__(self, other: Series[pd.Timedelta]) -> Series[bool]: ... # Override due to more types supported than dt.timedelta @overload # type: ignore[override] def __gt__(self, other: timedelta | Timedelta | np.timedelta64) -> bool: ... # type: ignore[misc] @@ -376,7 +370,7 @@ class Timedelta(timedelta): self, other: TimedeltaIndex | npt.NDArray[np.timedelta64] ) -> npt.NDArray[np.bool_]: ... @overload - def __gt__(self, other: TimedeltaSeries | Series[pd.Timedelta]) -> Series[bool]: ... + def __gt__(self, other: Series[pd.Timedelta]) -> Series[bool]: ... def __hash__(self) -> int: ... def isoformat(self) -> str: ... def to_numpy(self) -> np.timedelta64: ... diff --git a/pandas-stubs/_libs/tslibs/timestamps.pyi b/pandas-stubs/_libs/tslibs/timestamps.pyi index 3b991d7ef..e0b113cd6 100644 --- a/pandas-stubs/_libs/tslibs/timestamps.pyi +++ b/pandas-stubs/_libs/tslibs/timestamps.pyi @@ -22,10 +22,7 @@ from pandas import ( Index, TimedeltaIndex, ) -from pandas.core.series import ( - Series, - TimedeltaSeries, -) +from pandas.core.series import Series from typing_extensions import ( Never, Self, @@ -204,7 +201,7 @@ class Timestamp(datetime, SupportsIndex): @overload def __add__(self, other: timedelta | np.timedelta64 | Tick) -> Self: ... @overload - def __add__(self, other: TimedeltaSeries) -> Series[Timestamp]: ... + def __add__(self, other: Series[Timedelta]) -> Series[Timestamp]: ... @overload def __add__(self, other: TimedeltaIndex) -> DatetimeIndex: ... @overload @@ -223,9 +220,9 @@ class Timestamp(datetime, SupportsIndex): @overload def __sub__(self, other: TimedeltaIndex) -> DatetimeIndex: ... @overload - def __sub__(self, other: TimedeltaSeries) -> Series[Timestamp]: ... + def __sub__(self, other: Series[Timedelta]) -> Series[Timestamp]: ... @overload - def __sub__(self, other: Series[Timestamp]) -> TimedeltaSeries: ... + def __sub__(self, other: Series[Timestamp]) -> Series[Timedelta]: ... @overload def __sub__( self, other: npt.NDArray[np.timedelta64] diff --git a/pandas-stubs/core/indexes/accessors.pyi b/pandas-stubs/core/indexes/accessors.pyi index 70ce8e090..87dbf510f 100644 --- a/pandas-stubs/core/indexes/accessors.pyi +++ b/pandas-stubs/core/indexes/accessors.pyi @@ -31,7 +31,6 @@ from pandas.core.frame import DataFrame from pandas.core.series import ( PeriodSeries, Series, - TimedeltaSeries, ) from typing_extensions import Never @@ -316,11 +315,11 @@ class _TimedeltaPropertiesNoRounding( class TimedeltaProperties( Properties, _TimedeltaPropertiesNoRounding[Series[int], Series[float]], - _DatetimeRoundingMethods[TimedeltaSeries], + _DatetimeRoundingMethods[Series[Timedelta]], ): @property def unit(self) -> TimeUnit: ... - def as_unit(self, unit: TimeUnit) -> TimedeltaSeries: ... + def as_unit(self, unit: TimeUnit) -> Series[Timedelta]: ... _PeriodDTReturnTypes = TypeVar( "_PeriodDTReturnTypes", bound=Series[Timestamp] | DatetimeIndex @@ -439,6 +438,10 @@ class _dtDescriptor(CombinedDatetimelikeProperties, Generic[S1]): self, instance: Series[Timestamp], owner: Any ) -> TimestampProperties: ... @overload + def __get__( + self, instance: Series[Timedelta], owner: Any + ) -> TimedeltaProperties: ... + @overload def __get__( self, instance: Series[S1], owner: Any ) -> CombinedDatetimelikeProperties: ... diff --git a/pandas-stubs/core/indexes/datetimes.pyi b/pandas-stubs/core/indexes/datetimes.pyi index 589390856..798f747d2 100644 --- a/pandas-stubs/core/indexes/datetimes.pyi +++ b/pandas-stubs/core/indexes/datetimes.pyi @@ -22,10 +22,7 @@ from pandas import ( ) from pandas.core.indexes.accessors import DatetimeIndexProperties from pandas.core.indexes.datetimelike import DatetimeTimedeltaMixin -from pandas.core.series import ( - Series, - TimedeltaSeries, -) +from pandas.core.series import Series from typing_extensions import Self from pandas._typing import ( @@ -60,13 +57,13 @@ class DatetimeIndex(DatetimeTimedeltaMixin[Timestamp], DatetimeIndexProperties): # various ignores needed for mypy, as we do want to restrict what can be used in # arithmetic for these types @overload - def __add__(self, other: TimedeltaSeries) -> Series[Timestamp]: ... + def __add__(self, other: Series[Timedelta]) -> Series[Timestamp]: ... @overload def __add__( self, other: timedelta | Timedelta | TimedeltaIndex | BaseOffset ) -> DatetimeIndex: ... @overload - def __sub__(self, other: TimedeltaSeries) -> Series[Timestamp]: ... + def __sub__(self, other: Series[Timedelta]) -> Series[Timestamp]: ... @overload def __sub__( self, other: timedelta | Timedelta | TimedeltaIndex | BaseOffset diff --git a/pandas-stubs/core/indexes/interval.pyi b/pandas-stubs/core/indexes/interval.pyi index cc53b668c..1aa315610 100644 --- a/pandas-stubs/core/indexes/interval.pyi +++ b/pandas-stubs/core/indexes/interval.pyi @@ -13,9 +13,6 @@ import numpy as np import pandas as pd from pandas import Index from pandas.core.indexes.extension import ExtensionIndex -from pandas.core.series import ( - TimedeltaSeries, -) from typing_extensions import TypeAlias from pandas._libs.interval import ( @@ -58,7 +55,7 @@ _EdgesTimestamp: TypeAlias = ( _EdgesTimedelta: TypeAlias = ( Sequence[pd.Timedelta] | npt.NDArray[np.timedelta64] - | TimedeltaSeries + | pd.Series[pd.Timedelta] | pd.TimedeltaIndex ) _TimestampLike: TypeAlias = pd.Timestamp | np.datetime64 | dt.datetime diff --git a/pandas-stubs/core/indexes/timedeltas.pyi b/pandas-stubs/core/indexes/timedeltas.pyi index 6d50cdbd2..e14d84db5 100644 --- a/pandas-stubs/core/indexes/timedeltas.pyi +++ b/pandas-stubs/core/indexes/timedeltas.pyi @@ -19,7 +19,7 @@ from pandas.core.indexes.accessors import TimedeltaIndexProperties 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 TimedeltaSeries +from pandas.core.series import Series from typing_extensions import Self from pandas._libs import ( @@ -75,7 +75,7 @@ class TimedeltaIndex(DatetimeTimedeltaMixin[Timedelta], TimedeltaIndexProperties @property def inferred_type(self) -> str: ... @final - def to_series(self, index=..., name: Hashable = ...) -> TimedeltaSeries: ... + def to_series(self, index=..., name: Hashable = ...) -> Series[Timedelta]: ... def shift(self, periods: int = ..., freq=...) -> Self: ... def timedelta_range( diff --git a/pandas-stubs/core/series.pyi b/pandas-stubs/core/series.pyi index 68e9fccb0..7729fddf0 100644 --- a/pandas-stubs/core/series.pyi +++ b/pandas-stubs/core/series.pyi @@ -61,7 +61,6 @@ from pandas.core.groupby.groupby import BaseGroupBy from pandas.core.indexers import BaseIndexer from pandas.core.indexes.accessors import ( PeriodProperties, - TimedeltaProperties, _dtDescriptor, ) from pandas.core.indexes.category import CategoricalIndex @@ -175,6 +174,7 @@ from pandas._typing import ( VoidDtypeArg, WriteBuffer, np_ndarray_anyint, + np_ndarray_float, npt, num, ) @@ -206,7 +206,7 @@ _vector_timedelta: TypeAlias = ( | TimedeltaIndex ) _nonseries_timedelta: TypeAlias = _scalar_timedelta | _vector_timedelta -_T_TIMESTAMP = TypeVar("_T_TIMESTAMP", bound=Timestamp) +_T_STAMP_AND_DELTA = TypeVar("_T_STAMP_AND_DELTA", bound=Timestamp | Timedelta) class _iLocIndexerSeries(_iLocIndexer, Generic[S1]): # get item @@ -359,7 +359,7 @@ class Series(IndexOpsMixin[S1], NDFrame): dtype: TimedeltaDtypeArg = ..., name: Hashable = ..., copy: bool = ..., - ) -> TimedeltaSeries: ... + ) -> Series[Timedelta]: ... @overload def __new__( cls, @@ -840,7 +840,7 @@ class Series(IndexOpsMixin[S1], NDFrame): @overload def diff(self: Series[_str], periods: int = ...) -> Never: ... @overload - def diff(self: Series[Timestamp], periods: int = ...) -> TimedeltaSeries: ... # type: ignore[overload-overlap] + def diff(self: Series[_T_STAMP_AND_DELTA], periods: int = ...) -> Series[Timedelta]: ... # type: ignore[overload-overlap] @overload def diff(self, periods: int = ...) -> Series[float]: ... def autocorr(self, lag: int = ...) -> float: ... @@ -1337,7 +1337,7 @@ class Series(IndexOpsMixin[S1], NDFrame): dtype: TimedeltaDtypeArg, copy: _bool = ..., errors: IgnoreRaise = ..., - ) -> TimedeltaSeries: ... + ) -> Series[Timedelta]: ... @overload def astype( self, @@ -1656,9 +1656,9 @@ class Series(IndexOpsMixin[S1], NDFrame): ) -> Never: ... @overload def __add__( - self: Series[Timestamp], - other: _nonseries_timedelta | Series[Timedelta] | TimedeltaSeries, - ) -> Series[Timestamp]: ... + self: Series[_T_STAMP_AND_DELTA], + other: _nonseries_timedelta | Series[Timedelta], + ) -> Series[_T_STAMP_AND_DELTA]: ... @overload def __add__(self: Series[Timedelta], other: Period) -> PeriodSeries: ... @overload @@ -1685,7 +1685,7 @@ class Series(IndexOpsMixin[S1], NDFrame): @overload def __floordiv__( self: Series[Timedelta], other: float | Sequence[float] - ) -> TimedeltaSeries: ... + ) -> Series[Timedelta]: ... @overload def __floordiv__( self: Series[Timedelta], @@ -1715,19 +1715,23 @@ class Series(IndexOpsMixin[S1], NDFrame): ) -> Series[_T_COMPLEX]: ... @overload def __mul__( - self: Series[Timestamp], + self: Series[_T_STAMP_AND_DELTA], other: ( - num | Sequence[num] | Series[int] | Series[float] | float | Sequence[float] + _T_FLOAT + | Sequence[_T_FLOAT] + | np_ndarray_anyint + | np_ndarray_float + | Series[_T_FLOAT] ), - ) -> Series[Timestamp]: ... + ) -> Series[_T_STAMP_AND_DELTA]: ... @overload def __mul__( - self: Series[Timestamp], other: _nonseries_timedelta | TimedeltaSeries + self: Series[Timestamp], other: _nonseries_timedelta | Series[Timedelta] ) -> Never: ... @overload def __mul__( - self: Series[_T_FLOAT], other: _nonseries_timedelta | TimedeltaSeries - ) -> TimedeltaSeries: ... + self: Series[_T_FLOAT], other: _nonseries_timedelta | Series[Timedelta] + ) -> Series[Timedelta]: ... @overload def __mul__(self, other: num | _ListLike | Series) -> Series: ... def __mod__(self, other: num | _ListLike | Series[S1]) -> Series[S1]: ... @@ -1746,11 +1750,11 @@ class Series(IndexOpsMixin[S1], NDFrame): ) -> Series: ... @overload def __radd__( - self: Series[Timestamp], other: _nonseries_timedelta | Series[Timedelta] + self: Series[Timestamp], other: _nonseries_timedelta ) -> Series[Timestamp]: ... @overload def __radd__( - self: Series[Timedelta], other: datetime | Timestamp | Series[Timestamp] + self: Series[Timedelta], other: _nonseries_timestamp ) -> Series[Timestamp]: ... @overload def __radd__(self, other: S1 | Series[S1]) -> Self: ... @@ -1774,8 +1778,8 @@ class Series(IndexOpsMixin[S1], NDFrame): def __rmod__(self, other: num | _ListLike | Series[S1]) -> Series[S1]: ... @overload def __rmul__( - self, other: timedelta | Timedelta | TimedeltaSeries | np.timedelta64 - ) -> TimedeltaSeries: ... + self, other: timedelta | Timedelta | Series[Timedelta] | np.timedelta64 + ) -> Series[Timedelta]: ... @overload def __rmul__(self, other: num | _ListLike | Series) -> Series: ... def __rnatmul__(self, other: num | _ListLike | Series[S1]) -> Series[S1]: ... @@ -1805,12 +1809,16 @@ class Series(IndexOpsMixin[S1], NDFrame): def __sub__(self: Series[Never], other: num | _ListLike | Series) -> Series: ... @overload def __sub__( - self: Series[Timestamp], other: _nonseries_timedelta | TimedeltaSeries + self: Series[Timestamp], other: _nonseries_timedelta | Series[Timedelta] ) -> Series[Timestamp]: ... @overload def __sub__( - self: Series[Timestamp], other: _nonseries_timestamp | Series[_T_TIMESTAMP] - ) -> TimedeltaSeries: ... + self: Series[Timestamp], other: _nonseries_timestamp | Series[Timestamp] + ) -> Series[Timedelta]: ... + @overload + def __sub__( + self: Series[Timedelta], other: _nonseries_timedelta | Series[Timedelta] + ) -> Series[Timedelta]: ... @overload def __sub__(self, other: S1 | Self) -> Self: ... @overload @@ -1821,9 +1829,15 @@ class Series(IndexOpsMixin[S1], NDFrame): ) -> Series: ... @overload def __truediv__( - self: Series[Timestamp], - other: float | Series[int] | Series[float] | Sequence[float], - ) -> Series[Timestamp]: ... + self: Series[_T_STAMP_AND_DELTA], + other: ( + _T_FLOAT + | Sequence[_T_FLOAT] + | np_ndarray_anyint + | np_ndarray_float + | Series[_T_FLOAT] + ), + ) -> Series[_T_STAMP_AND_DELTA]: ... @overload def __truediv__( self: Series[Timedelta], other: _nonseries_timedelta | Series[Timedelta] @@ -1899,7 +1913,7 @@ class Series(IndexOpsMixin[S1], NDFrame): ) -> Never: ... @overload def cumprod( - self: Series[Timestamp], + self: Series[_T_STAMP_AND_DELTA], axis: AxisIndex = ..., skipna: _bool = ..., *args: Any, @@ -2021,22 +2035,13 @@ class Series(IndexOpsMixin[S1], NDFrame): ) -> S1: ... @overload def mean( - self: Series[Never], - axis: AxisIndex | None = ..., - skipna: _bool = ..., - level: None = ..., - numeric_only: _bool = ..., - **kwargs: Any, - ) -> float: ... - @overload - def mean( - self: Series[Timestamp], + self: Series[_T_STAMP_AND_DELTA], axis: AxisIndex | None = ..., skipna: _bool = ..., level: None = ..., numeric_only: _bool = ..., **kwargs: Any, - ) -> Timestamp: ... + ) -> _T_STAMP_AND_DELTA: ... @overload def mean( self, @@ -2072,11 +2077,19 @@ class Series(IndexOpsMixin[S1], NDFrame): @overload def mul( self, - other: timedelta | Timedelta | TimedeltaSeries | np.timedelta64, + other: timedelta | Timedelta | Series[Timedelta] | np.timedelta64, level: Level | None = ..., fill_value: float | None = ..., axis: AxisIndex | None = ..., - ) -> TimedeltaSeries: ... + ) -> Series[Timedelta]: ... + @overload + def mul( + self: Series[int], + other: int | Series[int], + level: Level | None = ..., + fill_value: int | None = ..., + axis: AxisIndex | None = ..., + ) -> Series[int]: ... @overload def mul( self, @@ -2157,11 +2170,11 @@ class Series(IndexOpsMixin[S1], NDFrame): @overload def rmul( self, - other: timedelta | Timedelta | TimedeltaSeries | np.timedelta64, + other: timedelta | Timedelta | Series[Timedelta] | np.timedelta64, level: Level | None = ..., fill_value: float | None = ..., axis: AxisIndex = ..., - ) -> TimedeltaSeries: ... + ) -> Series[Timedelta]: ... @overload def rmul( self, @@ -2236,7 +2249,7 @@ class Series(IndexOpsMixin[S1], NDFrame): ) -> Scalar: ... @overload def std( - self: Series[Timestamp], + self: Series[_T_STAMP_AND_DELTA], axis: AxisIndex | None = ..., skipna: _bool | None = ..., level: None = ..., @@ -2378,118 +2391,6 @@ class Series(IndexOpsMixin[S1], NDFrame): @final def __bool__(self) -> NoReturn: ... -class TimedeltaSeries(Series[Timedelta]): - # ignores needed because of mypy - @overload # type: ignore[override] - def __add__(self, other: Period) -> PeriodSeries: ... - @overload - def __add__( - self, other: datetime | Timestamp | Series[Timestamp] | DatetimeIndex - ) -> Series[Timestamp]: ... - @overload - def __add__( # pyright: ignore[reportIncompatibleMethodOverride] - self, other: timedelta | Timedelta | np.timedelta64 - ) -> TimedeltaSeries: ... - def __radd__(self, other: datetime | Timestamp | Series[Timestamp]) -> Series[Timestamp]: ... # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] - def __mul__( # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] - self, other: num | Sequence[num] | Series[int] | Series[float] - ) -> TimedeltaSeries: ... - def unique(self) -> TimedeltaArray: ... # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] - def __sub__( # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] - self, - other: ( - timedelta | Timedelta | TimedeltaSeries | TimedeltaIndex | np.timedelta64 - ), - ) -> TimedeltaSeries: ... - @overload # type: ignore[override] - def __truediv__(self, other: float | Sequence[float]) -> Self: ... - @overload - def __truediv__( # pyright: ignore[reportIncompatibleMethodOverride] - self, - other: ( - timedelta - | TimedeltaSeries - | np.timedelta64 - | TimedeltaIndex - | Sequence[timedelta] - ), - ) -> Series[float]: ... - def __rtruediv__( # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] - self, - other: ( - timedelta - | TimedeltaSeries - | np.timedelta64 - | TimedeltaIndex - | Sequence[timedelta] - ), - ) -> Series[float]: ... - @overload # type: ignore[override] - def __floordiv__(self, other: float | Sequence[float]) -> Self: ... - @overload - def __floordiv__( # pyright: ignore[reportIncompatibleMethodOverride] - self, - other: ( - timedelta - | TimedeltaSeries - | np.timedelta64 - | TimedeltaIndex - | Sequence[timedelta] - ), - ) -> Series[int]: ... - def __rfloordiv__( # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] - self, - other: ( - timedelta - | TimedeltaSeries - | np.timedelta64 - | TimedeltaIndex - | Sequence[timedelta] - ), - ) -> Series[int]: ... - @property - def dt(self) -> TimedeltaProperties: ... # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] - def mean( # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] - self, - axis: AxisIndex | None = ..., - skipna: _bool = ..., - level: None = ..., - numeric_only: _bool = ..., - **kwargs: Any, - ) -> Timedelta: ... - def median( - self, - axis: AxisIndex | None = ..., - skipna: _bool = ..., - level: None = ..., - numeric_only: _bool = ..., - **kwargs: Any, - ) -> Timedelta: ... - def std( # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] - self, - axis: AxisIndex | None = ..., - skipna: _bool | None = ..., - level: None = ..., - ddof: int = ..., - numeric_only: _bool = ..., - **kwargs: Any, - ) -> Timedelta: ... - def diff(self, periods: int = ...) -> TimedeltaSeries: ... # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] - def cumsum( - self, - axis: AxisIndex | None = ..., - skipna: _bool = ..., - *args: Any, - **kwargs: Any, - ) -> TimedeltaSeries: ... - def cumprod( # pyrefly: ignore - self, - axis: AxisIndex = ..., - skipna: _bool = ..., - *args: Any, - **kwargs: Any, - ) -> Never: ... - class PeriodSeries(Series[Period]): @property def dt(self) -> PeriodProperties: ... # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] diff --git a/pandas-stubs/core/tools/timedeltas.pyi b/pandas-stubs/core/tools/timedeltas.pyi index 38ed0c074..3057ce808 100644 --- a/pandas-stubs/core/tools/timedeltas.pyi +++ b/pandas-stubs/core/tools/timedeltas.pyi @@ -4,10 +4,7 @@ from typing import overload from pandas import Index from pandas.core.indexes.timedeltas import TimedeltaIndex -from pandas.core.series import ( - Series, - TimedeltaSeries, -) +from pandas.core.series import Series from pandas._libs.tslibs import Timedelta from pandas._libs.tslibs.timedeltas import TimeDeltaUnitChoices @@ -28,7 +25,7 @@ def to_timedelta( arg: Series, unit: TimeDeltaUnitChoices | None = ..., errors: RaiseCoerce = ..., -) -> TimedeltaSeries: ... +) -> Series[Timedelta]: ... @overload def to_timedelta( arg: ( diff --git a/tests/test_scalars.py b/tests/test_scalars.py index 0a782ddbd..126e378ad 100644 --- a/tests/test_scalars.py +++ b/tests/test_scalars.py @@ -38,11 +38,9 @@ from pandas.core.series import ( OffsetSeries, PeriodSeries, - TimedeltaSeries, ) else: - TimedeltaSeries: TypeAlias = pd.Series PeriodSeries: TypeAlias = pd.Series OffsetSeries: TypeAlias = pd.Series @@ -545,7 +543,9 @@ def test_timedelta_add_sub() -> None: 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, TimedeltaSeries), pd.Series, pd.Timedelta + 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) @@ -585,7 +585,9 @@ def test_timedelta_add_sub() -> None: ) check(assert_type(as_timedelta_index + td, pd.TimedeltaIndex), pd.TimedeltaIndex) check( - assert_type(as_timedelta_series + td, TimedeltaSeries), pd.Series, pd.Timedelta + 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) @@ -618,7 +620,9 @@ def test_timedelta_add_sub() -> None: 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, TimedeltaSeries), pd.Series, pd.Timedelta + 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]), @@ -650,7 +654,9 @@ def test_timedelta_add_sub() -> None: ) check(assert_type(as_timedelta_index - td, pd.TimedeltaIndex), pd.TimedeltaIndex) check( - assert_type(as_timedelta_series - td, TimedeltaSeries), pd.Series, pd.Timedelta + 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) @@ -704,8 +710,16 @@ def test_timedelta_mul_div() -> None: np.ndarray, np.timedelta64, ) - check(assert_type(td * mp_series_int, TimedeltaSeries), pd.Series, pd.Timedelta) - check(assert_type(td * md_series_float, TimedeltaSeries), pd.Series, pd.Timedelta) + 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) @@ -721,8 +735,16 @@ def test_timedelta_mul_div() -> None: np.ndarray, np.timedelta64, ) - check(assert_type(mp_series_int * td, TimedeltaSeries), pd.Series, pd.Timedelta) - check(assert_type(md_series_float * td, TimedeltaSeries), pd.Series, pd.Timedelta) + 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) @@ -740,8 +762,16 @@ def test_timedelta_mul_div() -> None: np.ndarray, np.timedelta64, ) - check(assert_type(td // mp_series_int, TimedeltaSeries), pd.Series, pd.Timedelta) - check(assert_type(td // md_series_float, TimedeltaSeries), pd.Series, pd.Timedelta) + 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( @@ -778,8 +808,16 @@ def test_timedelta_mul_div() -> None: np.ndarray, np.timedelta64, ) - check(assert_type(td / mp_series_int, TimedeltaSeries), pd.Series, pd.Timedelta) - check(assert_type(td / md_series_float, TimedeltaSeries), pd.Series, pd.Timedelta) + 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(td / md_timedelta_series, "pd.Series[float]"), pd.Series, float) @@ -820,8 +858,14 @@ def test_timedelta_mod_abs_unary() -> None: ) int_series = pd.Series([1, 2, 3], dtype=int) float_series = pd.Series([1.2, 2.2, 3.4], dtype=float) - check(assert_type(td % int_series, TimedeltaSeries), pd.Series, pd.Timedelta) - check(assert_type(td % float_series, TimedeltaSeries), pd.Series, pd.Timedelta) + check( + assert_type(td % int_series, "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta + ) + check( + assert_type(td % float_series, "pd.Series[pd.Timedelta]"), + pd.Series, + pd.Timedelta, + ) check(assert_type(td % i_idx, pd.TimedeltaIndex), pd.TimedeltaIndex) check( @@ -1197,7 +1241,11 @@ def test_timestamp_add_sub() -> None: as_timedelta_index = pd.to_timedelta([1, 2, 3], unit="D") as_timedelta_series = pd.Series(as_timedelta_index) - check(assert_type(as_timedelta_series, TimedeltaSeries), pd.Series, pd.Timedelta) + check( + assert_type(as_timedelta_series, "pd.Series[pd.Timedelta]"), + pd.Series, + pd.Timedelta, + ) as_np_ndarray_td64 = np_td64_arr check(assert_type(ts + as_pd_timedelta, pd.Timestamp), pd.Timestamp) @@ -1856,7 +1904,7 @@ def test_period_add_subtract() -> None: as_period = pd.Period("2012-1-1", freq="D") scale = 24 * 60 * 60 * 10**9 as_td_series = pd.Series(pd.timedelta_range(scale, scale, freq="D")) - check(assert_type(as_td_series, TimedeltaSeries), pd.Series, pd.Timedelta) + 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) as_timedelta_idx = pd.timedelta_range(scale, scale, freq="D") diff --git a/tests/test_series.py b/tests/test_series.py index f542c1bd2..c4e94e91d 100644 --- a/tests/test_series.py +++ b/tests/test_series.py @@ -65,10 +65,7 @@ ) if TYPE_CHECKING: - from pandas.core.series import ( - OffsetSeries, - TimedeltaSeries, - ) + from pandas.core.series import OffsetSeries from tests import ( BooleanDtypeArg, @@ -87,7 +84,6 @@ from tests import np_ndarray_int # noqa: F401 else: - TimedeltaSeries: TypeAlias = pd.Series OffsetSeries: TypeAlias = pd.Series @@ -813,9 +809,7 @@ def test_types_element_wise_arithmetic() -> None: check(assert_type(s.sub(s2, fill_value=0), "pd.Series[int]"), pd.Series, np.integer) check(assert_type(s * s2, "pd.Series[int]"), pd.Series, np.integer) - # TODO this below should type pd.Series[int] - # check(assert_type(s.mul(s2, fill_value=0), "pd.Series[int]"), pd.Series, np.integer) - check(assert_type(s.mul(s2, fill_value=0), pd.Series), pd.Series, np.integer) + check(assert_type(s.mul(s2, fill_value=0), "pd.Series[int]"), pd.Series, np.integer) # TODO these two below should type pd.Series[float] # check(assert_type(s / s2, "pd.Series[float]"), pd.Series, np.float64) @@ -1596,8 +1590,8 @@ def test_series_min_max_sub_axis() -> None: sm = s1 * s2 sd = s1 / s2 check(assert_type(sa, pd.Series), pd.Series) # type: ignore[assert-type] - check(assert_type(ss, pd.Series), pd.Series) # type: ignore[assert-type] - check(assert_type(sm, pd.Series), pd.Series) + check(assert_type(ss, pd.Series), pd.Series) + check(assert_type(sm, pd.Series), pd.Series) # type: ignore[assert-type] check(assert_type(sd, pd.Series), pd.Series) @@ -2842,58 +2836,58 @@ def test_astype_timestamp(cast_arg: TimestampDtypeArg, target_type: type) -> Non @pytest.mark.parametrize("cast_arg, target_type", ASTYPE_TIMEDELTA_ARGS, ids=repr) def test_astype_timedelta(cast_arg: TimedeltaDtypeArg, target_type: type) -> None: s = pd.Series([1, 2, 3]) - check(s.astype(cast_arg), TimedeltaSeries, target_type) + check(s.astype(cast_arg), pd.Series, target_type) if TYPE_CHECKING: - assert_type(s.astype("timedelta64[Y]"), "TimedeltaSeries") - assert_type(s.astype("timedelta64[M]"), "TimedeltaSeries") - assert_type(s.astype("timedelta64[W]"), "TimedeltaSeries") - assert_type(s.astype("timedelta64[D]"), "TimedeltaSeries") - assert_type(s.astype("timedelta64[h]"), "TimedeltaSeries") - assert_type(s.astype("timedelta64[m]"), "TimedeltaSeries") - assert_type(s.astype("timedelta64[s]"), "TimedeltaSeries") - assert_type(s.astype("timedelta64[ms]"), "TimedeltaSeries") - assert_type(s.astype("timedelta64[us]"), "TimedeltaSeries") - assert_type(s.astype("timedelta64[μs]"), "TimedeltaSeries") - assert_type(s.astype("timedelta64[ns]"), "TimedeltaSeries") - assert_type(s.astype("timedelta64[ps]"), "TimedeltaSeries") - assert_type(s.astype("timedelta64[fs]"), "TimedeltaSeries") - assert_type(s.astype("timedelta64[as]"), "TimedeltaSeries") + assert_type(s.astype("timedelta64[Y]"), "pd.Series[pd.Timedelta]") + assert_type(s.astype("timedelta64[M]"), "pd.Series[pd.Timedelta]") + assert_type(s.astype("timedelta64[W]"), "pd.Series[pd.Timedelta]") + assert_type(s.astype("timedelta64[D]"), "pd.Series[pd.Timedelta]") + assert_type(s.astype("timedelta64[h]"), "pd.Series[pd.Timedelta]") + assert_type(s.astype("timedelta64[m]"), "pd.Series[pd.Timedelta]") + assert_type(s.astype("timedelta64[s]"), "pd.Series[pd.Timedelta]") + assert_type(s.astype("timedelta64[ms]"), "pd.Series[pd.Timedelta]") + assert_type(s.astype("timedelta64[us]"), "pd.Series[pd.Timedelta]") + assert_type(s.astype("timedelta64[μs]"), "pd.Series[pd.Timedelta]") + assert_type(s.astype("timedelta64[ns]"), "pd.Series[pd.Timedelta]") + assert_type(s.astype("timedelta64[ps]"), "pd.Series[pd.Timedelta]") + assert_type(s.astype("timedelta64[fs]"), "pd.Series[pd.Timedelta]") + assert_type(s.astype("timedelta64[as]"), "pd.Series[pd.Timedelta]") # numpy timedelta64 type codes - assert_type(s.astype("m8[Y]"), "TimedeltaSeries") - assert_type(s.astype("m8[M]"), "TimedeltaSeries") - assert_type(s.astype("m8[W]"), "TimedeltaSeries") - assert_type(s.astype("m8[D]"), "TimedeltaSeries") - assert_type(s.astype("m8[h]"), "TimedeltaSeries") - assert_type(s.astype("m8[m]"), "TimedeltaSeries") - assert_type(s.astype("m8[s]"), "TimedeltaSeries") - assert_type(s.astype("m8[ms]"), "TimedeltaSeries") - assert_type(s.astype("m8[us]"), "TimedeltaSeries") - assert_type(s.astype("m8[μs]"), "TimedeltaSeries") - assert_type(s.astype("m8[ns]"), "TimedeltaSeries") - assert_type(s.astype("m8[ps]"), "TimedeltaSeries") - assert_type(s.astype("m8[fs]"), "TimedeltaSeries") - assert_type(s.astype("m8[as]"), "TimedeltaSeries") + assert_type(s.astype("m8[Y]"), "pd.Series[pd.Timedelta]") + assert_type(s.astype("m8[M]"), "pd.Series[pd.Timedelta]") + assert_type(s.astype("m8[W]"), "pd.Series[pd.Timedelta]") + assert_type(s.astype("m8[D]"), "pd.Series[pd.Timedelta]") + assert_type(s.astype("m8[h]"), "pd.Series[pd.Timedelta]") + assert_type(s.astype("m8[m]"), "pd.Series[pd.Timedelta]") + assert_type(s.astype("m8[s]"), "pd.Series[pd.Timedelta]") + assert_type(s.astype("m8[ms]"), "pd.Series[pd.Timedelta]") + assert_type(s.astype("m8[us]"), "pd.Series[pd.Timedelta]") + assert_type(s.astype("m8[μs]"), "pd.Series[pd.Timedelta]") + assert_type(s.astype("m8[ns]"), "pd.Series[pd.Timedelta]") + assert_type(s.astype("m8[ps]"), "pd.Series[pd.Timedelta]") + assert_type(s.astype("m8[fs]"), "pd.Series[pd.Timedelta]") + assert_type(s.astype("m8[as]"), "pd.Series[pd.Timedelta]") # numpy timedelta64 type codes - assert_type(s.astype(" None: check(assert_type(series / delta, "pd.Series[float]"), pd.Series, float) check(assert_type(series / [delta], "pd.Series[float]"), pd.Series, float) - check(assert_type(series / 1, "TimedeltaSeries"), pd.Series, pd.Timedelta) - check(assert_type(series / [1], "TimedeltaSeries"), pd.Series, pd.Timedelta) + check(assert_type(series / 1, "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) + check(assert_type(series / [1], "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) check(assert_type(series // delta, "pd.Series[int]"), pd.Series, np.longlong) check(assert_type(series // [delta], "pd.Series[int]"), pd.Series, int) - check(assert_type(series // 1, "TimedeltaSeries"), pd.Series, pd.Timedelta) - check(assert_type(series // [1], "TimedeltaSeries"), pd.Series, pd.Timedelta) + check(assert_type(series // 1, "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) + check( + assert_type(series // [1], "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta + ) check(assert_type(delta / series, "pd.Series[float]"), pd.Series, float) check(assert_type([delta] / series, "pd.Series[float]"), pd.Series, float) @@ -3235,10 +3231,10 @@ def test_timedelta_div() -> None: check(assert_type([delta] // series, "pd.Series[int]"), pd.Series, np.signedinteger) if TYPE_CHECKING_INVALID_USAGE: - 1 / series # type: ignore[operator] # pyright: ignore[reportOperatorIssue] - [1] / series # type: ignore[operator] # pyright: ignore[reportOperatorIssue] - 1 // series # type: ignore[operator] # pyright: ignore[reportOperatorIssue] - [1] // series # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + 1 / series + [1] / series + 1 // series + [1] // series def test_rank() -> None: @@ -3346,7 +3342,7 @@ def test_timedeltaseries_operators() -> None: pd.Timestamp, ) check( - assert_type(series + datetime.timedelta(1), TimedeltaSeries), + assert_type(series + datetime.timedelta(1), "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta, ) @@ -3356,7 +3352,7 @@ def test_timedeltaseries_operators() -> None: pd.Timestamp, ) check( - assert_type(series - datetime.timedelta(1), TimedeltaSeries), + assert_type(series - datetime.timedelta(1), "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta, ) @@ -3508,7 +3504,7 @@ def test_diff() -> None: pd.Series( [datetime.datetime.now().date(), datetime.datetime.now().date()] ).diff(), - "TimedeltaSeries", + "pd.Series[pd.Timedelta]", ), pd.Series, pd.Timedelta, @@ -3517,7 +3513,7 @@ def test_diff() -> None: # timestamp -> timedelta times = pd.Series([pd.Timestamp(0), pd.Timestamp(1)]) check( - assert_type(times.diff(), "TimedeltaSeries"), + assert_type(times.diff(), "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta, index_to_check_for_type=-1, @@ -3525,7 +3521,8 @@ def test_diff() -> None: # timedelta -> timedelta64 check( assert_type( - pd.Series([pd.Timedelta(0), pd.Timedelta(1)]).diff(), "TimedeltaSeries" + pd.Series([pd.Timedelta(0), pd.Timedelta(1)]).diff(), + "pd.Series[pd.Timedelta]", ), pd.Series, pd.Timedelta, @@ -3606,22 +3603,22 @@ def test_operator_constistency() -> None: # created for #748 s = pd.Series([1, 2, 3]) check( - assert_type(s * np.timedelta64(1, "s"), "TimedeltaSeries"), + assert_type(s * np.timedelta64(1, "s"), "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta, ) check( - assert_type(np.timedelta64(1, "s") * s, "TimedeltaSeries"), + assert_type(np.timedelta64(1, "s") * s, "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta, ) check( - assert_type(s.mul(np.timedelta64(1, "s")), "TimedeltaSeries"), + assert_type(s.mul(np.timedelta64(1, "s")), "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta, ) check( - assert_type(s.rmul(np.timedelta64(1, "s")), "TimedeltaSeries"), + assert_type(s.rmul(np.timedelta64(1, "s")), "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta, ) @@ -3899,7 +3896,7 @@ def test_series_items() -> None: def test_cumsum_timedelta() -> None: s = pd.Series(pd.to_timedelta([1, 2, 3], "h")) - check(assert_type(s.cumsum(), "TimedeltaSeries"), pd.Series, pd.Timedelta) + check(assert_type(s.cumsum(), "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) check( assert_type(pd.Timestamp(0) + s.cumsum(), "pd.Series[pd.Timestamp]"), pd.Series, diff --git a/tests/test_timefuncs.py b/tests/test_timefuncs.py index 67de2b7ae..d2b73a8b3 100644 --- a/tests/test_timefuncs.py +++ b/tests/test_timefuncs.py @@ -4,7 +4,6 @@ from typing import ( TYPE_CHECKING, Optional, - cast, ) from dateutil.relativedelta import ( @@ -50,7 +49,6 @@ if TYPE_CHECKING: from pandas.core.series import PeriodSeries # noqa: F401 - from pandas.core.series import TimedeltaSeries # noqa: F401 from tests import np_ndarray_bool @@ -183,26 +181,26 @@ def test_timestamp_timedelta_series_arithmetic() -> None: td1 = pd.to_timedelta([2, 3], "seconds") ts2 = pd.to_datetime(pd.Series(["2022-03-08", "2022-03-10"])) r1 = ts1 - ts2 - check(assert_type(r1, "TimedeltaSeries"), pd.Series, pd.Timedelta) + check(assert_type(r1, "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) r2 = r1 / td1 check(assert_type(r2, "pd.Series[float]"), pd.Series, float) r3 = r1 - td1 - check(assert_type(r3, "TimedeltaSeries"), pd.Series, pd.Timedelta) + check(assert_type(r3, "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) r4 = pd.Timedelta(5, "days") / r1 check(assert_type(r4, "pd.Series[float]"), pd.Series, float) sb = pd.Series([1, 2]) == pd.Series([1, 3]) check(assert_type(sb, "pd.Series[bool]"), pd.Series, np.bool_) r5 = sb * r1 - check(assert_type(r5, "TimedeltaSeries"), pd.Series, pd.Timedelta) + check(assert_type(r5, "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) r6 = r1 * 4 - check(assert_type(r6, "TimedeltaSeries"), pd.Series, pd.Timedelta) + check(assert_type(r6, "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) tsp1 = pd.Timestamp("2022-03-05") dt1 = dt.datetime(2022, 9, 1, 12, 5, 30) r7 = ts1 - tsp1 - check(assert_type(r7, "TimedeltaSeries"), pd.Series, pd.Timedelta) + check(assert_type(r7, "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) r8 = ts1 - dt1 - check(assert_type(r8, "TimedeltaSeries"), pd.Series, pd.Timedelta) + check(assert_type(r8, "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) def test_timestamp_dateoffset_arithmetic() -> None: @@ -223,17 +221,9 @@ def test_datetimeindex_plus_timedelta() -> None: 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, - ) + 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, - ) + 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) @@ -258,11 +248,7 @@ def test_datetimeindex_minus_timedelta() -> None: 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, - ) + 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) @@ -292,11 +278,7 @@ def test_timestamp_plus_timedelta_series() -> None: 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, "TimedeltaSeries"), - pd.Series, - pd.Timedelta, - ) + check(assert_type(std, "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) def test_timedelta_series_sum() -> None: @@ -357,16 +339,14 @@ def test_to_datetime_nat() -> None: ) check( assert_type( - pd.to_datetime("2021-03-01", errors="coerce"), - "pd.Timestamp | NaTType", + pd.to_datetime("2021-03-01", errors="coerce"), "pd.Timestamp | NaTType" ), pd.Timestamp, ) check( assert_type( - pd.to_datetime("not a date", errors="coerce"), - "pd.Timestamp | NaTType", + pd.to_datetime("not a date", errors="coerce"), "pd.Timestamp | NaTType" ), NaTType, ) @@ -425,11 +405,7 @@ def test_series_dt_accessors() -> None: dt.datetime, ) s0_local = s0.dt.tz_localize("UTC") - check( - assert_type(s0_local, "pd.Series[pd.Timestamp]"), - pd.Series, - pd.Timestamp, - ) + check(assert_type(s0_local, "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp) check( assert_type(s0.dt.tz_localize(None), "pd.Series[pd.Timestamp]"), pd.Series, @@ -567,7 +543,9 @@ def test_series_dt_accessors() -> None: i2 = pd.timedelta_range(start="1 day", periods=10) check(assert_type(i2, pd.TimedeltaIndex), pd.TimedeltaIndex) - check(assert_type(i2.to_series(), "TimedeltaSeries"), pd.Series, pd.Timedelta) + check( + assert_type(i2.to_series(), "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta + ) s2 = pd.Series(i2) @@ -584,12 +562,28 @@ def test_series_dt_accessors() -> None: check(assert_type(s2.dt.to_pytimedelta(), np.ndarray), np.ndarray) check(assert_type(s2.dt.total_seconds(), "pd.Series[float]"), pd.Series, float) check(assert_type(s2.dt.unit, TimeUnit), str) - check(assert_type(s2.dt.as_unit("s"), "TimedeltaSeries"), pd.Series, pd.Timedelta) - check(assert_type(s2.dt.as_unit("ms"), "TimedeltaSeries"), pd.Series, pd.Timedelta) - check(assert_type(s2.dt.as_unit("us"), "TimedeltaSeries"), pd.Series, pd.Timedelta) - check(assert_type(s2.dt.as_unit("ns"), "TimedeltaSeries"), pd.Series, pd.Timedelta) + check( + assert_type(s2.dt.as_unit("s"), "pd.Series[pd.Timedelta]"), + pd.Series, + pd.Timedelta, + ) + check( + assert_type(s2.dt.as_unit("ms"), "pd.Series[pd.Timedelta]"), + pd.Series, + pd.Timedelta, + ) + check( + assert_type(s2.dt.as_unit("us"), "pd.Series[pd.Timedelta]"), + pd.Series, + pd.Timedelta, + ) + check( + assert_type(s2.dt.as_unit("ns"), "pd.Series[pd.Timedelta]"), + pd.Series, + pd.Timedelta, + ) - # Checks for general Series other than Series[Timestamp] and TimedeltaSeries + # Checks for general Series other than pd.Series[pd.Timestamp] and pd.Series[pd.Timedelta] s4 = pd.Series([pd.Timestamp("2024-01-01"), pd.Timestamp("2024-01-02")]) @@ -615,29 +609,26 @@ def test_series_dt_accessors() -> None: pd.Timestamp, ) - s5 = cast( - "pd.Series[pd.Timedelta]", - pd.Series([pd.Timedelta("1 day"), pd.Timedelta("2 days")]), - ) + s5 = pd.Series([pd.Timedelta("1 day"), pd.Timedelta("2 days")]) check(assert_type(s5.dt.unit, TimeUnit), str) check( - assert_type(s5.dt.as_unit("s"), "pd.Series[pd.Timedelta]"), # type: ignore[assert-type] + assert_type(s5.dt.as_unit("s"), "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta, ) check( - assert_type(s5.dt.as_unit("ms"), "pd.Series[pd.Timedelta]"), # type: ignore[assert-type] + assert_type(s5.dt.as_unit("ms"), "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta, ) check( - assert_type(s5.dt.as_unit("us"), "pd.Series[pd.Timedelta]"), # type: ignore[assert-type] + assert_type(s5.dt.as_unit("us"), "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta, ) check( - assert_type(s5.dt.as_unit("ns"), "pd.Series[pd.Timedelta]"), # type: ignore[assert-type] + assert_type(s5.dt.as_unit("ns"), "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta, ) @@ -989,8 +980,8 @@ def test_to_timedelta_scalar() -> None: def test_to_timedelta_series() -> None: s = pd.Series([10, 20, 30, 40]) s2 = pd.Series(["10ms", "20ms", "30ms"]) - check(assert_type(pd.to_timedelta(s, "ms"), "TimedeltaSeries"), pd.Series) - check(assert_type(pd.to_timedelta(s2), "TimedeltaSeries"), pd.Series) + check(assert_type(pd.to_timedelta(s, "ms"), "pd.Series[pd.Timedelta]"), pd.Series) + check(assert_type(pd.to_timedelta(s2), "pd.Series[pd.Timedelta]"), pd.Series) def test_to_timedelta_index() -> None: @@ -1261,9 +1252,7 @@ def test_to_datetime_array() -> None: pd.DatetimeIndex, ) pd.to_datetime( - pd.Index([2451544.5, 2451545.5, 2451546.5]), - unit="D", - origin="julian", + pd.Index([2451544.5, 2451545.5, 2451546.5]), unit="D", origin="julian" ) check( assert_type( @@ -1399,7 +1388,7 @@ def test_timedelta64_and_arithmatic_operator() -> None: s1 = pd.Series(data=pd.date_range("1/1/2020", "2/1/2020")) s2 = pd.Series(data=pd.date_range("1/1/2021", "2/1/2021")) s3 = s2 - s1 - check(assert_type(s3, "TimedeltaSeries"), pd.Series, pd.Timedelta) + check(assert_type(s3, "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) td1 = pd.Timedelta(1, "D") check(assert_type(s2 - td1, "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp) # GH 758 @@ -1409,8 +1398,8 @@ def test_timedelta64_and_arithmatic_operator() -> None: td = np.timedelta64(1, "D") check(assert_type((s1 - td), "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp) check(assert_type((s1 + td), "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp) - check(assert_type((s3 - td), "TimedeltaSeries"), pd.Series, pd.Timedelta) - check(assert_type((s3 + td), "TimedeltaSeries"), pd.Series, pd.Timedelta) + check(assert_type((s3 - td), "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) + check(assert_type((s3 + td), "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta) check(assert_type((s3 / td), "pd.Series[float]"), pd.Series, float) if TYPE_CHECKING_INVALID_USAGE: r1 = s1 * td # type: ignore[var-annotated] @@ -1502,31 +1491,19 @@ def test_dateoffset_weekday() -> None: def test_date_range_unit(): check( - assert_type( - pd.date_range("1/1/2022", "2/1/2022", unit="s"), - pd.DatetimeIndex, - ), + assert_type(pd.date_range("1/1/2022", "2/1/2022", unit="s"), pd.DatetimeIndex), pd.DatetimeIndex, ) check( - assert_type( - pd.date_range("1/1/2022", "2/1/2022", unit="ms"), - pd.DatetimeIndex, - ), + assert_type(pd.date_range("1/1/2022", "2/1/2022", unit="ms"), pd.DatetimeIndex), pd.DatetimeIndex, ) check( - assert_type( - pd.date_range("1/1/2022", "2/1/2022", unit="us"), - pd.DatetimeIndex, - ), + assert_type(pd.date_range("1/1/2022", "2/1/2022", unit="us"), pd.DatetimeIndex), pd.DatetimeIndex, ) check( - assert_type( - pd.date_range("1/1/2022", "2/1/2022", unit="ns"), - pd.DatetimeIndex, - ), + assert_type(pd.date_range("1/1/2022", "2/1/2022", unit="ns"), pd.DatetimeIndex), pd.DatetimeIndex, ) @@ -1552,4 +1529,4 @@ def test_timestamp_sub_series() -> None: ts1 = pd.to_datetime(pd.Series(["2022-03-05", "2022-03-06"])) one_ts = ts1.iloc[0] check(assert_type(ts1.iloc[0], pd.Timestamp), pd.Timestamp) - check(assert_type(one_ts - ts1, "TimedeltaSeries"), pd.Series, pd.Timedelta) + check(assert_type(one_ts - ts1, "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta)