From 6a1015aafa88b524b036a94e46f3e6af29476de5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Diridollou?= Date: Thu, 9 Oct 2025 19:47:16 -0400 Subject: [PATCH 1/5] GH1418 Disallow pd.Timedelta * bool --- pandas-stubs/_libs/tslibs/timedeltas.pyi | 5 +++++ tests/indexes/arithmetic/timedelta/test_mul.py | 10 ++++++++++ 2 files changed, 15 insertions(+) diff --git a/pandas-stubs/_libs/tslibs/timedeltas.pyi b/pandas-stubs/_libs/tslibs/timedeltas.pyi index 1b8406a51..4c08f65cd 100644 --- a/pandas-stubs/_libs/tslibs/timedeltas.pyi +++ b/pandas-stubs/_libs/tslibs/timedeltas.pyi @@ -8,6 +8,7 @@ from typing import ( ClassVar, Literal, NamedTuple, + Never, TypeAlias, overload, ) @@ -209,12 +210,16 @@ class Timedelta(timedelta): def __abs__(self) -> Self: ... # Override due to more types supported than timedelta @overload # type: ignore[override] + def __mul__(self, other: bool) -> Never: ... + @overload def __mul__(self, other: float) -> Self: ... @overload def __mul__( self, other: np_ndarray[ShapeT, np.bool_ | np.integer | np.floating] ) -> np_ndarray[ShapeT, np.timedelta64]: ... @overload + def __rmul__(self, other: bool) -> Never: ... + @overload def __rmul__(self, other: float) -> Self: ... @overload def __rmul__( diff --git a/tests/indexes/arithmetic/timedelta/test_mul.py b/tests/indexes/arithmetic/timedelta/test_mul.py index 590dc57d6..262c65823 100644 --- a/tests/indexes/arithmetic/timedelta/test_mul.py +++ b/tests/indexes/arithmetic/timedelta/test_mul.py @@ -113,3 +113,13 @@ def test_mul_pd_index(left: "pd.Index[pd.Timedelta]") -> None: check(assert_type(f * left, "pd.Index[pd.Timedelta]"), pd.Index, timedelta) if TYPE_CHECKING_INVALID_USAGE: _1 = c * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + + +def test_mul_bool() -> None: + """Test checking that pd.Timedelta * bool is not allowed GH1418.""" + a = pd.Timedelta("1 day") + b = True + + if TYPE_CHECKING_INVALID_USAGE: + assert_type(a * b, Never) + assert_type(b * a, Never) From d1d673f80a754dd63eef52ff504aa7553ff2c295 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Diridollou?= Date: Thu, 9 Oct 2025 19:52:42 -0400 Subject: [PATCH 2/5] GH1418 Fix source of Never --- pandas-stubs/_libs/tslibs/timedeltas.pyi | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pandas-stubs/_libs/tslibs/timedeltas.pyi b/pandas-stubs/_libs/tslibs/timedeltas.pyi index 4c08f65cd..2cb5fccc7 100644 --- a/pandas-stubs/_libs/tslibs/timedeltas.pyi +++ b/pandas-stubs/_libs/tslibs/timedeltas.pyi @@ -8,7 +8,6 @@ from typing import ( ClassVar, Literal, NamedTuple, - Never, TypeAlias, overload, ) @@ -22,7 +21,10 @@ from pandas import ( Series, TimedeltaIndex, ) -from typing_extensions import Self +from typing_extensions import ( + Never, + Self, +) from pandas._libs.tslibs import ( BaseOffset, From 3c7f3a3fd0942cc0acaf9134887b92c576c44535 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Diridollou?= Date: Fri, 10 Oct 2025 15:30:31 -0400 Subject: [PATCH 3/5] GH1418 PR Feedback --- pandas-stubs/_libs/tslibs/timedeltas.pyi | 14 ++++------- .../indexes/arithmetic/timedelta/test_mul.py | 10 -------- tests/scalars/test_scalars.py | 1 - tests/scalars/timedelta/__init__.py | 0 tests/scalars/timedelta/test_arithmetic.py | 24 +++++++++++++++++++ 5 files changed, 28 insertions(+), 21 deletions(-) create mode 100644 tests/scalars/timedelta/__init__.py create mode 100644 tests/scalars/timedelta/test_arithmetic.py diff --git a/pandas-stubs/_libs/tslibs/timedeltas.pyi b/pandas-stubs/_libs/tslibs/timedeltas.pyi index 2cb5fccc7..2e98b8276 100644 --- a/pandas-stubs/_libs/tslibs/timedeltas.pyi +++ b/pandas-stubs/_libs/tslibs/timedeltas.pyi @@ -21,10 +21,7 @@ from pandas import ( Series, TimedeltaIndex, ) -from typing_extensions import ( - Never, - Self, -) +from typing_extensions import Self from pandas._libs.tslibs import ( BaseOffset, @@ -33,6 +30,7 @@ from pandas._libs.tslibs import ( from pandas._libs.tslibs.period import Period from pandas._libs.tslibs.timestamps import Timestamp from pandas._typing import ( + Just, ShapeT, TimeUnit, np_1darray, @@ -212,17 +210,13 @@ class Timedelta(timedelta): def __abs__(self) -> Self: ... # Override due to more types supported than timedelta @overload # type: ignore[override] - def __mul__(self, other: bool) -> Never: ... - @overload - def __mul__(self, other: float) -> Self: ... + def __mul__(self, other: Just[float] | Just[int]) -> Self: ... @overload def __mul__( self, other: np_ndarray[ShapeT, np.bool_ | np.integer | np.floating] ) -> np_ndarray[ShapeT, np.timedelta64]: ... @overload - def __rmul__(self, other: bool) -> Never: ... - @overload - def __rmul__(self, other: float) -> Self: ... + def __rmul__(self, other: Just[float] | Just[int]) -> Self: ... @overload def __rmul__( self, other: np_ndarray[ShapeT, np.bool_ | np.integer | np.floating] diff --git a/tests/indexes/arithmetic/timedelta/test_mul.py b/tests/indexes/arithmetic/timedelta/test_mul.py index 262c65823..590dc57d6 100644 --- a/tests/indexes/arithmetic/timedelta/test_mul.py +++ b/tests/indexes/arithmetic/timedelta/test_mul.py @@ -113,13 +113,3 @@ def test_mul_pd_index(left: "pd.Index[pd.Timedelta]") -> None: check(assert_type(f * left, "pd.Index[pd.Timedelta]"), pd.Index, timedelta) if TYPE_CHECKING_INVALID_USAGE: _1 = c * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue] - - -def test_mul_bool() -> None: - """Test checking that pd.Timedelta * bool is not allowed GH1418.""" - a = pd.Timedelta("1 day") - b = True - - if TYPE_CHECKING_INVALID_USAGE: - assert_type(a * b, Never) - assert_type(b * a, Never) diff --git a/tests/scalars/test_scalars.py b/tests/scalars/test_scalars.py index 03053901d..19b4aaf33 100644 --- a/tests/scalars/test_scalars.py +++ b/tests/scalars/test_scalars.py @@ -2063,5 +2063,4 @@ def test_period_methods() -> None: def test_nattype_hashable() -> None: # GH 827 - _aset = {pd.NaT} check(assert_type(pd.NaT.__hash__(), int), int) diff --git a/tests/scalars/timedelta/__init__.py b/tests/scalars/timedelta/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/scalars/timedelta/test_arithmetic.py b/tests/scalars/timedelta/test_arithmetic.py new file mode 100644 index 000000000..df57a86af --- /dev/null +++ b/tests/scalars/timedelta/test_arithmetic.py @@ -0,0 +1,24 @@ +"""Test file for arithmetic method on Timedelta objects.""" + +import pandas as pd +from typing_extensions import assert_type + +from tests import ( + TYPE_CHECKING_INVALID_USAGE, + check, +) + + +def test_mul_bool() -> None: + """Test checking that pd.Timedelta * bool is not allowed GH1418.""" + a = pd.Timedelta("1 day") + b = True + c = 1.0 + d = 5 + + check(assert_type(a * c, pd.Timedelta), pd.Timedelta) + check(assert_type(a * d, pd.Timedelta), pd.Timedelta) + + if TYPE_CHECKING_INVALID_USAGE: + _0 = a * b # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _1 = b * a # type: ignore[operator] # pyright: ignore[reportOperatorIssue] From 0cdd29e3c93d9ad1e1ce3ab2fcb5f21c27754380 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Diridollou?= Date: Fri, 10 Oct 2025 16:49:12 -0400 Subject: [PATCH 4/5] GH1418 PR Feedback --- tests/scalars/timedelta/test_arithmetic.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/scalars/timedelta/test_arithmetic.py b/tests/scalars/timedelta/test_arithmetic.py index df57a86af..27e22e11c 100644 --- a/tests/scalars/timedelta/test_arithmetic.py +++ b/tests/scalars/timedelta/test_arithmetic.py @@ -9,8 +9,8 @@ ) -def test_mul_bool() -> None: - """Test checking that pd.Timedelta * bool is not allowed GH1418.""" +def test_mul() -> None: + """Test checking that pd.Timedelta * int / float.""" a = pd.Timedelta("1 day") b = True c = 1.0 @@ -20,5 +20,6 @@ def test_mul_bool() -> None: check(assert_type(a * d, pd.Timedelta), pd.Timedelta) if TYPE_CHECKING_INVALID_USAGE: + # pd.Timedelta * bool is not allowed, see GH1418 _0 = a * b # type: ignore[operator] # pyright: ignore[reportOperatorIssue] _1 = b * a # type: ignore[operator] # pyright: ignore[reportOperatorIssue] From 43e895488fedfddc36eb238c88acfe65d02b31ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Diridollou?= Date: Fri, 10 Oct 2025 17:10:18 -0400 Subject: [PATCH 5/5] GH1418 PR Feedback --- tests/scalars/timedelta/test_arithmetic.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/scalars/timedelta/test_arithmetic.py b/tests/scalars/timedelta/test_arithmetic.py index 27e22e11c..e401ae032 100644 --- a/tests/scalars/timedelta/test_arithmetic.py +++ b/tests/scalars/timedelta/test_arithmetic.py @@ -15,11 +15,16 @@ def test_mul() -> None: b = True c = 1.0 d = 5 + e = 1 + 3.0j check(assert_type(a * c, pd.Timedelta), pd.Timedelta) + check(assert_type(c * a, pd.Timedelta), pd.Timedelta) check(assert_type(a * d, pd.Timedelta), pd.Timedelta) + check(assert_type(d * a, pd.Timedelta), pd.Timedelta) if TYPE_CHECKING_INVALID_USAGE: # pd.Timedelta * bool is not allowed, see GH1418 _0 = a * b # type: ignore[operator] # pyright: ignore[reportOperatorIssue] _1 = b * a # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _2 = a * e # type: ignore[operator] # pyright: ignore[reportOperatorIssue] + _3 = e * a # type: ignore[operator] # pyright: ignore[reportOperatorIssue]