1
+ from datetime import (
2
+ datetime ,
3
+ timedelta ,
4
+ )
5
+ from typing import Any
6
+
1
7
import numpy as np
2
8
from numpy import typing as npt # noqa: F401
3
9
import 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
+ )
5
20
6
- from tests import check
7
21
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_ )
9
27
10
28
11
- def test_mul_py_scalar () -> None :
29
+ def test_mul_py_scalar (left : "pd.Series[bool]" ) -> None :
12
30
"""Test pd.Series[bool] * Python native scalars"""
13
31
b , i , f , c = True , 1 , 1.0 , 1j
32
+ s , d = datetime (2025 , 10 , 1 ), timedelta (seconds = 1 )
14
33
15
34
check (assert_type (left * b , "pd.Series[bool]" ), pd .Series , np .bool_ )
16
35
check (assert_type (left * i , "pd.Series[int]" ), pd .Series , np .integer )
17
36
check (assert_type (left * f , "pd.Series[float]" ), pd .Series , np .floating )
18
37
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 )
19
41
20
42
check (assert_type (b * left , "pd.Series[bool]" ), pd .Series , np .bool_ )
21
43
check (assert_type (i * left , "pd.Series[int]" ), pd .Series , np .integer )
22
44
check (assert_type (f * left , "pd.Series[float]" ), pd .Series , np .floating )
23
45
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 )
24
49
25
50
check (assert_type (left .mul (b ), "pd.Series[bool]" ), pd .Series , np .bool_ )
26
51
check (assert_type (left .mul (i ), "pd.Series[int]" ), pd .Series , np .integer )
27
52
check (assert_type (left .mul (f ), "pd.Series[float]" ), pd .Series , np .floating )
28
53
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 )
29
57
30
58
check (assert_type (left .rmul (b ), "pd.Series[bool]" ), pd .Series , np .bool_ )
31
59
check (assert_type (left .rmul (i ), "pd.Series[int]" ), pd .Series , np .integer )
32
60
check (assert_type (left .rmul (f ), "pd.Series[float]" ), pd .Series , np .floating )
33
61
check (
34
62
assert_type (left .rmul (c ), "pd.Series[complex]" ), pd .Series , np .complexfloating
35
63
)
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 )
36
67
37
68
38
- def test_mul_py_sequence () -> None :
69
+ def test_mul_py_sequence (left : "pd.Series[bool]" ) -> None :
39
70
"""Test pd.Series[bool] * Python native sequences"""
40
71
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 )]
41
74
42
75
check (assert_type (left * b , "pd.Series[bool]" ), pd .Series , np .bool_ )
43
76
check (assert_type (left * i , "pd.Series[int]" ), pd .Series , np .integer )
44
77
check (assert_type (left * f , "pd.Series[float]" ), pd .Series , np .floating )
45
78
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 )
46
82
47
83
check (assert_type (b * left , "pd.Series[bool]" ), pd .Series , np .bool_ )
48
84
check (assert_type (i * left , "pd.Series[int]" ), pd .Series , np .integer )
49
85
check (assert_type (f * left , "pd.Series[float]" ), pd .Series , np .floating )
50
86
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 )
51
90
52
91
check (assert_type (left .mul (b ), "pd.Series[bool]" ), pd .Series , np .bool_ )
53
92
check (assert_type (left .mul (i ), "pd.Series[int]" ), pd .Series , np .integer )
54
93
check (assert_type (left .mul (f ), "pd.Series[float]" ), pd .Series , np .floating )
55
94
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 )
56
98
57
99
check (assert_type (left .rmul (b ), "pd.Series[bool]" ), pd .Series , np .bool_ )
58
100
check (assert_type (left .rmul (i ), "pd.Series[int]" ), pd .Series , np .integer )
59
101
check (assert_type (left .rmul (f ), "pd.Series[float]" ), pd .Series , np .floating )
60
102
check (
61
103
assert_type (left .rmul (c ), "pd.Series[complex]" ), pd .Series , np .complexfloating
62
104
)
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 )
63
108
64
109
65
- def test_mul_numpy_array () -> None :
110
+ def test_mul_numpy_array (left : "pd.Series[bool]" ) -> None :
66
111
"""Test pd.Series[bool] * numpy arrays"""
67
112
b = np .array ([True , False , True ], np .bool_ )
68
113
i = np .array ([2 , 3 , 5 ], np .int64 )
69
114
f = np .array ([1.0 , 2.0 , 3.0 ], np .float64 )
70
115
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 )
71
118
72
119
check (assert_type (left * b , "pd.Series[bool]" ), pd .Series , np .bool_ )
73
120
check (assert_type (left * i , "pd.Series[int]" ), pd .Series , np .integer )
74
121
check (assert_type (left * f , "pd.Series[float]" ), pd .Series , np .floating )
75
122
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 )
76
126
77
127
# `numpy` typing gives the corresponding `ndarray`s in the static type
78
128
# checking, where our `__rmul__` cannot override. At runtime, they return
@@ -85,75 +135,112 @@ def test_mul_numpy_array() -> None:
85
135
pd .Series ,
86
136
np .complexfloating ,
87
137
)
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 )
88
141
89
142
check (assert_type (left .mul (b ), "pd.Series[bool]" ), pd .Series , np .bool_ )
90
143
check (assert_type (left .mul (i ), "pd.Series[int]" ), pd .Series , np .integer )
91
144
check (assert_type (left .mul (f ), "pd.Series[float]" ), pd .Series , np .floating )
92
145
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 )
93
149
94
150
check (assert_type (left .rmul (b ), "pd.Series[bool]" ), pd .Series , np .bool_ )
95
151
check (assert_type (left .rmul (i ), "pd.Series[int]" ), pd .Series , np .integer )
96
152
check (assert_type (left .rmul (f ), "pd.Series[float]" ), pd .Series , np .floating )
97
153
check (
98
154
assert_type (left .rmul (c ), "pd.Series[complex]" ), pd .Series , np .complexfloating
99
155
)
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 )
100
159
101
160
102
- def test_mul_pd_index () -> None :
161
+ def test_mul_pd_index (left : "pd.Series[bool]" ) -> None :
103
162
"""Test pd.Series[bool] * pandas Indexes"""
104
163
b = pd .Index ([True , False , True ])
105
164
i = pd .Index ([2 , 3 , 5 ])
106
165
f = pd .Index ([1.0 , 2.0 , 3.0 ])
107
166
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 )])
108
169
109
170
check (assert_type (left * b , "pd.Series[bool]" ), pd .Series , np .bool_ )
110
171
check (assert_type (left * i , "pd.Series[int]" ), pd .Series , np .integer )
111
172
check (assert_type (left * f , "pd.Series[float]" ), pd .Series , np .floating )
112
173
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 )
113
177
114
178
check (assert_type (b * left , "pd.Series[bool]" ), pd .Series , np .bool_ )
115
179
check (assert_type (i * left , "pd.Series[int]" ), pd .Series , np .integer )
116
180
check (assert_type (f * left , "pd.Series[float]" ), pd .Series , np .floating )
117
181
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 )
118
185
119
186
check (assert_type (left .mul (b ), "pd.Series[bool]" ), pd .Series , np .bool_ )
120
187
check (assert_type (left .mul (i ), "pd.Series[int]" ), pd .Series , np .integer )
121
188
check (assert_type (left .mul (f ), "pd.Series[float]" ), pd .Series , np .floating )
122
189
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 )
123
193
124
194
check (assert_type (left .rmul (b ), "pd.Series[bool]" ), pd .Series , np .bool_ )
125
195
check (assert_type (left .rmul (i ), "pd.Series[int]" ), pd .Series , np .integer )
126
196
check (assert_type (left .rmul (f ), "pd.Series[float]" ), pd .Series , np .floating )
127
197
check (
128
198
assert_type (left .rmul (c ), "pd.Series[complex]" ), pd .Series , np .complexfloating
129
199
)
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 )
130
203
131
204
132
- def test_mul_pd_series () -> None :
205
+ def test_mul_pd_series (left : "pd.Series[bool]" ) -> None :
133
206
"""Test pd.Series[bool] * pandas Series"""
134
207
b = pd .Series ([True , False , True ])
135
208
i = pd .Series ([2 , 3 , 5 ])
136
209
f = pd .Series ([1.0 , 2.0 , 3.0 ])
137
210
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 )])
138
213
139
214
check (assert_type (left * b , "pd.Series[bool]" ), pd .Series , np .bool_ )
140
215
check (assert_type (left * i , "pd.Series[int]" ), pd .Series , np .integer )
141
216
check (assert_type (left * f , "pd.Series[float]" ), pd .Series , np .floating )
142
217
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 )
143
221
144
222
check (assert_type (b * left , "pd.Series[bool]" ), pd .Series , np .bool_ )
145
223
check (assert_type (i * left , "pd.Series[int]" ), pd .Series , np .integer )
146
224
check (assert_type (f * left , "pd.Series[float]" ), pd .Series , np .floating )
147
225
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 )
148
229
149
230
check (assert_type (left .mul (b ), "pd.Series[bool]" ), pd .Series , np .bool_ )
150
231
check (assert_type (left .mul (i ), "pd.Series[int]" ), pd .Series , np .integer )
151
232
check (assert_type (left .mul (f ), "pd.Series[float]" ), pd .Series , np .floating )
152
233
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 )
153
237
154
238
check (assert_type (left .rmul (b ), "pd.Series[bool]" ), pd .Series , np .bool_ )
155
239
check (assert_type (left .rmul (i ), "pd.Series[int]" ), pd .Series , np .integer )
156
240
check (assert_type (left .rmul (f ), "pd.Series[float]" ), pd .Series , np .floating )
157
241
check (
158
242
assert_type (left .rmul (c ), "pd.Series[complex]" ), pd .Series , np .complexfloating
159
243
)
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