1+ from datetime import (
2+ datetime ,
3+ timedelta ,
4+ )
5+ from typing import Any
6+
17import numpy as np
28from numpy import typing as npt # noqa: F401
39import pandas as pd
4- from typing_extensions import assert_type
10+ import pytest
11+ from typing_extensions import (
12+ Never ,
13+ assert_type ,
14+ )
15+
16+ from tests import (
17+ TYPE_CHECKING_INVALID_USAGE ,
18+ check ,
19+ )
520
6- from tests import check
721
8- left = pd .Series ([True , True , False ]) # left operand
22+ @pytest .fixture
23+ def left () -> "pd.Series[bool]" :
24+ """left operand"""
25+ lo = pd .Series ([True , True , False ])
26+ return check (assert_type (lo , "pd.Series[bool]" ), pd .Series , np .bool_ )
927
1028
11- def test_mul_py_scalar () -> None :
29+ def test_mul_py_scalar (left : "pd.Series[bool]" ) -> None :
1230 """Test pd.Series[bool] * Python native scalars"""
1331 b , i , f , c = True , 1 , 1.0 , 1j
32+ s , d = datetime (2025 , 10 , 1 ), timedelta (seconds = 1 )
1433
1534 check (assert_type (left * b , "pd.Series[bool]" ), pd .Series , np .bool_ )
1635 check (assert_type (left * i , "pd.Series[int]" ), pd .Series , np .integer )
1736 check (assert_type (left * f , "pd.Series[float]" ), pd .Series , np .floating )
1837 check (assert_type (left * c , "pd.Series[complex]" ), pd .Series , np .complexfloating )
38+ if TYPE_CHECKING_INVALID_USAGE :
39+ _05 = left * s # type: ignore[operator] # pyright: ignore[reportOperatorIssue]
40+ check (assert_type (left * d , "pd.Series[pd.Timedelta]" ), pd .Series , pd .Timedelta )
1941
2042 check (assert_type (b * left , "pd.Series[bool]" ), pd .Series , np .bool_ )
2143 check (assert_type (i * left , "pd.Series[int]" ), pd .Series , np .integer )
2244 check (assert_type (f * left , "pd.Series[float]" ), pd .Series , np .floating )
2345 check (assert_type (c * left , "pd.Series[complex]" ), pd .Series , np .complexfloating )
46+ if TYPE_CHECKING_INVALID_USAGE :
47+ _15 = s * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue]
48+ check (assert_type (d * left , "pd.Series[pd.Timedelta]" ), pd .Series , pd .Timedelta )
2449
2550 check (assert_type (left .mul (b ), "pd.Series[bool]" ), pd .Series , np .bool_ )
2651 check (assert_type (left .mul (i ), "pd.Series[int]" ), pd .Series , np .integer )
2752 check (assert_type (left .mul (f ), "pd.Series[float]" ), pd .Series , np .floating )
2853 check (assert_type (left .mul (c ), "pd.Series[complex]" ), pd .Series , np .complexfloating )
54+ if TYPE_CHECKING_INVALID_USAGE :
55+ left .mul (s ) # type: ignore[call-overload] # pyright: ignore[reportArgumentType,reportCallIssue]
56+ check (assert_type (left .mul (d ), "pd.Series[pd.Timedelta]" ), pd .Series , pd .Timedelta )
2957
3058 check (assert_type (left .rmul (b ), "pd.Series[bool]" ), pd .Series , np .bool_ )
3159 check (assert_type (left .rmul (i ), "pd.Series[int]" ), pd .Series , np .integer )
3260 check (assert_type (left .rmul (f ), "pd.Series[float]" ), pd .Series , np .floating )
3361 check (
3462 assert_type (left .rmul (c ), "pd.Series[complex]" ), pd .Series , np .complexfloating
3563 )
64+ if TYPE_CHECKING_INVALID_USAGE :
65+ left .rmul (s ) # type: ignore[call-overload] # pyright: ignore[reportArgumentType,reportCallIssue]
66+ check (assert_type (left .rmul (d ), "pd.Series[pd.Timedelta]" ), pd .Series , pd .Timedelta )
3667
3768
38- def test_mul_py_sequence () -> None :
69+ def test_mul_py_sequence (left : "pd.Series[bool]" ) -> None :
3970 """Test pd.Series[bool] * Python native sequences"""
4071 b , i , f , c = [True , False , True ], [2 , 3 , 5 ], [1.0 , 2.0 , 3.0 ], [1j , 1j , 4j ]
72+ s = [datetime (2025 , 10 , d ) for d in (1 , 2 , 3 )]
73+ d = [timedelta (seconds = s + 1 ) for s in range (3 )]
4174
4275 check (assert_type (left * b , "pd.Series[bool]" ), pd .Series , np .bool_ )
4376 check (assert_type (left * i , "pd.Series[int]" ), pd .Series , np .integer )
4477 check (assert_type (left * f , "pd.Series[float]" ), pd .Series , np .floating )
4578 check (assert_type (left * c , "pd.Series[complex]" ), pd .Series , np .complexfloating )
79+ if TYPE_CHECKING_INVALID_USAGE :
80+ _05 = left * s # type: ignore[operator] # pyright: ignore[reportOperatorIssue]
81+ check (assert_type (left * d , "pd.Series[pd.Timedelta]" ), pd .Series , timedelta )
4682
4783 check (assert_type (b * left , "pd.Series[bool]" ), pd .Series , np .bool_ )
4884 check (assert_type (i * left , "pd.Series[int]" ), pd .Series , np .integer )
4985 check (assert_type (f * left , "pd.Series[float]" ), pd .Series , np .floating )
5086 check (assert_type (c * left , "pd.Series[complex]" ), pd .Series , np .complexfloating )
87+ if TYPE_CHECKING_INVALID_USAGE :
88+ _15 = s * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue]
89+ check (assert_type (d * left , "pd.Series[pd.Timedelta]" ), pd .Series , timedelta )
5190
5291 check (assert_type (left .mul (b ), "pd.Series[bool]" ), pd .Series , np .bool_ )
5392 check (assert_type (left .mul (i ), "pd.Series[int]" ), pd .Series , np .integer )
5493 check (assert_type (left .mul (f ), "pd.Series[float]" ), pd .Series , np .floating )
5594 check (assert_type (left .mul (c ), "pd.Series[complex]" ), pd .Series , np .complexfloating )
95+ if TYPE_CHECKING_INVALID_USAGE :
96+ left .mul (s ) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue]
97+ check (assert_type (left .mul (d ), "pd.Series[pd.Timedelta]" ), pd .Series , timedelta )
5698
5799 check (assert_type (left .rmul (b ), "pd.Series[bool]" ), pd .Series , np .bool_ )
58100 check (assert_type (left .rmul (i ), "pd.Series[int]" ), pd .Series , np .integer )
59101 check (assert_type (left .rmul (f ), "pd.Series[float]" ), pd .Series , np .floating )
60102 check (
61103 assert_type (left .rmul (c ), "pd.Series[complex]" ), pd .Series , np .complexfloating
62104 )
105+ if TYPE_CHECKING_INVALID_USAGE :
106+ left .rmul (s ) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue]
107+ check (assert_type (left .rmul (d ), "pd.Series[pd.Timedelta]" ), pd .Series , timedelta )
63108
64109
65- def test_mul_numpy_array () -> None :
110+ def test_mul_numpy_array (left : "pd.Series[bool]" ) -> None :
66111 """Test pd.Series[bool] * numpy arrays"""
67112 b = np .array ([True , False , True ], np .bool_ )
68113 i = np .array ([2 , 3 , 5 ], np .int64 )
69114 f = np .array ([1.0 , 2.0 , 3.0 ], np .float64 )
70115 c = np .array ([1.1j , 2.2j , 4.1j ], np .complex128 )
116+ s = np .array ([np .datetime64 (f"2025-10-{ d :02d} " ) for d in (1 , 2 , 3 )], np .datetime64 )
117+ d = np .array ([np .timedelta64 (s + 1 , "s" ) for s in range (3 )], np .timedelta64 )
71118
72119 check (assert_type (left * b , "pd.Series[bool]" ), pd .Series , np .bool_ )
73120 check (assert_type (left * i , "pd.Series[int]" ), pd .Series , np .integer )
74121 check (assert_type (left * f , "pd.Series[float]" ), pd .Series , np .floating )
75122 check (assert_type (left * c , "pd.Series[complex]" ), pd .Series , np .complexfloating )
123+ if TYPE_CHECKING_INVALID_USAGE :
124+ assert_type (left * s , Never )
125+ check (assert_type (left * d , "pd.Series[pd.Timedelta]" ), pd .Series , pd .Timedelta )
76126
77127 # `numpy` typing gives the corresponding `ndarray`s in the static type
78128 # checking, where our `__rmul__` cannot override. At runtime, they return
@@ -85,75 +135,112 @@ def test_mul_numpy_array() -> None:
85135 pd .Series ,
86136 np .complexfloating ,
87137 )
138+ if TYPE_CHECKING_INVALID_USAGE :
139+ assert_type (s * left , Any )
140+ check (assert_type (d * left , "npt.NDArray[np.timedelta64]" ), pd .Series , pd .Timedelta )
88141
89142 check (assert_type (left .mul (b ), "pd.Series[bool]" ), pd .Series , np .bool_ )
90143 check (assert_type (left .mul (i ), "pd.Series[int]" ), pd .Series , np .integer )
91144 check (assert_type (left .mul (f ), "pd.Series[float]" ), pd .Series , np .floating )
92145 check (assert_type (left .mul (c ), "pd.Series[complex]" ), pd .Series , np .complexfloating )
146+ if TYPE_CHECKING_INVALID_USAGE :
147+ left .mul (s ) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue]
148+ check (assert_type (left .mul (d ), "pd.Series[pd.Timedelta]" ), pd .Series , pd .Timedelta )
93149
94150 check (assert_type (left .rmul (b ), "pd.Series[bool]" ), pd .Series , np .bool_ )
95151 check (assert_type (left .rmul (i ), "pd.Series[int]" ), pd .Series , np .integer )
96152 check (assert_type (left .rmul (f ), "pd.Series[float]" ), pd .Series , np .floating )
97153 check (
98154 assert_type (left .rmul (c ), "pd.Series[complex]" ), pd .Series , np .complexfloating
99155 )
156+ if TYPE_CHECKING_INVALID_USAGE :
157+ left .rmul (s ) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue]
158+ check (assert_type (left .rmul (d ), "pd.Series[pd.Timedelta]" ), pd .Series , pd .Timedelta )
100159
101160
102- def test_mul_pd_index () -> None :
161+ def test_mul_pd_index (left : "pd.Series[bool]" ) -> None :
103162 """Test pd.Series[bool] * pandas Indexes"""
104163 b = pd .Index ([True , False , True ])
105164 i = pd .Index ([2 , 3 , 5 ])
106165 f = pd .Index ([1.0 , 2.0 , 3.0 ])
107166 c = pd .Index ([1.1j , 2.2j , 4.1j ])
167+ s = pd .Index ([datetime (2025 , 10 , d ) for d in (1 , 2 , 3 )])
168+ d = pd .Index ([timedelta (seconds = s + 1 ) for s in range (3 )])
108169
109170 check (assert_type (left * b , "pd.Series[bool]" ), pd .Series , np .bool_ )
110171 check (assert_type (left * i , "pd.Series[int]" ), pd .Series , np .integer )
111172 check (assert_type (left * f , "pd.Series[float]" ), pd .Series , np .floating )
112173 check (assert_type (left * c , "pd.Series[complex]" ), pd .Series , np .complexfloating )
174+ if TYPE_CHECKING_INVALID_USAGE :
175+ _05 = left * s # type: ignore[operator] # pyright: ignore[reportOperatorIssue]
176+ check (assert_type (left * d , "pd.Series[pd.Timedelta]" ), pd .Series , timedelta )
113177
114178 check (assert_type (b * left , "pd.Series[bool]" ), pd .Series , np .bool_ )
115179 check (assert_type (i * left , "pd.Series[int]" ), pd .Series , np .integer )
116180 check (assert_type (f * left , "pd.Series[float]" ), pd .Series , np .floating )
117181 check (assert_type (c * left , "pd.Series[complex]" ), pd .Series , np .complexfloating )
182+ if TYPE_CHECKING_INVALID_USAGE :
183+ _15 = s * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue]
184+ check (assert_type (d * left , "pd.Series[pd.Timedelta]" ), pd .Series , timedelta )
118185
119186 check (assert_type (left .mul (b ), "pd.Series[bool]" ), pd .Series , np .bool_ )
120187 check (assert_type (left .mul (i ), "pd.Series[int]" ), pd .Series , np .integer )
121188 check (assert_type (left .mul (f ), "pd.Series[float]" ), pd .Series , np .floating )
122189 check (assert_type (left .mul (c ), "pd.Series[complex]" ), pd .Series , np .complexfloating )
190+ if TYPE_CHECKING_INVALID_USAGE :
191+ left .mul (s ) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue]
192+ check (assert_type (left .mul (d ), "pd.Series[pd.Timedelta]" ), pd .Series , timedelta )
123193
124194 check (assert_type (left .rmul (b ), "pd.Series[bool]" ), pd .Series , np .bool_ )
125195 check (assert_type (left .rmul (i ), "pd.Series[int]" ), pd .Series , np .integer )
126196 check (assert_type (left .rmul (f ), "pd.Series[float]" ), pd .Series , np .floating )
127197 check (
128198 assert_type (left .rmul (c ), "pd.Series[complex]" ), pd .Series , np .complexfloating
129199 )
200+ if TYPE_CHECKING_INVALID_USAGE :
201+ left .rmul (s ) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue]
202+ check (assert_type (left .rmul (d ), "pd.Series[pd.Timedelta]" ), pd .Series , timedelta )
130203
131204
132- def test_mul_pd_series () -> None :
205+ def test_mul_pd_series (left : "pd.Series[bool]" ) -> None :
133206 """Test pd.Series[bool] * pandas Series"""
134207 b = pd .Series ([True , False , True ])
135208 i = pd .Series ([2 , 3 , 5 ])
136209 f = pd .Series ([1.0 , 2.0 , 3.0 ])
137210 c = pd .Series ([1.1j , 2.2j , 4.1j ])
211+ s = pd .Series ([datetime (2025 , 10 , d ) for d in (1 , 2 , 3 )])
212+ d = pd .Series ([timedelta (seconds = s + 1 ) for s in range (3 )])
138213
139214 check (assert_type (left * b , "pd.Series[bool]" ), pd .Series , np .bool_ )
140215 check (assert_type (left * i , "pd.Series[int]" ), pd .Series , np .integer )
141216 check (assert_type (left * f , "pd.Series[float]" ), pd .Series , np .floating )
142217 check (assert_type (left * c , "pd.Series[complex]" ), pd .Series , np .complexfloating )
218+ if TYPE_CHECKING_INVALID_USAGE :
219+ _05 = left * s # type: ignore[operator] # pyright: ignore[reportOperatorIssue]
220+ check (assert_type (left * d , "pd.Series[pd.Timedelta]" ), pd .Series , timedelta )
143221
144222 check (assert_type (b * left , "pd.Series[bool]" ), pd .Series , np .bool_ )
145223 check (assert_type (i * left , "pd.Series[int]" ), pd .Series , np .integer )
146224 check (assert_type (f * left , "pd.Series[float]" ), pd .Series , np .floating )
147225 check (assert_type (c * left , "pd.Series[complex]" ), pd .Series , np .complexfloating )
226+ if TYPE_CHECKING_INVALID_USAGE :
227+ _15 = s * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue]
228+ check (assert_type (d * left , "pd.Series[pd.Timedelta]" ), pd .Series , timedelta )
148229
149230 check (assert_type (left .mul (b ), "pd.Series[bool]" ), pd .Series , np .bool_ )
150231 check (assert_type (left .mul (i ), "pd.Series[int]" ), pd .Series , np .integer )
151232 check (assert_type (left .mul (f ), "pd.Series[float]" ), pd .Series , np .floating )
152233 check (assert_type (left .mul (c ), "pd.Series[complex]" ), pd .Series , np .complexfloating )
234+ if TYPE_CHECKING_INVALID_USAGE :
235+ left .mul (s ) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue]
236+ check (assert_type (left .mul (d ), "pd.Series[pd.Timedelta]" ), pd .Series , timedelta )
153237
154238 check (assert_type (left .rmul (b ), "pd.Series[bool]" ), pd .Series , np .bool_ )
155239 check (assert_type (left .rmul (i ), "pd.Series[int]" ), pd .Series , np .integer )
156240 check (assert_type (left .rmul (f ), "pd.Series[float]" ), pd .Series , np .floating )
157241 check (
158242 assert_type (left .rmul (c ), "pd.Series[complex]" ), pd .Series , np .complexfloating
159243 )
244+ if TYPE_CHECKING_INVALID_USAGE :
245+ left .rmul (s ) # type: ignore[arg-type] # pyright: ignore[reportArgumentType,reportCallIssue]
246+ check (assert_type (left .rmul (d ), "pd.Series[pd.Timedelta]" ), pd .Series , timedelta )
0 commit comments