Skip to content

Commit 72208c7

Browse files
committed
feat: tests for subtraction
1 parent aac6cff commit 72208c7

File tree

5 files changed

+188
-26
lines changed

5 files changed

+188
-26
lines changed

pandas-stubs/core/series.pyi

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2799,7 +2799,7 @@ class Series(IndexOpsMixin[S1], NDFrame):
27992799
),
28002800
) -> TimedeltaSeries: ...
28012801
@overload
2802-
def sub( # type: ignore[overload-overlap]
2802+
def sub(
28032803
self: Series[Never],
28042804
other: datetime | np.datetime64 | np_ndarray_dt | Series[Timestamp],
28052805
level: Level | None = None,
@@ -2907,14 +2907,21 @@ class Series(IndexOpsMixin[S1], NDFrame):
29072907
axis: int = 0,
29082908
) -> Series[complex]: ...
29092909
@overload
2910+
def sub(
2911+
self: Series[Timestamp],
2912+
other: datetime | np.datetime64 | np_ndarray_dt | Series[Timestamp],
2913+
level: Level | None = None,
2914+
fill_value: float | None = None,
2915+
axis: int = 0,
2916+
) -> TimedeltaSeries: ...
2917+
@overload
29102918
def sub(
29112919
self: Series[Timestamp],
29122920
other: (
29132921
timedelta
29142922
| np.timedelta64
29152923
| np_ndarray_td
29162924
| TimedeltaIndex
2917-
| Series[Never]
29182925
| TimedeltaSeries
29192926
| BaseOffset
29202927
),
@@ -3008,6 +3015,8 @@ class Series(IndexOpsMixin[S1], NDFrame):
30083015
),
30093016
) -> Series[complex]: ...
30103017
@overload
3018+
def __rsub__(self: Series[Timestamp], other: np_ndarray_td) -> Never: ...
3019+
@overload
30113020
def __rsub__(
30123021
self: Series[Timestamp], other: datetime | np.datetime64 | np_ndarray_dt
30133022
) -> TimedeltaSeries: ...

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ mypy = "1.17.0"
3939
pandas = "2.3.1"
4040
pyarrow = ">=10.0.1"
4141
pytest = ">=7.1.2"
42-
pyright = ">=1.1.403"
42+
pyright = ">=1.1.404"
4343
ty = "^0.0.1a8"
4444
pyrefly = "^0.21.0"
4545
poethepoet = ">=0.16.5"
Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
from builtins import str as _str
1+
import sys
22
from typing import Any
33

44
import numpy as np
5+
from numpy import typing as npt # noqa: F401
56
import pandas as pd
67
from typing_extensions import assert_type
78

@@ -14,57 +15,67 @@ def test_add_py_scalar() -> None:
1415
"""Testpd.Series[str]+ Python native str"""
1516
r0 = "right"
1617

17-
check(assert_type(left + r0, "pd.Series[_str]"), pd.Series, _str)
18+
check(assert_type(left + r0, "pd.Series[str]"), pd.Series, str)
1819

19-
check(assert_type(r0 + left, "pd.Series[_str]"), pd.Series, _str)
20+
check(assert_type(r0 + left, "pd.Series[str]"), pd.Series, str)
2021

21-
check(assert_type(left.add(r0), "pd.Series[_str]"), pd.Series, _str)
22+
check(assert_type(left.add(r0), "pd.Series[str]"), pd.Series, str)
2223

23-
check(assert_type(left.radd(r0), "pd.Series[_str]"), pd.Series, _str)
24+
check(assert_type(left.radd(r0), "pd.Series[str]"), pd.Series, str)
2425

2526

2627
def test_add_py_sequence() -> None:
2728
"""Testpd.Series[str]+ Python native sequence"""
2829
r0 = ["a", "bc", "def"]
2930
r1 = tuple(r0)
3031

