Skip to content

Commit 48a72c3

Browse files
committed
feat: add
1 parent 48df895 commit 48a72c3

File tree

4 files changed

+186
-4
lines changed

4 files changed

+186
-4
lines changed

pandas-stubs/core/series.pyi

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1922,7 +1922,12 @@ class Series(IndexOpsMixin[S1], NDFrame):
19221922
def add(
19231923
self: Series[Timedelta],
19241924
other: (
1925-
datetime | np.datetime64 | np_ndarray_dt | DatetimeIndex | Series[Timestamp]
1925+
datetime
1926+
| Sequence[datetime]
1927+
| np.datetime64
1928+
| np_ndarray_dt
1929+
| DatetimeIndex
1930+
| Series[Timestamp]
19261931
),
19271932
level: Level | None = None,
19281933
fill_value: float | None = None,
@@ -1933,6 +1938,7 @@ class Series(IndexOpsMixin[S1], NDFrame):
19331938
self: Series[Timedelta],
19341939
other: (
19351940
timedelta
1941+
| Sequence[timedelta]
19361942
| np.timedelta64
19371943
| np_ndarray_td
19381944
| TimedeltaIndex
@@ -2222,7 +2228,12 @@ class Series(IndexOpsMixin[S1], NDFrame):
22222228
def radd(
22232229
self: Series[Timedelta],
22242230
other: (
2225-
datetime | np.datetime64 | np_ndarray_dt | DatetimeIndex | Series[Timestamp]
2231+
datetime
2232+
| Sequence[datetime]
2233+
| np.datetime64
2234+
| np_ndarray_dt
2235+
| DatetimeIndex
2236+
| Series[Timestamp]
22262237
),
22272238
level: Level | None = None,
22282239
fill_value: float | None = None,
@@ -2233,6 +2244,7 @@ class Series(IndexOpsMixin[S1], NDFrame):
22332244
self: Series[Timedelta],
22342245
other: (
22352246
timedelta
2247+
| Sequence[timedelta]
22362248
| np.timedelta64
22372249
| np_ndarray_td
22382250
| TimedeltaIndex

tests/series/arithmetic/timedelta/__init__.py

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

tests/series/arithmetic/timestamp/test_add.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,11 @@ def test_add_py_sequence() -> None:
100100

101101
if TYPE_CHECKING_INVALID_USAGE:
102102
left.add(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue]
103-
left.add(d)
103+
check(assert_type(left.add(d), "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp)
104104

105105
if TYPE_CHECKING_INVALID_USAGE:
106106
left.radd(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue]
107-
left.radd(d)
107+
check(assert_type(left.radd(d), "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp)
108108

109109

110110
def test_add_numpy_array() -> None:
@@ -134,6 +134,28 @@ def test_add_numpy_array() -> None:
134134
check(assert_type(left.radd(d), "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp)
135135

136136

137+
def test_add_pd_index() -> None:
138+
"""Test pd.Series[pd.Timestamp] + pandas Index"""
139+
s = pd.Index([pd.Timestamp("2025-08-20")])
140+
d = pd.Index([pd.Timedelta(seconds=1)])
141+
142+
if TYPE_CHECKING_INVALID_USAGE:
143+
_0 = left + s # type: ignore[operator] # pyright: ignore[reportOperatorIssue]
144+
check(assert_type(left + d, "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp)
145+
146+
if TYPE_CHECKING_INVALID_USAGE:
147+
_1 = s + left # type: ignore[operator] # pyright: ignore[reportOperatorIssue]
148+
check(assert_type(d + left, "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp)
149+
150+
if TYPE_CHECKING_INVALID_USAGE:
151+
left.add(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue]
152+
check(assert_type(left.add(d), "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp)
153+
154+
if TYPE_CHECKING_INVALID_USAGE:
155+
left.radd(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue]
156+
check(assert_type(left.radd(d), "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp)
157+
158+
137159
def test_add_pd_series() -> None:
138160
"""Test pd.Series[pd.Timestamp] + pandas Series"""
139161
s = pd.Series([pd.Timestamp("2025-08-20")])

0 commit comments

Comments
 (0)