Skip to content

Commit 1ebfe2b

Browse files
committed
feat: truediv, also pandas-dev/pandas#62712
1 parent 9fc0ace commit 1ebfe2b

File tree

8 files changed

+386
-23
lines changed

8 files changed

+386
-23
lines changed

pandas-stubs/_libs/tslibs/period.pyi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ class Period(PeriodMixin):
9999
# Ignored due to indecipherable error from mypy:
100100
# Forward operator "__add__" is not callable [misc]
101101
@overload
102-
def __radd__(self, other: _PeriodAddSub) -> Self: ... # type: ignore[misc]
102+
def __radd__(self, other: _PeriodAddSub) -> Self: ...
103103
@overload
104104
def __radd__(self, other: NaTType) -> NaTType: ...
105105
# Real signature is -> PeriodIndex, but conflicts with Index.__add__

pandas-stubs/_libs/tslibs/timedeltas.pyi

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,7 @@ class Timedelta(timedelta):
321321
# for le, lt ge and gt
322322
# Override due to more types supported than timedelta
323323
@overload # type: ignore[override]
324-
def __le__(self, other: timedelta | Timedelta | np.timedelta64) -> bool: ... # type: ignore[misc]
324+
def __le__(self, other: timedelta | Timedelta | np.timedelta64) -> bool: ...
325325
@overload
326326
def __le__(self, other: TimedeltaIndex) -> np_1darray[np.bool]: ...
327327
@overload
@@ -332,7 +332,7 @@ class Timedelta(timedelta):
332332
def __le__(self, other: Series[pd.Timedelta]) -> Series[bool]: ...
333333
# Override due to more types supported than timedelta
334334
@overload # type: ignore[override]
335-
def __lt__(self, other: timedelta | Timedelta | np.timedelta64) -> bool: ... # type: ignore[misc]
335+
def __lt__(self, other: timedelta | Timedelta | np.timedelta64) -> bool: ...
336336
@overload
337337
def __lt__(self, other: TimedeltaIndex) -> np_1darray[np.bool]: ...
338338
@overload
@@ -343,7 +343,7 @@ class Timedelta(timedelta):
343343
def __lt__(self, other: Series[pd.Timedelta]) -> Series[bool]: ...
344344
# Override due to more types supported than timedelta
345345
@overload # type: ignore[override]
346-
def __ge__(self, other: timedelta | Timedelta | np.timedelta64) -> bool: ... # type: ignore[misc]
346+
def __ge__(self, other: timedelta | Timedelta | np.timedelta64) -> bool: ...
347347
@overload
348348
def __ge__(self, other: TimedeltaIndex) -> np_1darray[np.bool]: ...
349349
@overload
@@ -354,7 +354,7 @@ class Timedelta(timedelta):
354354
def __ge__(self, other: Series[pd.Timedelta]) -> Series[bool]: ...
355355
# Override due to more types supported than timedelta
356356
@overload # type: ignore[override]
357-
def __gt__(self, other: timedelta | Timedelta | np.timedelta64) -> bool: ... # type: ignore[misc]
357+
def __gt__(self, other: timedelta | Timedelta | np.timedelta64) -> bool: ...
358358
@overload
359359
def __gt__(self, other: TimedeltaIndex) -> np_1darray[np.bool]: ...
360360
@overload