31-
check(assert_type(left + r0, "pd.Series[_str]"), pd.Series, _str)
32-
check(assert_type(left + r1, "pd.Series[_str]"), pd.Series, _str)
32+
check(assert_type(left + r0, "pd.Series[str]"), pd.Series, str)
33+
check(assert_type(left + r1, "pd.Series[str]"), pd.Series, str)
3334

34-
check(assert_type(r0 + left, "pd.Series[_str]"), pd.Series, _str)
35-
check(assert_type(r1 + left, "pd.Series[_str]"), pd.Series, _str)
35+
check(assert_type(r0 + left, "pd.Series[str]"), pd.Series, str)
36+
check(assert_type(r1 + left, "pd.Series[str]"), pd.Series, str)
3637

37-
check(assert_type(left.add(r0), "pd.Series[_str]"), pd.Series, _str)
38-
check(assert_type(left.add(r1), "pd.Series[_str]"), pd.Series, _str)
38+
check(assert_type(left.add(r0), "pd.Series[str]"), pd.Series, str)
39+
check(assert_type(left.add(r1), "pd.Series[str]"), pd.Series, str)
3940

40-
check(assert_type(left.radd(r0), "pd.Series[_str]"), pd.Series, _str)
41-
check(assert_type(left.radd(r1), "pd.Series[_str]"), pd.Series, _str)
41+
check(assert_type(left.radd(r0), "pd.Series[str]"), pd.Series, str)
42+
check(assert_type(left.radd(r1), "pd.Series[str]"), pd.Series, str)
4243

4344

4445
def test_add_numpy_array() -> None:
4546
"""Testpd.Series[str]+ numpy array"""
4647
r0 = np.array(["a", "bc", "def"], np.str_)
4748

48-
check(assert_type(left + r0, "pd.Series[_str]"), pd.Series, _str)
49+
check(assert_type(left + r0, "pd.Series[str]"), pd.Series, str)
4950

50-
# `numpy` typing gives `Any` in the static type
51+
# `numpy` typing gives `npt.NDArray[np.str_]` in the static type
5152
# checking, where our `__radd__` cannot override. At runtime, they return
5253
# `Series`s.
53-
check(assert_type(r0 + left, Any), pd.Series, _str)
54+
if sys.version_info >= (3, 11):
55+
check(
56+
assert_type(
57+
r0 + left, # pyright: ignore[reportAssertTypeFailure]
58+
"npt.NDArray[np.str_]",
59+
),
60+
pd.Series,
61+
str,
62+
)
63+
else:
64+
check(assert_type(r0 + left, Any), pd.Series, str)
5465

55-
check(assert_type(left.add(r0), "pd.Series[_str]"), pd.Series, _str)
66+
check(assert_type(left.add(r0), "pd.Series[str]"), pd.Series, str)
5667

57-
check(assert_type(left.radd(r0), "pd.Series[_str]"), pd.Series, _str)
68+
check(assert_type(left.radd(r0), "pd.Series[str]"), pd.Series, str)
5869

5970

6071
def test_add_pd_series() -> None:
6172
"""Testpd.Series[str]+ pandas series"""
6273
r0 = pd.Series(["a", "bc", "def"])
6374

64-
check(assert_type(left + r0, "pd.Series[_str]"), pd.Series, _str)
75+
check(assert_type(left + r0, "pd.Series[str]"), pd.Series, str)
6576

66-
check(assert_type(r0 + left, "pd.Series[_str]"), pd.Series, _str)
77+
check(assert_type(r0 + left, "pd.Series[str]"), pd.Series, str)
6778

68-
check(assert_type(left.add(r0), "pd.Series[_str]"), pd.Series, _str)
79+
check(assert_type(left.add(r0), "pd.Series[str]"), pd.Series, str)
6980

70-
check(assert_type(left.radd(r0), "pd.Series[_str]"), pd.Series, _str)
81+
check(assert_type(left.radd(r0), "pd.Series[str]"), pd.Series, str)

tests/series/arithmetic/timestamp/test_add.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
check,
1717
)
1818

