From 2a99f70c9152893380b93b3e7a20eaaded405846 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Diridollou?= Date: Tue, 19 Aug 2025 20:56:42 -0400 Subject: [PATCH 1/5] GH1327 Add overloead for date_range and timedelta_range --- pandas-stubs/core/groupby/generic.pyi | 6 +-- pandas-stubs/core/indexes/datetimes.pyi | 42 +++++++++++++++++++-- pandas-stubs/core/indexes/timedeltas.pyi | 47 ++++++++++++++++++++++-- tests/test_styler.py | 4 +- tests/test_timefuncs.py | 7 ++++ 5 files changed, 95 insertions(+), 11 deletions(-) diff --git a/pandas-stubs/core/groupby/generic.pyi b/pandas-stubs/core/groupby/generic.pyi index 75430c052..2b23b7589 100644 --- a/pandas-stubs/core/groupby/generic.pyi +++ b/pandas-stubs/core/groupby/generic.pyi @@ -213,17 +213,17 @@ class SeriesGroupBy(GroupBy[Series[S2]], Generic[S2, ByT]): _TT = TypeVar("_TT", bound=Literal[True, False]) # ty ignore needed because of https://github.com/astral-sh/ty/issues/157#issuecomment-3017337945 -class DFCallable1(Protocol[P]): # ty: ignore[invalid-argument-type] +class DFCallable1(Protocol[P]): def __call__( self, df: DataFrame, /, *args: P.args, **kwargs: P.kwargs ) -> Scalar | list | dict: ... -class DFCallable2(Protocol[P]): # ty: ignore[invalid-argument-type] +class DFCallable2(Protocol[P]): def __call__( self, df: DataFrame, /, *args: P.args, **kwargs: P.kwargs ) -> DataFrame | Series: ... -class DFCallable3(Protocol[P]): # ty: ignore[invalid-argument-type] +class DFCallable3(Protocol[P]): def __call__(self, df: Iterable, /, *args: P.args, **kwargs: P.kwargs) -> float: ... class DataFrameGroupBy(GroupBy[DataFrame], Generic[ByT, _TT]): diff --git a/pandas-stubs/core/indexes/datetimes.pyi b/pandas-stubs/core/indexes/datetimes.pyi index 8bcf703f7..41f92c595 100644 --- a/pandas-stubs/core/indexes/datetimes.pyi +++ b/pandas-stubs/core/indexes/datetimes.pyi @@ -102,11 +102,45 @@ class DatetimeIndex( self, periods: int = 1, freq: DateOffset | Timedelta | str | None = None ) -> Self: ... +@overload +def date_range( + end: str | DateAndDatetimeLike, + periods: int, + freq: str | timedelta | Timedelta | BaseOffset | None = None, + tz: TimeZones = None, + normalize: bool = False, + name: Hashable | None = None, + inclusive: IntervalClosedType = "both", + unit: TimeUnit | None = None, +) -> DatetimeIndex: ... +@overload +def date_range( + start: str | DateAndDatetimeLike, + periods: int, + freq: str | timedelta | Timedelta | BaseOffset | None = None, + tz: TimeZones = None, + normalize: bool = False, + name: Hashable | None = None, + inclusive: IntervalClosedType = "both", + unit: TimeUnit | None = None, +) -> DatetimeIndex: ... +@overload +def date_range( + start: str | DateAndDatetimeLike | None, + end: str | DateAndDatetimeLike | None, + freq: str | timedelta | Timedelta | BaseOffset | None = None, + tz: TimeZones = None, + normalize: bool = False, + name: Hashable | None = None, + inclusive: IntervalClosedType = "both", + unit: TimeUnit | None = None, +) -> DatetimeIndex: ... +@overload def date_range( - start: str | DateAndDatetimeLike | None = None, - end: str | DateAndDatetimeLike | None = None, - periods: int | None = None, - freq: str | timedelta | Timedelta | BaseOffset = "D", + start: str | DateAndDatetimeLike, + end: str | DateAndDatetimeLike, + periods: int, + freq: None = None, tz: TimeZones = None, normalize: bool = False, name: Hashable | None = None, diff --git a/pandas-stubs/core/indexes/timedeltas.pyi b/pandas-stubs/core/indexes/timedeltas.pyi index 28613dee3..07bb95556 100644 --- a/pandas-stubs/core/indexes/timedeltas.pyi +++ b/pandas-stubs/core/indexes/timedeltas.pyi @@ -79,10 +79,51 @@ class TimedeltaIndex( def to_series(self, index=..., name: Hashable = ...) -> TimedeltaSeries: ... def shift(self, periods: int = 1, freq=...) -> Self: ... +@overload def timedelta_range( - start: TimedeltaConvertibleTypes | None = None, - end: TimedeltaConvertibleTypes | None = None, - periods: int | None = None, + start: TimedeltaConvertibleTypes, + end: TimedeltaConvertibleTypes, + periods: int, + freq: None = None, + name: Hashable | None = None, + closed: Literal["left", "right"] | None = None, + *, + unit: None | str = ..., +) -> TimedeltaIndex: ... +@overload +def timedelta_range( + start: TimedeltaConvertibleTypes, + periods: int, + freq: None = None, + name: Hashable | None = None, + closed: Literal["left", "right"] | None = None, + *, + unit: None | str = ..., +) -> TimedeltaIndex: ... +@overload +def timedelta_range( + start: TimedeltaConvertibleTypes, + periods: int, + freq: str | DateOffset | Timedelta | dt.timedelta | None = None, + name: Hashable | None = None, + closed: Literal["left", "right"] | None = None, + *, + unit: None | str = ..., +) -> TimedeltaIndex: ... +@overload +def timedelta_range( + start: TimedeltaConvertibleTypes, + end: TimedeltaConvertibleTypes, + freq: str | DateOffset | Timedelta | dt.timedelta | None = None, + name: Hashable | None = None, + closed: Literal["left", "right"] | None = None, + *, + unit: None | str = ..., +) -> TimedeltaIndex: ... +@overload +def timedelta_range( + end: TimedeltaConvertibleTypes, + periods: int, freq: str | DateOffset | Timedelta | dt.timedelta | None = None, name: Hashable | None = None, closed: Literal["left", "right"] | None = None, diff --git a/tests/test_styler.py b/tests/test_styler.py index 0efff33ca..862d9e0f9 100644 --- a/tests/test_styler.py +++ b/tests/test_styler.py @@ -22,6 +22,7 @@ from pandas._typing import Scalar from tests import ( + PD_LTE_23, check, ensure_clean, ) @@ -141,7 +142,8 @@ def test_highlight_quantile() -> None: def test_loader() -> None: - check(assert_type(DF.style.loader, PackageLoader), PackageLoader) + if PD_LTE_23: + check(assert_type(DF.style.loader, PackageLoader), PackageLoader) def test_pipe() -> None: diff --git a/tests/test_timefuncs.py b/tests/test_timefuncs.py index 356d1516a..ed57fc1e2 100644 --- a/tests/test_timefuncs.py +++ b/tests/test_timefuncs.py @@ -1563,6 +1563,13 @@ def test_timedelta_range() -> None: pd.TimedeltaIndex, ) + if TYPE_CHECKING_INVALID_USAGE: + day1 = pd.Timedelta(1, unit="D") + day10 = pd.Timedelta(10, unit="D") + pd.timedelta_range( + day1, day10, periods=10, freq="D" # type: ignore[call-overload] # pyright: ignore[reportArgumentType] + ) + def test_dateoffset_freqstr() -> None: offset = DateOffset(minutes=10) From f26b7103df9880e7255aae7f73c40d65848febb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Diridollou?= Date: Tue, 19 Aug 2025 21:03:19 -0400 Subject: [PATCH 2/5] GH1327 Fix CI --- pandas-stubs/core/groupby/generic.pyi | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pandas-stubs/core/groupby/generic.pyi b/pandas-stubs/core/groupby/generic.pyi index 2b23b7589..75430c052 100644 --- a/pandas-stubs/core/groupby/generic.pyi +++ b/pandas-stubs/core/groupby/generic.pyi @@ -213,17 +213,17 @@ class SeriesGroupBy(GroupBy[Series[S2]], Generic[S2, ByT]): _TT = TypeVar("_TT", bound=Literal[True, False]) # ty ignore needed because of https://github.com/astral-sh/ty/issues/157#issuecomment-3017337945 -class DFCallable1(Protocol[P]): +class DFCallable1(Protocol[P]): # ty: ignore[invalid-argument-type] def __call__( self, df: DataFrame, /, *args: P.args, **kwargs: P.kwargs ) -> Scalar | list | dict: ... -class DFCallable2(Protocol[P]): +class DFCallable2(Protocol[P]): # ty: ignore[invalid-argument-type] def __call__( self, df: DataFrame, /, *args: P.args, **kwargs: P.kwargs ) -> DataFrame | Series: ... -class DFCallable3(Protocol[P]): +class DFCallable3(Protocol[P]): # ty: ignore[invalid-argument-type] def __call__(self, df: Iterable, /, *args: P.args, **kwargs: P.kwargs) -> float: ... class DataFrameGroupBy(GroupBy[DataFrame], Generic[ByT, _TT]): From 584e2882ae16be670e6932275385b7c0ca597f86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Diridollou?= Date: Wed, 20 Aug 2025 16:24:15 -0400 Subject: [PATCH 3/5] GH1327 PR feedback --- pandas-stubs/core/groupby/groupby.pyi | 6 +++--- pandas-stubs/core/indexes/datetimes.pyi | 21 ++++++++++-------- pandas-stubs/core/indexes/timedeltas.pyi | 27 ++++++++++++------------ tests/test_styler.py | 1 + tests/test_timefuncs.py | 9 +++++++- 5 files changed, 37 insertions(+), 27 deletions(-) diff --git a/pandas-stubs/core/groupby/groupby.pyi b/pandas-stubs/core/groupby/groupby.pyi index 1490e7d17..ec4458d6b 100644 --- a/pandas-stubs/core/groupby/groupby.pyi +++ b/pandas-stubs/core/groupby/groupby.pyi @@ -74,9 +74,9 @@ from pandas._typing import ( from pandas.plotting import PlotAccessor _ResamplerGroupBy: TypeAlias = ( - DatetimeIndexResamplerGroupby[NDFrameT] - | PeriodIndexResamplerGroupby[NDFrameT] - | TimedeltaIndexResamplerGroupby[NDFrameT] + DatetimeIndexResamplerGroupby[NDFrameT] # ty: ignore + | PeriodIndexResamplerGroupby[NDFrameT] # ty: ignore + | TimedeltaIndexResamplerGroupby[NDFrameT] # ty: ignore ) class GroupBy(BaseGroupBy[NDFrameT]): diff --git a/pandas-stubs/core/indexes/datetimes.pyi b/pandas-stubs/core/indexes/datetimes.pyi index 41f92c595..fb97f8d58 100644 --- a/pandas-stubs/core/indexes/datetimes.pyi +++ b/pandas-stubs/core/indexes/datetimes.pyi @@ -104,8 +104,9 @@ class DatetimeIndex( @overload def date_range( - end: str | DateAndDatetimeLike, - periods: int, + *, + end: str | DateAndDatetimeLike = ..., + periods: int = ..., freq: str | timedelta | Timedelta | BaseOffset | None = None, tz: TimeZones = None, normalize: bool = False, @@ -116,7 +117,8 @@ def date_range( @overload def date_range( start: str | DateAndDatetimeLike, - periods: int, + *, + periods: int = ..., freq: str | timedelta | Timedelta | BaseOffset | None = None, tz: TimeZones = None, normalize: bool = False, @@ -126,8 +128,9 @@ def date_range( ) -> DatetimeIndex: ... @overload def date_range( - start: str | DateAndDatetimeLike | None, - end: str | DateAndDatetimeLike | None, + start: str | DateAndDatetimeLike = ..., + end: str | DateAndDatetimeLike = ..., + *, freq: str | timedelta | Timedelta | BaseOffset | None = None, tz: TimeZones = None, normalize: bool = False, @@ -137,10 +140,10 @@ def date_range( ) -> DatetimeIndex: ... @overload def date_range( - start: str | DateAndDatetimeLike, - end: str | DateAndDatetimeLike, - periods: int, - freq: None = None, + start: str | DateAndDatetimeLike = ..., + end: str | DateAndDatetimeLike = ..., + periods: int = ..., + *, tz: TimeZones = None, normalize: bool = False, name: Hashable | None = None, diff --git a/pandas-stubs/core/indexes/timedeltas.pyi b/pandas-stubs/core/indexes/timedeltas.pyi index 07bb95556..221776afc 100644 --- a/pandas-stubs/core/indexes/timedeltas.pyi +++ b/pandas-stubs/core/indexes/timedeltas.pyi @@ -81,19 +81,18 @@ class TimedeltaIndex( @overload def timedelta_range( - start: TimedeltaConvertibleTypes, - end: TimedeltaConvertibleTypes, - periods: int, - freq: None = None, + start: TimedeltaConvertibleTypes = ..., + end: TimedeltaConvertibleTypes = ..., + periods: int = ..., + *, name: Hashable | None = None, closed: Literal["left", "right"] | None = None, - *, unit: None | str = ..., ) -> TimedeltaIndex: ... @overload def timedelta_range( - start: TimedeltaConvertibleTypes, - periods: int, + start: TimedeltaConvertibleTypes = ..., + periods: int = ..., freq: None = None, name: Hashable | None = None, closed: Literal["left", "right"] | None = None, @@ -102,31 +101,31 @@ def timedelta_range( ) -> TimedeltaIndex: ... @overload def timedelta_range( - start: TimedeltaConvertibleTypes, - periods: int, + start: TimedeltaConvertibleTypes = ..., + *, + periods: int = ..., freq: str | DateOffset | Timedelta | dt.timedelta | None = None, name: Hashable | None = None, closed: Literal["left", "right"] | None = None, - *, unit: None | str = ..., ) -> TimedeltaIndex: ... @overload def timedelta_range( - start: TimedeltaConvertibleTypes, - end: TimedeltaConvertibleTypes, + start: TimedeltaConvertibleTypes = ..., + end: TimedeltaConvertibleTypes = ..., + *, freq: str | DateOffset | Timedelta | dt.timedelta | None = None, name: Hashable | None = None, closed: Literal["left", "right"] | None = None, - *, unit: None | str = ..., ) -> TimedeltaIndex: ... @overload def timedelta_range( + *, end: TimedeltaConvertibleTypes, periods: int, freq: str | DateOffset | Timedelta | dt.timedelta | None = None, name: Hashable | None = None, closed: Literal["left", "right"] | None = None, - *, unit: None | str = ..., ) -> TimedeltaIndex: ... diff --git a/tests/test_styler.py b/tests/test_styler.py index 862d9e0f9..49cb3f845 100644 --- a/tests/test_styler.py +++ b/tests/test_styler.py @@ -143,6 +143,7 @@ def test_highlight_quantile() -> None: def test_loader() -> None: if PD_LTE_23: + # see GH62123 for pandas main repo, type changes in pandas 3.0 check(assert_type(DF.style.loader, PackageLoader), PackageLoader) diff --git a/tests/test_timefuncs.py b/tests/test_timefuncs.py index ed57fc1e2..5a497c450 100644 --- a/tests/test_timefuncs.py +++ b/tests/test_timefuncs.py @@ -1567,7 +1567,7 @@ def test_timedelta_range() -> None: day1 = pd.Timedelta(1, unit="D") day10 = pd.Timedelta(10, unit="D") pd.timedelta_range( - day1, day10, periods=10, freq="D" # type: ignore[call-overload] # pyright: ignore[reportArgumentType] + day1, day10, 10, "D" # type: ignore[call-overload] # pyright: ignore[reportArgumentType] ) @@ -1750,6 +1750,13 @@ def test_creating_date_range() -> None: dr = pd.date_range(start="2021-12-01", periods=24, freq="h") check(assert_type(dr.strftime("%H:%M:%S"), pd.Index), pd.Index, str) + if TYPE_CHECKING_INVALID_USAGE: + day1 = pd.Timestamp("2023-04-03") + day10 = pd.Timedelta("2023-04-08") + pd.date_range( + day1, day10, 10, "D" # type: ignore[call-overload] # pyright: ignore[reportCallIssue] + ) + def test_timestamp_to_list_add() -> None: # https://github.com/microsoft/python-type-stubs/issues/110 From 56be8d44ea6a3d2e75a0a5b41bb75ab4638276bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Diridollou?= Date: Thu, 21 Aug 2025 17:44:39 -0400 Subject: [PATCH 4/5] GH1327 Revert changes to start clean --- pandas-stubs/core/indexes/datetimes.pyi | 45 +++-------------------- pandas-stubs/core/indexes/timedeltas.pyi | 46 ++---------------------- tests/test_timefuncs.py | 14 -------- 3 files changed, 7 insertions(+), 98 deletions(-) diff --git a/pandas-stubs/core/indexes/datetimes.pyi b/pandas-stubs/core/indexes/datetimes.pyi index fb97f8d58..8bcf703f7 100644 --- a/pandas-stubs/core/indexes/datetimes.pyi +++ b/pandas-stubs/core/indexes/datetimes.pyi @@ -102,48 +102,11 @@ class DatetimeIndex( self, periods: int = 1, freq: DateOffset | Timedelta | str | None = None ) -> Self: ... -@overload def date_range( - *, - end: str | DateAndDatetimeLike = ..., - periods: int = ..., - freq: str | timedelta | Timedelta | BaseOffset | None = None, - tz: TimeZones = None, - normalize: bool = False, - name: Hashable | None = None, - inclusive: IntervalClosedType = "both", - unit: TimeUnit | None = None, -) -> DatetimeIndex: ... -@overload -def date_range( - start: str | DateAndDatetimeLike, - *, - periods: int = ..., - freq: str | timedelta | Timedelta | BaseOffset | None = None, - tz: TimeZones = None, - normalize: bool = False, - name: Hashable | None = None, - inclusive: IntervalClosedType = "both", - unit: TimeUnit | None = None, -) -> DatetimeIndex: ... -@overload -def date_range( - start: str | DateAndDatetimeLike = ..., - end: str | DateAndDatetimeLike = ..., - *, - freq: str | timedelta | Timedelta | BaseOffset | None = None, - tz: TimeZones = None, - normalize: bool = False, - name: Hashable | None = None, - inclusive: IntervalClosedType = "both", - unit: TimeUnit | None = None, -) -> DatetimeIndex: ... -@overload -def date_range( - start: str | DateAndDatetimeLike = ..., - end: str | DateAndDatetimeLike = ..., - periods: int = ..., - *, + start: str | DateAndDatetimeLike | None = None, + end: str | DateAndDatetimeLike | None = None, + periods: int | None = None, + freq: str | timedelta | Timedelta | BaseOffset = "D", tz: TimeZones = None, normalize: bool = False, name: Hashable | None = None, diff --git a/pandas-stubs/core/indexes/timedeltas.pyi b/pandas-stubs/core/indexes/timedeltas.pyi index 221776afc..28613dee3 100644 --- a/pandas-stubs/core/indexes/timedeltas.pyi +++ b/pandas-stubs/core/indexes/timedeltas.pyi @@ -79,53 +79,13 @@ class TimedeltaIndex( def to_series(self, index=..., name: Hashable = ...) -> TimedeltaSeries: ... def shift(self, periods: int = 1, freq=...) -> Self: ... -@overload def timedelta_range( - start: TimedeltaConvertibleTypes = ..., - end: TimedeltaConvertibleTypes = ..., - periods: int = ..., - *, - name: Hashable | None = None, - closed: Literal["left", "right"] | None = None, - unit: None | str = ..., -) -> TimedeltaIndex: ... -@overload -def timedelta_range( - start: TimedeltaConvertibleTypes = ..., - periods: int = ..., - freq: None = None, - name: Hashable | None = None, - closed: Literal["left", "right"] | None = None, - *, - unit: None | str = ..., -) -> TimedeltaIndex: ... -@overload -def timedelta_range( - start: TimedeltaConvertibleTypes = ..., - *, - periods: int = ..., + start: TimedeltaConvertibleTypes | None = None, + end: TimedeltaConvertibleTypes | None = None, + periods: int | None = None, freq: str | DateOffset | Timedelta | dt.timedelta | None = None, name: Hashable | None = None, closed: Literal["left", "right"] | None = None, - unit: None | str = ..., -) -> TimedeltaIndex: ... -@overload -def timedelta_range( - start: TimedeltaConvertibleTypes = ..., - end: TimedeltaConvertibleTypes = ..., *, - freq: str | DateOffset | Timedelta | dt.timedelta | None = None, - name: Hashable | None = None, - closed: Literal["left", "right"] | None = None, - unit: None | str = ..., -) -> TimedeltaIndex: ... -@overload -def timedelta_range( - *, - end: TimedeltaConvertibleTypes, - periods: int, - freq: str | DateOffset | Timedelta | dt.timedelta | None = None, - name: Hashable | None = None, - closed: Literal["left", "right"] | None = None, unit: None | str = ..., ) -> TimedeltaIndex: ... diff --git a/tests/test_timefuncs.py b/tests/test_timefuncs.py index 5a497c450..356d1516a 100644 --- a/tests/test_timefuncs.py +++ b/tests/test_timefuncs.py @@ -1563,13 +1563,6 @@ def test_timedelta_range() -> None: pd.TimedeltaIndex, ) - if TYPE_CHECKING_INVALID_USAGE: - day1 = pd.Timedelta(1, unit="D") - day10 = pd.Timedelta(10, unit="D") - pd.timedelta_range( - day1, day10, 10, "D" # type: ignore[call-overload] # pyright: ignore[reportArgumentType] - ) - def test_dateoffset_freqstr() -> None: offset = DateOffset(minutes=10) @@ -1750,13 +1743,6 @@ def test_creating_date_range() -> None: dr = pd.date_range(start="2021-12-01", periods=24, freq="h") check(assert_type(dr.strftime("%H:%M:%S"), pd.Index), pd.Index, str) - if TYPE_CHECKING_INVALID_USAGE: - day1 = pd.Timestamp("2023-04-03") - day10 = pd.Timedelta("2023-04-08") - pd.date_range( - day1, day10, 10, "D" # type: ignore[call-overload] # pyright: ignore[reportCallIssue] - ) - def test_timestamp_to_list_add() -> None: # https://github.com/microsoft/python-type-stubs/issues/110 From fc35578c7e1266552f64bafce56e4df6fa1a5664 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Diridollou?= Date: Thu, 21 Aug 2025 18:28:55 -0400 Subject: [PATCH 5/5] GH1327 PR Feedback --- pandas-stubs/core/indexes/datetimes.pyi | 43 +++++++++- pandas-stubs/core/indexes/timedeltas.pyi | 36 +++++++- tests/test_timefuncs.py | 104 +++++++++++++++++++++++ 3 files changed, 176 insertions(+), 7 deletions(-) diff --git a/pandas-stubs/core/indexes/datetimes.pyi b/pandas-stubs/core/indexes/datetimes.pyi index 8bcf703f7..29c8f1123 100644 --- a/pandas-stubs/core/indexes/datetimes.pyi +++ b/pandas-stubs/core/indexes/datetimes.pyi @@ -102,11 +102,46 @@ class DatetimeIndex( self, periods: int = 1, freq: DateOffset | Timedelta | str | None = None ) -> Self: ... +@overload +def date_range( + start: str | DateAndDatetimeLike, + end: str | DateAndDatetimeLike, + freq: str | timedelta | Timedelta | BaseOffset | None = None, + tz: TimeZones = None, + normalize: bool = False, + name: Hashable | None = None, + inclusive: IntervalClosedType = "both", + unit: TimeUnit | None = None, +) -> DatetimeIndex: ... +@overload def date_range( - start: str | DateAndDatetimeLike | None = None, - end: str | DateAndDatetimeLike | None = None, - periods: int | None = None, - freq: str | timedelta | Timedelta | BaseOffset = "D", + start: str | DateAndDatetimeLike, + end: str | DateAndDatetimeLike, + periods: int, + tz: TimeZones = None, + normalize: bool = False, + name: Hashable | None = None, + inclusive: IntervalClosedType = "both", + unit: TimeUnit | None = None, +) -> DatetimeIndex: ... +@overload +def date_range( + start: str | DateAndDatetimeLike, + *, + periods: int, + freq: str | timedelta | Timedelta | BaseOffset | None = None, + tz: TimeZones = None, + normalize: bool = False, + name: Hashable | None = None, + inclusive: IntervalClosedType = "both", + unit: TimeUnit | None = None, +) -> DatetimeIndex: ... +@overload +def date_range( + *, + end: str | DateAndDatetimeLike, + periods: int, + freq: str | timedelta | Timedelta | BaseOffset | None = None, tz: TimeZones = None, normalize: bool = False, name: Hashable | None = None, diff --git a/pandas-stubs/core/indexes/timedeltas.pyi b/pandas-stubs/core/indexes/timedeltas.pyi index 28613dee3..eb0d073c1 100644 --- a/pandas-stubs/core/indexes/timedeltas.pyi +++ b/pandas-stubs/core/indexes/timedeltas.pyi @@ -79,13 +79,43 @@ class TimedeltaIndex( def to_series(self, index=..., name: Hashable = ...) -> TimedeltaSeries: ... def shift(self, periods: int = 1, freq=...) -> Self: ... +@overload def timedelta_range( - start: TimedeltaConvertibleTypes | None = None, - end: TimedeltaConvertibleTypes | None = None, - periods: int | None = None, + start: TimedeltaConvertibleTypes, + end: TimedeltaConvertibleTypes, + *, + freq: str | DateOffset | Timedelta | dt.timedelta | None = None, + name: Hashable | None = None, + closed: Literal["left", "right"] | None = None, + unit: None | str = ..., +) -> TimedeltaIndex: ... +@overload +def timedelta_range( + *, + end: TimedeltaConvertibleTypes, + periods: int, + freq: str | DateOffset | Timedelta | dt.timedelta | None = None, + name: Hashable | None = None, + closed: Literal["left", "right"] | None = None, + unit: None | str = ..., +) -> TimedeltaIndex: ... +@overload +def timedelta_range( + start: TimedeltaConvertibleTypes, + *, + periods: int, freq: str | DateOffset | Timedelta | dt.timedelta | None = None, name: Hashable | None = None, closed: Literal["left", "right"] | None = None, + unit: None | str = ..., +) -> TimedeltaIndex: ... +@overload +def timedelta_range( + start: TimedeltaConvertibleTypes, + end: TimedeltaConvertibleTypes, + periods: int, *, + name: Hashable | None = None, + closed: Literal["left", "right"] | None = None, unit: None | str = ..., ) -> TimedeltaIndex: ... diff --git a/tests/test_timefuncs.py b/tests/test_timefuncs.py index 356d1516a..cf7c0bf5c 100644 --- a/tests/test_timefuncs.py +++ b/tests/test_timefuncs.py @@ -1705,6 +1705,110 @@ def test_date_range_unit(): ) +def test_date_range_overloads() -> None: + """Test different overloads of pd.date_range (GH1327).""" + t1 = pd.Timestamp("2023-04-05") + t2 = pd.Timestamp("2023-05-05") + # start end (freq None) + check(assert_type(pd.date_range(t1, t2), pd.DatetimeIndex), pd.DatetimeIndex) + # start end positional (freq None) + check( + assert_type(pd.date_range(start=t1, end=t2), pd.DatetimeIndex), pd.DatetimeIndex + ) + # start periods (freq None) + check( + assert_type(pd.date_range(start=t1, periods=10), pd.DatetimeIndex), + pd.DatetimeIndex, + ) + # end periods (freq None) + check( + assert_type(pd.date_range(end=t2, periods=10), pd.DatetimeIndex), + pd.DatetimeIndex, + ) + # start periods (freq None) + check(assert_type(pd.date_range(t1, t2, 10), pd.DatetimeIndex), pd.DatetimeIndex) + # start end periods + check( + assert_type(pd.date_range(start=t1, end=t2, periods=10), pd.DatetimeIndex), + pd.DatetimeIndex, + ) + # start end freq + check( + assert_type(pd.date_range(start=t1, end=t2, freq="ME"), pd.DatetimeIndex), + pd.DatetimeIndex, + ) + # start periods freq + check( + assert_type(pd.date_range(start=t1, periods=10, freq="ME"), pd.DatetimeIndex), + pd.DatetimeIndex, + ) + + if TYPE_CHECKING_INVALID_USAGE: + pd.date_range(t1) # type: ignore[call-overload] # pyright: ignore[reportCallIssue] + pd.date_range(start=t1) # type: ignore[call-overload] # pyright: ignore[reportCallIssue] + pd.date_range(end=t1) # type: ignore[call-overload] # pyright: ignore[reportCallIssue] + pd.date_range(periods=10) # type: ignore[call-overload] # pyright: ignore[reportCallIssue] + pd.date_range(freq="BD") # type: ignore[call-overload] # pyright: ignore[reportCallIssue] + pd.date_range(start=t1, end=t2, periods=10, freq="BD") # type: ignore[call-overload] # pyright: ignore[reportCallIssue] + + +def test_timedelta_range_overloads() -> None: + """Test different overloads of pd.timedelta_range (GH1327).""" + t1 = "1 day" + t2 = "20 day" + # start end (freq None) + check(assert_type(pd.timedelta_range(t1, t2), pd.TimedeltaIndex), pd.TimedeltaIndex) + # start end positional (freq None) + check( + assert_type(pd.timedelta_range(start=t1, end=t2), pd.TimedeltaIndex), + pd.TimedeltaIndex, + ) + # start periods (freq None) + check( + assert_type(pd.timedelta_range(start=t1, periods=10), pd.TimedeltaIndex), + pd.TimedeltaIndex, + ) + # end periods (freq None) + check( + assert_type(pd.timedelta_range(end=t2, periods=10), pd.TimedeltaIndex), + pd.TimedeltaIndex, + ) + # start periods (freq None) + check( + assert_type(pd.timedelta_range(t1, t2, 10), pd.TimedeltaIndex), + pd.TimedeltaIndex, + ) + # start end periods + check( + assert_type( + pd.timedelta_range(start=t1, end=t2, periods=10), pd.TimedeltaIndex + ), + pd.TimedeltaIndex, + ) + # start end freq + check( + assert_type( + pd.timedelta_range(start=t1, end=t2, freq="48h"), pd.TimedeltaIndex + ), + pd.TimedeltaIndex, + ) + # start periods freq + check( + assert_type( + pd.timedelta_range(start=t1, periods=10, freq="48h"), pd.TimedeltaIndex + ), + pd.TimedeltaIndex, + ) + + if TYPE_CHECKING_INVALID_USAGE: + pd.timedelta_range(t1) # type: ignore[call-overload] # pyright: ignore[reportCallIssue] + pd.timedelta_range(start=t1) # type: ignore[call-overload] # pyright: ignore[reportCallIssue] + pd.timedelta_range(end=t1) # type: ignore[call-overload] # pyright: ignore[reportCallIssue] + pd.timedelta_range(periods=10) # type: ignore[call-overload] # pyright: ignore[reportCallIssue] + pd.timedelta_range(freq="BD") # type: ignore[call-overload] # pyright: ignore[reportCallIssue] + pd.timedelta_range(start=t1, end=t2, periods=10, freq="BD") # type: ignore[call-overload] # pyright: ignore[reportCallIssue] + + def test_DatetimeIndex_sub_timedelta() -> None: # GH838 check(