Skip to content

Commit 8d8691e

Browse files
committed
feat(series): implement the proposal
1 parent 5c23f68 commit 8d8691e

File tree

7 files changed

+109
-38
lines changed

7 files changed

+109
-38
lines changed

pandas-stubs/core/series.pyi

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1631,7 +1631,9 @@ class Series(IndexOpsMixin[S1], NDFrame):
16311631
# just failed to generate these so I couldn't match
16321632
# them up.
16331633
@overload
1634-
def __add__(self: Series[Never], other: Scalar | _ListLike | Series) -> Series: ...
1634+
def __add__(self: Series[Never], other: _str) -> Never: ...
1635+
@overload
1636+
def __add__(self: Series[Never], other: complex | _ListLike | Series) -> Series: ...
16351637
@overload
16361638
def __add__(self, other: Series[Never]) -> Series: ...
16371639
@overload
@@ -1709,7 +1711,15 @@ class Series(IndexOpsMixin[S1], NDFrame):
17091711
@overload
17101712
def add(
17111713
self: Series[Never],
1712-
other: Scalar | _ListLike | Series,
1714+
other: _str,
1715+
level: Level | None = None,
1716+
fill_value: float | None = None,
1717+
axis: int = 0,
1718+
) -> Never: ...
1719+
@overload
1720+
def add(
1721+
self: Series[Never],
1722+
other: complex | _ListLike | Series,
17131723
level: Level | None = None,
17141724
fill_value: float | None = None,
17151725
axis: int = 0,
@@ -1852,7 +1862,11 @@ class Series(IndexOpsMixin[S1], NDFrame):
18521862
axis: int = 0,
18531863
) -> Series[_str]: ...
18541864
@overload # type: ignore[override]
1855-
def __radd__(self: Series[Never], other: Scalar | _ListLike) -> Series: ...
1865+
def __radd__(self: Series[Never], other: _str) -> Never: ...
1866+
@overload
1867+
def __radd__(
1868+
self: Series[Never], other: complex | _ListLike | Series
1869+
) -> Series: ...
18561870
@overload
18571871
def __radd__(
18581872
self: Series[bool],
@@ -1924,7 +1938,15 @@ class Series(IndexOpsMixin[S1], NDFrame):
19241938
@overload
19251939
def radd(
19261940
self: Series[Never],
1927-
other: Scalar | _ListLike | Series,
1941+
other: _str,
1942+
level: Level | None = None,
1943+
fill_value: float | None = None,
1944+
axis: int = 0,
1945+
) -> Never: ...
1946+
@overload
1947+
def radd(
1948+
self: Series[Never],
1949+
other: complex | _ListLike | Series,
19281950
level: Level | None = None,
19291951
fill_value: float | None = None,
19301952
axis: int = 0,

tests/series/arithmetic/str/test_add.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44
import numpy as np
55
from numpy import typing as npt # noqa: F401
66
import pandas as pd
7+
import pytest
78
from typing_extensions import (
8-
Never,
9+
assert_never,
910
assert_type,
1011
)
1112

@@ -72,7 +73,8 @@ def test_add_numpy_array() -> None:
7273
r0 = np.array(["a", "bc", "def"], np.str_)
7374

7475
if TYPE_CHECKING_INVALID_USAGE:
75-
assert_type(left + i, Never)
76+
with pytest.raises(AssertionError):
77+
assert_never(left + i)
7678
check(assert_type(left + r0, "pd.Series[str]"), pd.Series, str)
7779

7880
# `numpy` typing gives `npt.NDArray[np.int64]` in the static type

tests/series/arithmetic/test_add.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
import numpy as np
22
from numpy import typing as npt # noqa: F401
33
import pandas as pd
4-
from typing_extensions import assert_type
4+
import pytest
5+
from typing_extensions import (
6+
assert_never,
7+
assert_type,
8+
)
59

6-
from tests import check
10+
from tests import (
11+
TYPE_CHECKING_INVALID_USAGE,
12+
check,
13+
)
714

815
# left operands
916
left_i = pd.DataFrame({"a": [1, 2, 3]})["a"]
@@ -131,3 +138,18 @@ def test_add_i_pd_series() -> None:
131138
check(assert_type(left_i.radd(i), pd.Series), pd.Series)
132139
check(assert_type(left_i.radd(f), pd.Series), pd.Series)
133140
check(assert_type(left_i.radd(c), pd.Series), pd.Series)
141+
142+
143+
def test_add_str_py_str() -> None:
144+
"""Test pd.Series[Any] (int) + Python str"""
145+
s = "abc"
146+
147+
if TYPE_CHECKING_INVALID_USAGE:
148+
with pytest.raises(AssertionError):
149+
assert_never(left_i + s)
150+
with pytest.raises(AssertionError):
151+
assert_never(s + left_i)
152+
with pytest.raises(AssertionError):
153+
assert_never(left_i.add(s))
154+
with pytest.raises(AssertionError):
155+
assert_never(left_i.radd(s))

tests/series/arithmetic/test_mul.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33
import pandas as pd
44
from typing_extensions import assert_type
55

6-
from tests import check
6+
from tests import (
7+
TYPE_CHECKING_INVALID_USAGE,
8+
check,
9+
)
710

811
left_i = pd.DataFrame({"a": [1, 2, 3]})["a"] # left operand
912

@@ -129,3 +132,14 @@ def test_mul_pd_series() -> None:
129132
check(assert_type(left_i.rmul(i), pd.Series), pd.Series)
130133
check(assert_type(left_i.rmul(f), pd.Series), pd.Series)
131134
check(assert_type(left_i.rmul(c), pd.Series), pd.Series)
135+
136+
137+
def test_mul_str_py_str() -> None:
138+
"""Test pd.Series[Any] (int) * Python str"""
139+
s = "abc"
140+
141+
if TYPE_CHECKING_INVALID_USAGE:
142+
left_i * s # type: ignore[operator] # pyright:ignore[reportOperatorIssue]
143+
s * left_i # type: ignore[operator] # pyright:ignore[reportOperatorIssue]
144+
left_i.mul(s) # type: ignore[type-var] # pyright: ignore[reportArgumentType,reportCallIssue]
145+
left_i.rmul(s) # type: ignore[type-var] # pyright: ignore[reportArgumentType,reportCallIssue]

tests/series/arithmetic/test_sub.py

Lines changed: 22 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@
77
import numpy as np
88
from numpy import typing as npt # noqa: F401
99
import pandas as pd
10+
import pytest
1011
from typing_extensions import (
11-
Never,
12+
assert_never,
1213
assert_type,
14+
Never
1315
)
1416

1517
from tests import (
@@ -179,8 +181,6 @@ def test_sub_ts_numpy_datetime() -> None:
179181
a = np.array([s + np.timedelta64(m, "m") for m in range(3)], np.datetime64)
180182

181183
if TYPE_CHECKING_INVALID_USAGE:
182-
# `numpy` typing gives the corresponding `ndarray`s in the static type
183-
# checking, where our `__rsub__` cannot override.
184184
_0 = left_ts - s # type: ignore[operator] # pyright: ignore[reportOperatorIssue]
185185
# _1 = left_ts - a
186186
_2 = left_td - s # type: ignore[operator] # pyright: ignore[reportOperatorIssue]
@@ -210,45 +210,39 @@ def test_sub_ts_pd_datetime() -> None:
210210
if TYPE_CHECKING_INVALID_USAGE:
211211
_0 = left_ts - s # type: ignore[operator] # pyright: ignore[reportOperatorIssue]
212212
assert_type(left_ts - a, Never)
213+
213214
_2 = left_td - s # type: ignore[operator] # pyright: ignore[reportOperatorIssue]
214215
assert_type(left_td - a, Never)
215216

216217
_4 = s - left_ts # type: ignore[operator] # pyright: ignore[reportOperatorIssue]
217218
assert_type(a - left_ts, Never)
219+
218220
_6 = s - left_td # type: ignore[operator] # pyright: ignore[reportOperatorIssue]
219221
assert_type(a - left_td, Never)
220222

221223
left_ts.sub(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue]
222-
assert_type(left_ts.sub(a), Never)
224+
with pytest.raises(AssertionError):
225+
assert_never(left_ts.sub(a))
223226

227+
left_td.sub(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue]
228+
with pytest.raises(AssertionError):
229+
assert_never(left_td.sub(a))
224230

225-
def test_sub_ts_pd_datetime_1() -> None:
226-
"""Test pd.Series[Any] (Timestamp | Timedelta) - Pandas datetime(s)"""
227-
s = pd.Timestamp(anchor)
228-
a = pd.Series([s + pd.Timedelta(minutes=m) for m in range(3)])
229-
230-
if TYPE_CHECKING_INVALID_USAGE:
231-
# When I merge this one to the previous one, mypy does not allow me to pass
232231
left_ts.rsub(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue]
233-
assert_type(left_ts.rsub(a), Never)
232+
with pytest.raises(AssertionError):
233+
assert_never(left_ts.rsub(a))
234234

235-
236-
def test_sub_ts_pd_datetime_2() -> None:
237-
"""Test pd.Series[Any] (Timestamp | Timedelta) - Pandas datetime(s)"""
238-
s = pd.Timestamp(anchor)
239-
a = pd.Series([s + pd.Timedelta(minutes=m) for m in range(3)])
240-
241-
if TYPE_CHECKING_INVALID_USAGE:
242-
# When I merge this one to the previous one, mypy does not allow me to pass
243235
left_td.rsub(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue]
244-
assert_type(left_td.rsub(a), Never)
236+
with pytest.raises(AssertionError):
237+
assert_never(left_td.rsub(a))
238+
245239

240+
def test_sub_str_py_str() -> None:
241+
"""Test pd.Series[Any] (int) - Python str"""
242+
s = "abc"
246243

247-
def test_str_sub() -> None:
248244
if TYPE_CHECKING_INVALID_USAGE:
249-
left_i - "abc" # type: ignore[operator] # pyright:ignore[reportOperatorIssue]
250-
"abc" - left_i # type: ignore[operator] # pyright:ignore[reportOperatorIssue]
251-
left_i * "abc" # type: ignore[operator] # pyright:ignore[reportOperatorIssue]
252-
"abc" * left_i # type: ignore[operator] # pyright:ignore[reportOperatorIssue]
253-
left_i / "abc" # type: ignore[operator] # pyright:ignore[reportOperatorIssue]
254-
"abc" / left_i # type: ignore[operator] # pyright:ignore[reportOperatorIssue]
245+
_0 = left_i - s # type: ignore[operator] # pyright:ignore[reportOperatorIssue]
246+
_1 = s - left_i # type: ignore[operator] # pyright:ignore[reportOperatorIssue]
247+
left_i.sub(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue]
248+
left_i.rsub(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue]

tests/series/arithmetic/test_truediv.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
from tests import (
99
PD_LTE_23,
10+
TYPE_CHECKING_INVALID_USAGE,
1011
check,
1112
)
1213

@@ -211,3 +212,18 @@ def test_truediv_path(tmp_path: Path) -> None:
211212

212213
check(assert_type(fnames.rtruediv(tmp_path), pd.Series), pd.Series, Path)
213214
check(assert_type(fnames.rdiv(tmp_path), pd.Series), pd.Series, Path)
215+
216+
217+
def test_truediv_str_py_str() -> None:
218+
"""Test pd.Series[Any] (int) / Python str"""
219+
s = "abc"
220+
221+
if TYPE_CHECKING_INVALID_USAGE:
222+
_0 = left_i / s # type: ignore[operator] # pyright:ignore[reportOperatorIssue]
223+
_1 = s / left_i # type: ignore[operator] # pyright:ignore[reportOperatorIssue]
224+
225+
left_i.truediv(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue]
226+
left_i.div(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue]
227+
228+
left_i.rtruediv(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue]
229+
left_i.rdiv(s) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue]

tests/test_frame.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474

7575
if TYPE_CHECKING:
7676
from pandas.core.frame import _PandasNamedTuple
77+
from pandas.core.series import TimestampSeries
7778
else:
7879
_PandasNamedTuple: TypeAlias = tuple
7980

@@ -4440,7 +4441,7 @@ def test_frame_setitem_na() -> None:
44404441
df.loc[ind, :] = pd.NA
44414442
df.iloc[[0, 2], :] = pd.NA
44424443

4443-
df["x"] = df["y"] + pd.Timedelta(days=3)
4444+
df["x"] = cast("TimestampSeries", df["y"]) + pd.Timedelta(days=3)
44444445
df.loc[ind, :] = pd.NaT
44454446
df.iloc[[0, 2], :] = pd.NaT
44464447

0 commit comments

Comments
 (0)