pandas-stubs/_libs/tslibs/timestamps.pyi

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ class Timestamp(datetime, SupportsIndex):
175175
# Mypy complains Forward operator "<inequality op>" is not callable, so ignore misc
176176
# for le, lt ge and gt
177177
@overload # type: ignore[override]
178-
def __le__(self, other: Timestamp | datetime | np.datetime64) -> bool: ... # type: ignore[misc]
178+
def __le__(self, other: Timestamp | datetime | np.datetime64) -> bool: ...
179179
@overload
180180
def __le__(self, other: DatetimeIndex) -> np_1darray[np.bool]: ...
181181
@overload
@@ -185,7 +185,7 @@ class Timestamp(datetime, SupportsIndex):
185185
@overload
186186
def __le__(self, other: Series[Timestamp]) -> Series[bool]: ...
187187
@overload # type: ignore[override]
188-
def __lt__(self, other: Timestamp | datetime | np.datetime64) -> bool: ... # type: ignore[misc]
188+
def __lt__(self, other: Timestamp | datetime | np.datetime64) -> bool: ...
189189
@overload
190190
def __lt__(self, other: DatetimeIndex) -> np_1darray[np.bool]: ...
191191
@overload
@@ -195,7 +195,7 @@ class Timestamp(datetime, SupportsIndex):
195195
@overload
196196
def __lt__(self, other: Series[Timestamp]) -> Series[bool]: ...
197197
@overload # type: ignore[override]
198-
def __ge__(self, other: Timestamp | datetime | np.datetime64) -> bool: ... # type: ignore[misc]
198+
def __ge__(self, other: Timestamp | datetime | np.datetime64) -> bool: ...
199199
@overload
200200
def __ge__(self, other: DatetimeIndex) -> np_1darray[np.bool]: ...
201201
@overload
@@ -205,7 +205,7 @@ class Timestamp(datetime, SupportsIndex):
205205
@overload
206206
def __ge__(self, other: Series[Timestamp]) -> Series[bool]: ...
207207
@overload # type: ignore[override]
208-
def __gt__(self, other: Timestamp | datetime | np.datetime64) -> bool: ... # type: ignore[misc]
208+
def __gt__(self, other: Timestamp | datetime | np.datetime64) -> bool: ...
209209
@overload
210210
def __gt__(self, other: DatetimeIndex) -> np_1darray[np.bool]: ...
211211
@overload
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import numpy as np
2+
from numpy import typing as npt # noqa: F401
3+
import pandas as pd
4+
import pytest
5+
from typing_extensions import assert_type
6+
7+
from tests import (
8+
TYPE_CHECKING_INVALID_USAGE,
9+
check,
10+
)
11+
12+
13+
@pytest.fixture
14+
def left() -> "pd.Index[float]":
15+
"""Left operand"""
16+
lo = pd.Index([1.0, 2.0, 3.0])
17+
return check(assert_type(lo, "pd.Index[float]"), pd.Index, np.floating)
18+
19+
20+
def test_truediv_py_scalar(left: "pd.Index[float]") -> None:
21+
"""Test pd.Index[float] / Python native scalars"""
22+
b, i, f, c = True, 1, 1.0, 1j
23+
24+
check(assert_type(left / b, "pd.Index[float]"), pd.Index, np.floating)
25+
check(assert_type(left / i, "pd.Index[float]"), pd.Index, np.floating)
26+
check(assert_type(left / f, "pd.Index[float]"), pd.Index, np.floating)
27+
check(assert_type(left / c, "pd.Index[complex]"), pd.Index, np.complexfloating)
28+
29+
check(assert_type(b / left, "pd.Index[float]"), pd.Index, np.floating)
30+
check(assert_type(i / left, "pd.Index[float]"), pd.Index, np.floating)
31+
check(assert_type(f / left, "pd.Index[float]"), pd.Index, np.floating)
32+
check(assert_type(c / left, "pd.Index[complex]"), pd.Index, np.complexfloating)
33+
34+
35+
def test_truediv_py_sequence(left: "pd.Index[float]") -> None:
36+
"""Test pd.Index[float] / Python native sequences"""
37+
b, i, f, c = [True, False, True], [2, 3, 5], [1.0, 2.0, 3.0], [1j, 1j, 4j]
38+
39+
check(assert_type(left / b, "pd.Index[float]"), pd.Index, np.floating)
40+
check(assert_type(left / i, "pd.Index[float]"), pd.Index, np.floating)
41+
check(assert_type(left / f, "pd.Index[float]"), pd.Index, np.floating)
42+
check(assert_type(left / c, "pd.Index[complex]"), pd.Index, np.complexfloating)
43+
44+
check(assert_type(b / left, "pd.Index[float]"), pd.Index, np.floating)
45+
check(assert_type(i / left, "pd.Index[float]"), pd.Index, np.floating)
46+
check(assert_type(f / left, "pd.Index[float]"), pd.Index, np.floating)
47+
check(assert_type(c / left, "pd.Index[complex]"), pd.Index, np.complexfloating)
48+
49+
50+
def test_truediv_numpy_array(left: "pd.Index[float]") -> None:
51+
"""Test pd.Index[float] / numpy arrays"""
52+
b = np.array([True, False, True], np.bool_)
53+
i = np.array([2, 3, 5], np.int64)
54+
f = np.array([1.0, 2.0, 3.0], np.float64)
55+
c = np.array([1.1j, 2.2j, 4.1j], np.complex128)
56+
57+
check(assert_type(left / b, "pd.Index[float]"), pd.Index, np.floating)
58+
check(assert_type(left / i, "pd.Index[float]"), pd.Index, np.floating)
59+
check(assert_type(left / f, "pd.Index[float]"), pd.Index, np.floating)
60+
check(assert_type(left / c, "pd.Index[complex]"), pd.Index, np.complexfloating)
61+
62+
# `numpy` typing gives the corresponding `ndarray`s in the static type
63+
# checking, where our `__rtruediv__` cannot override. At runtime, they return
64+
# `Series` with the correct element type.
65+
check(assert_type(b / left, "npt.NDArray[np.float64]"), pd.Index, np.floating)
66+
check(assert_type(i / left, "npt.NDArray[np.float64]"), pd.Index, np.floating)
67+
check(assert_type(f / left, "npt.NDArray[np.float64]"), pd.Index, np.floating)
68+
check(
69+
assert_type(c / left, "npt.NDArray[np.complex128]"),
70+
pd.Index,
71+
np.complexfloating,
72+
)
73+
74+
75+
def test_truediv_pd_scalar(left: "pd.Index[float]") -> None:
76+
"""Test pd.Index[float] / pandas scalars"""
77+
s, d = pd.Timestamp(2025, 9, 24), pd.Timedelta(seconds=1)
78+
79+
if TYPE_CHECKING_INVALID_USAGE:
80+
_00 = left / s # type: ignore[operator] # pyright: ignore[reportOperatorIssue]
81+
_01 = left / d # type: ignore[operator] # pyright: ignore[reportOperatorIssue]
82+
83+
if TYPE_CHECKING_INVALID_USAGE:
84+
_10 = s / left # type: ignore[operator] # pyright: ignore[reportOperatorIssue]
85+
check(assert_type(d / left, "pd.Index[pd.Timedelta]"), pd.Index, pd.Timedelta)
86+
87+
88+
def test_truediv_pd_index(left: "pd.Index[float]") -> None:
89+
"""Test pd.Index[float] / pandas Indexes"""
90+
b = pd.Index([True, False, True])
91+
i = pd.Index([2, 3, 5])
92+
f = pd.Index([1.0, 2.0, 3.0])
93+
c = pd.Index([1.1j, 2.2j, 4.1j])
94+
95+
check(assert_type(left / b, "pd.Index[float]"), pd.Index, np.floating)
96+
check(assert_type(left / i, "pd.Index[float]"), pd.Index, np.floating)
97+
check(assert_type(left / f, "pd.Index[float]"), pd.Index, np.floating)
98+
check(assert_type(left / c, "pd.Index[complex]"), pd.Index, np.complexfloating)
99+
100+
check(assert_type(b / left, "pd.Index[float]"), pd.Index, np.floating)
101+
check(assert_type(i / left, "pd.Index[float]"), pd.Index, np.floating)
102+
check(assert_type(f / left, "pd.Index[float]"), pd.Index, np.floating)
103+
check(assert_type(c / left, "pd.Index[complex]"), pd.Index, np.complexfloating)
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import numpy as np
2+
from numpy import typing as npt # noqa: F401
3+
import pandas as pd
4+
import pytest
5+
from typing_extensions import assert_type
6+
7+
from tests import (
8+
TYPE_CHECKING_INVALID_USAGE,
9+
check,
10+
)
11+
12+
13+
@pytest.fixture
14+
def left() -> "pd.Index[int]":
15+
"""Left operand"""
16+
lo = pd.Index([1, 2, 3])
17+
return check(assert_type(lo, "pd.Index[int]"), pd.Index, np.integer)
18+
19+
20+
def test_truediv_py_scalar(left: "pd.Index[int]") -> None:
21+
"""Test pd.Index[int] / Python native scalars"""
22+
b, i, f, c = True, 1, 1.0, 1j
23+
24+
check(assert_type(left / b, "pd.Index[float]"), pd.Index, np.floating)
25+
check(assert_type(left / i, "pd.Index[float]"), pd.Index, np.floating)
26+
check(assert_type(left / f, "pd.Index[float]"), pd.Index, np.floating)
27+
check(assert_type(left / c, "pd.Index[complex]"), pd.Index, np.complexfloating)
28+
29+
check(assert_type(b / left, "pd.Index[float]"), pd.Index, np.floating)
30+
check(assert_type(i / left, "pd.Index[float]"), pd.Index, np.floating)
31+
check(assert_type(f / left, "pd.Index[float]"), pd.Index, np.floating)
32+
check(assert_type(c / left, "pd.Index[complex]"), pd.Index, np.complexfloating)
33+
34+
35+
def test_truediv_py_sequence(left: "pd.Index[int]") -> None:
36+
"""Test pd.Index[int] / Python native sequences"""
37+
b, i, f, c = [True, False, True], [2, 3, 5], [1.0, 2.0, 3.0], [1j, 1j, 4j]
38+
39+
check(assert_type(left / b, "pd.Index[float]"), pd.Index, np.floating)
40+
check(assert_type(left / i, "pd.Index[float]"), pd.Index, np.floating)
41+
check(assert_type(left / f, "pd.Index[float]"), pd.Index, np.floating)
42+
check(assert_type(left / c, "pd.Index[complex]"), pd.Index, np.complexfloating)
43+
44+
check(assert_type(b / left, "pd.Index[float]"), pd.Index, np.floating)
45+
check(assert_type(i / left, "pd.Index[float]"), pd.Index, np.floating)
46+
check(assert_type(f / left, "pd.Index[float]"), pd.Index, np.floating)
47+
check(assert_type(c / left, "pd.Index[complex]"), pd.Index, np.complexfloating)
48+
49+
50+
def test_truediv_numpy_array(left: "pd.Index[int]") -> None:
51+
"""Test pd.Index[int] / numpy arrays"""
52+
b = np.array([True, False, True], np.bool_)
53+
i = np.array([2, 3, 5], np.int64)
54+
f = np.array([1.0, 2.0, 3.0], np.float64)
55+
c = np.array([1.1j, 2.2j, 4.1j], np.complex128)
56+
57+
check(assert_type(left / b, "pd.Index[float]"), pd.Index, np.floating)
58+
check(assert_type(left / i, "pd.Index[float]"), pd.Index, np.floating)
59+
check(assert_type(left / f, "pd.Index[float]"), pd.Index, np.floating)
60+
check(assert_type(left / c, "pd.Index[complex]"), pd.Index, np.complexfloating)
61+
62+
# `numpy` typing gives the corresponding `ndarray`s in the static type
63+
# checking, where our `__rtruediv__` cannot override. At runtime, they return
64+
# `Series` with the correct element type.
65+
check(assert_type(b / left, "npt.NDArray[np.float64]"), pd.Index, np.floating)
66+
check(assert_type(i / left, "npt.NDArray[np.float64]"), pd.Index, np.floating)
67+
check(assert_type(f / left, "npt.NDArray[np.float64]"), pd.Index, np.floating)
68+
check(
69+
assert_type(c / left, "npt.NDArray[np.complex128]"),
70+
pd.Index,
71+
np.complexfloating,
72+
)
73+
74+
75+
def test_truediv_pd_scalar(left: "pd.Index[int]") -> None:
76+
"""Test pd.Index[int] / pandas scalars"""
77+
s, d = pd.Timestamp(2025, 9, 24), pd.Timedelta(seconds=1)
78+
79+
if TYPE_CHECKING_INVALID_USAGE:
80+
_00 = left / s # type: ignore[operator] # pyright: ignore[reportOperatorIssue]
81+
_01 = left / d # type: ignore[operator] # pyright: ignore[reportOperatorIssue]
82+
83+
if TYPE_CHECKING_INVALID_USAGE:
84+
_10 = s / left # type: ignore[operator] # pyright: ignore[reportOperatorIssue]
85+
check(assert_type(d / left, "pd.Index[pd.Timedelta]"), pd.Index, pd.Timedelta)
86+
87+
88+
def test_truediv_pd_index(left: "pd.Index[int]") -> None:
89+
"""Test pd.Index[int] / pandas Indexes"""
90+
b = pd.Index([True, False, True])
91+
i = pd.Index([2, 3, 5])
92+
f = pd.Index([1.0, 2.0, 3.0])
93+
c = pd.Index([1.1j, 2.2j, 4.1j])
94+
95+
check(assert_type(left / b, "pd.Index[float]"), pd.Index, np.floating)
96+
check(assert_type(left / i, "pd.Index[float]"), pd.Index, np.floating)
97+
check(assert_type(left / f, "pd.Index[float]"), pd.Index, np.floating)
98+
check(assert_type(left / c, "pd.Index[complex]"), pd.Index, np.complexfloating)
99+
100+
check(assert_type(b / left, "pd.Index[float]"), pd.Index, np.floating)
101+
check(assert_type(i / left, "pd.Index[float]"), pd.Index, np.floating)
102+
check(assert_type(f / left, "pd.Index[float]"), pd.Index, np.floating)
103+
check(assert_type(c / left, "pd.Index[complex]"), pd.Index, np.complexfloating)

0 commit comments

Comments
 (0)