19-
left = pd.Series([pd.Timestamp(2025, 8, 20)])
19+
left = pd.Series([pd.Timestamp(2025, 8, 20)]) # left operand
2020

2121

2222
def test_add_py_scalar() -> None:
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
from datetime import (
2+
datetime,
3+
timedelta,
4+
)
5+
from typing import TYPE_CHECKING
6+
7+
import numpy as np
8+
from numpy import typing as npt # noqa: F401
9+
import pandas as pd
10+
from typing_extensions import assert_type
11+
12+
from tests import (
13+
TYPE_CHECKING_INVALID_USAGE,
14+
check,
15+
)
16+
17+
if TYPE_CHECKING:
18+
from pandas.core.series import TimedeltaSeries # noqa: F401
19+
20+
left = pd.Series([pd.Timestamp(2025, 8, 20)]) # left operand
21+
22+
23+
def test_sub_py_scalar() -> None:
24+
"""Test pd.Series[pd.Timestamp] - Python native scalars"""
25+
s = datetime(2025, 8, 20)
26+
d = timedelta(seconds=1)
27+
28+
check(assert_type(left - s, "TimedeltaSeries"), pd.Series, pd.Timedelta)
29+
check(assert_type(left - d, "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp)
30+
31+
check(assert_type(s - left, "TimedeltaSeries"), pd.Series, pd.Timedelta)
32+
if TYPE_CHECKING_INVALID_USAGE:
33+
_ = d - left # type: ignore[operator] # pyright: ignore[reportOperatorIssue]
34+
35+
check(assert_type(left.sub(s), "TimedeltaSeries"), pd.Series, pd.Timedelta)
36+
check(assert_type(left - d, "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp)
37+
38+
check(assert_type(left.rsub(s), "TimedeltaSeries"), pd.Series, pd.Timedelta)
39+
if TYPE_CHECKING_INVALID_USAGE:
40+
left.rsub(d) # type: ignore[call-overload] # pyright: ignore[reportArgumentType,reportCallIssue]
41+
42+
43+
def test_sub_numpy_scalar() -> None:
44+
"""Test pd.Series[pd.Timestamp] - numpy scalars"""
45+
s = np.datetime64("2025-08-20")
46+
d = np.timedelta64(1, "s")
47+
48+
check(assert_type(left - s, "TimedeltaSeries"), pd.Series, pd.Timedelta)
49+
check(assert_type(left - d, "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp)
50+
51+
check(assert_type(s - left, "TimedeltaSeries"), pd.Series, pd.Timedelta)
52+
if TYPE_CHECKING_INVALID_USAGE:
53+
_ = d - left # type: ignore[operator] # pyright: ignore[reportOperatorIssue]
54+
55+
check(assert_type(left.sub(s), "TimedeltaSeries"), pd.Series, pd.Timedelta)
56+
check(assert_type(left - d, "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp)
57+
58+
check(assert_type(left.rsub(s), "TimedeltaSeries"), pd.Series, pd.Timedelta)
59+
if TYPE_CHECKING_INVALID_USAGE:
60+
left.rsub(d) # type: ignore[call-overload] # pyright: ignore[reportArgumentType,reportCallIssue]
61+
62+
63+
def test_sub_pd_scalar() -> None:
64+
"""Test pd.Series[pd.Timestamp] - pandas scalars"""
65+
s = pd.Timestamp("2025-08-20")
66+
d = pd.Timedelta(seconds=1)
67+
68+
check(assert_type(left - s, "TimedeltaSeries"), pd.Series, pd.Timedelta)
69+
check(assert_type(left - d, "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp)
70+
71+
check(assert_type(s - left, "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta)
72+
if TYPE_CHECKING_INVALID_USAGE:
73+
_ = d - left # type: ignore[operator] # pyright: ignore[reportOperatorIssue]
74+
75+
check(assert_type(left.sub(s), "TimedeltaSeries"), pd.Series, pd.Timedelta)
76+
check(assert_type(left - d, "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp)
77+
78+
check(assert_type(left.rsub(s), "TimedeltaSeries"), pd.Series, pd.Timedelta)
79+
if TYPE_CHECKING_INVALID_USAGE:
80+
left.rsub(d) # type: ignore[call-overload] # pyright: ignore[reportArgumentType,reportCallIssue]
81+
82+
83+
def test_sub_py_sequence() -> None:
84+
"""Test pd.Series[pd.Timestamp] - Python native sequence"""
85+
s = [datetime(2025, 8, 20)]
86+
d = [timedelta(seconds=1)]
87+
88+
if TYPE_CHECKING_INVALID_USAGE:
89+
_0 = left - s # type: ignore[operator] # pyright: ignore[reportOperatorIssue]
90+
_a = left - d # type: ignore[operator] # pyright: ignore[reportOperatorIssue]
91+
92+
_1 = s - left # type: ignore[operator] # pyright: ignore[reportOperatorIssue]
93+
_b = d - left # type: ignore[operator] # pyright: ignore[reportOperatorIssue]
94+
95+
left.sub(s) # type: ignore[call-overload] # pyright: ignore[reportArgumentType,reportCallIssue]
96+
left.sub(d) # type: ignore[call-overload] # pyright: ignore[reportArgumentType,reportCallIssue]
97+
98+
left.rsub(s) # type: ignore[call-overload] # pyright: ignore[reportArgumentType,reportCallIssue]
99+
left.rsub(d) # type: ignore[call-overload] # pyright: ignore[reportArgumentType,reportCallIssue]
100+
101+
102+
def test_sub_numpy_array() -> None:
103+
"""Test pd.Series[pd.Timestamp] - numpy array"""
104+
s = np.array([np.datetime64("2025-08-20")], np.datetime64)
105+
d = np.array([np.timedelta64(1, "s")], np.timedelta64)
106+
107+
check(assert_type(left - s, "TimedeltaSeries"), pd.Series, pd.Timedelta)
108+
check(assert_type(left - d, "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp)
109+
110+
# `numpy` typing gives the corresponding `ndarray`s in the static type
111+
# checking, where our `__radd__` cannot override. At runtime, they return
112+
# `Series`s.
113+
check(assert_type(s - left, "npt.NDArray[np.datetime64]"), pd.Series, pd.Timedelta)
114+
if TYPE_CHECKING_INVALID_USAGE:
115+
assert_type(d - left, "npt.NDArray[np.timedelta64]")
116+
117+
check(assert_type(left.sub(s), "TimedeltaSeries"), pd.Series, pd.Timedelta)
118+
check(assert_type(left - d, "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp)
119+
120+
check(assert_type(left.rsub(s), "TimedeltaSeries"), pd.Series, pd.Timedelta)
121+
if TYPE_CHECKING_INVALID_USAGE:
122+
left.rsub(d) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue]
123+
124+
125+
def test_sub_pd_series() -> None:
126+
"""Test pd.Series[pd.Timestamp] - pandas Series"""
127+
s = pd.Series([pd.Timestamp("2025-08-20")])
128+
d = pd.Series([pd.Timedelta(seconds=1)])
129+
130+
check(assert_type(left - s, "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta)
131+
check(assert_type(left - d, "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp)
132+
133+
check(assert_type(s - left, "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta)
134+
if TYPE_CHECKING_INVALID_USAGE:
135+
_ = d - left # type: ignore[operator] # pyright: ignore[reportOperatorIssue]
136+
137+
check(assert_type(left.sub(s), "TimedeltaSeries"), pd.Series, pd.Timedelta)
138+
check(assert_type(left - d, "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp)
139+
140+
check(assert_type(left.rsub(s), "TimedeltaSeries"), pd.Series, pd.Timedelta)
141+
if TYPE_CHECKING_INVALID_USAGE:
142+
left.rsub(d) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue]

0 commit comments

Comments
 (0)