Skip to content

Commit d2d11d1

Browse files
committed
complex
1 parent b9c57d2 commit d2d11d1

File tree

10 files changed

+258
-33
lines changed

10 files changed

+258
-33
lines changed

pandas-stubs/core/indexes/base.pyi

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -752,6 +752,8 @@ class Index(IndexOpsMixin[S1]):
752752
def __mul__(self, other: Index[Never]) -> Index: ...
753753
@overload
754754
def __mul__(self, other: np_ndarray_dt) -> Never: ...
755+
@overload
756+
def __mul__(self: Index[complex], other: np_ndarray_td) -> Never: ...
755757
# pandas-dev/pandas#62524
756758
@overload
757759
def __mul__( # type: ignore[overload-overlap]
@@ -823,6 +825,8 @@ class Index(IndexOpsMixin[S1]):
823825
def __rmul__(self, other: Index[Never]) -> Index: ...
824826
@overload
825827
def __rmul__(self, other: np_ndarray_dt) -> Never: ...
828+
@overload
829+
def __rmul__(self: Index[complex], other: np_ndarray_td) -> Never: ...
826830
# pandas-dev/pandas#62524
827831
@overload
828832
def __rmul__( # type: ignore[overload-overlap]

pandas-stubs/core/series.pyi

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2548,6 +2548,8 @@ class Series(IndexOpsMixin[S1], NDFrame):
25482548
@overload
25492549
def __mul__(self, other: np_ndarray_dt) -> Never: ...
25502550
@overload
2551+
def __mul__(self: Series[complex], other: np_ndarray_td) -> Never: ...
2552+
@overload
25512553
def __mul__(
25522554
self: Series[bool] | Series[int] | Series[float],
25532555
other: (
@@ -2777,6 +2779,8 @@ class Series(IndexOpsMixin[S1], NDFrame):
27772779
@overload
27782780
def __rmul__(self: Series[Timestamp], other: np_ndarray) -> Never: ...
27792781
@overload
2782+
def __rmul__(self: Series[complex], other: np_ndarray_td) -> Never: ...
2783+
@overload
27802784
def __rmul__(self: Series[Timedelta], other: np_ndarray_complex) -> Never: ...
27812785
@overload
27822786
def __rmul__(
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
from datetime import (
2+
datetime,
3+
timedelta,
4+
)
5+
from typing import Any
6+
7+
import numpy as np
8+
from numpy import typing as npt # noqa: F401
9+
import pandas as pd
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+
)
20+
21+
22+
@pytest.fixture
23+
def left() -> "pd.Index[complex]":
24+
"""left operand"""
25+
lo = pd.Index([1j, 2j, 3j])
26+
return check(assert_type(lo, "pd.Index[complex]"), pd.Index, np.complexfloating)
27+
28+
29+
def test_mul_py_scalar(left: "pd.Index[complex]") -> None:
30+
"""Test pd.Index[complex] * Python native scalars"""
31+
b, i, f, c = True, 1, 1.0, 1j
32+
s, d = datetime(2025, 9, 30), timedelta(seconds=1)
33+
34+
check(assert_type(left * b, "pd.Index[complex]"), pd.Index, np.complexfloating)
35+
check(assert_type(left * i, "pd.Index[complex]"), pd.Index, np.complexfloating)
36+
check(assert_type(left * f, "pd.Index[complex]"), pd.Index, np.complexfloating)
37+
check(assert_type(left * c, "pd.Index[complex]"), pd.Index, np.complexfloating)
38+
if TYPE_CHECKING_INVALID_USAGE:
39+
_05 = left * s # type: ignore[operator] # pyright: ignore[reportOperatorIssue]
40+
_06 = left * d # type: ignore[operator] # pyright: ignore[reportOperatorIssue]
41+
42+
check(assert_type(b * left, "pd.Index[complex]"), pd.Index, np.complexfloating)
43+
check(assert_type(i * left, "pd.Index[complex]"), pd.Index, np.complexfloating)
44+
check(assert_type(f * left, "pd.Index[complex]"), pd.Index, np.complexfloating)
45+
check(assert_type(c * left, "pd.Index[complex]"), pd.Index, np.complexfloating)
46+
if TYPE_CHECKING_INVALID_USAGE:
47+
_15 = s * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue]
48+
_16 = d * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue]
49+
50+
51+
def test_mul_py_sequence(left: "pd.Index[complex]") -> None:
52+
"""Test pd.Index[complex] * Python native sequences"""
53+
b, i, f, c = [True, False, True], [2, 3, 5], [1.0, 2.0, 3.0], [1j, 1j, 4j]
54+
s = [datetime(2025, 9, d) for d in (27, 28, 29)]
55+
d = [timedelta(seconds=s + 1) for s in range(3)]
56+
57+
check(assert_type(left * b, "pd.Index[complex]"), pd.Index, np.complexfloating)
58+
check(assert_type(left * i, "pd.Index[complex]"), pd.Index, np.complexfloating)
59+
check(assert_type(left * f, "pd.Index[complex]"), pd.Index, np.complexfloating)
60+
check(assert_type(left * c, "pd.Index[complex]"), pd.Index, np.complexfloating)
61+
if TYPE_CHECKING_INVALID_USAGE:
62+
_05 = left * s # type: ignore[operator] # pyright: ignore[reportOperatorIssue]
63+
_06 = left * d # type: ignore[operator] # pyright: ignore[reportOperatorIssue]
64+
65+
check(assert_type(b * left, "pd.Index[complex]"), pd.Index, np.complexfloating)
66+
check(assert_type(i * left, "pd.Index[complex]"), pd.Index, np.complexfloating)
67+
check(assert_type(f * left, "pd.Index[complex]"), pd.Index, np.complexfloating)
68+
check(assert_type(c * left, "pd.Index[complex]"), pd.Index, np.complexfloating)
69+
if TYPE_CHECKING_INVALID_USAGE:
70+
_15 = s * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue]
71+
_16 = d * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue]
72+
73+
74+
def test_mul_numpy_array(left: "pd.Index[complex]") -> None:
75+
"""Test pd.Index[complex] * numpy arrays"""
76+
b = np.array([True, False, True], np.bool_)
77+
i = np.array([2, 3, 5], np.int64)
78+
f = np.array([1.0, 2.0, 3.0], np.float64)
79+
c = np.array([1.1j, 2.2j, 4.1j], np.complex128)
80+
s = np.array([np.datetime64(f"2025-09-{d}") for d in (27, 28, 29)], np.datetime64)
81+
d = np.array([np.timedelta64(s + 1, "s") for s in range(3)], np.timedelta64)
82+
83+
check(assert_type(left * b, "pd.Index[complex]"), pd.Index, np.complexfloating)
84+
check(assert_type(left * i, "pd.Index[complex]"), pd.Index, np.complexfloating)
85+
check(assert_type(left * f, "pd.Index[complex]"), pd.Index, np.complexfloating)
86+
check(assert_type(left * c, "pd.Index[complex]"), pd.Index, np.complexfloating)
87+
if TYPE_CHECKING_INVALID_USAGE:
88+
assert_type(left * s, Never)
89+
assert_type(left * d, Never)
90+
91+
# `numpy` typing gives the corresponding `ndarray`s in the static type
92+
# checking, where our `__rmul__` cannot override. At runtime, they return
93+
# `Index` with the correct element type.
94+
check(assert_type(b * left, "npt.NDArray[np.bool_]"), pd.Index, np.complexfloating)
95+
check(assert_type(i * left, "npt.NDArray[np.int64]"), pd.Index, np.complexfloating)
96+
check(
97+
assert_type(f * left, "npt.NDArray[np.float64]"), pd.Index, np.complexfloating
98+
)
99+
check(
100+
assert_type(c * left, "npt.NDArray[np.complex128]"),
101+
pd.Index,
102+
np.complexfloating,
103+
)
104+
if TYPE_CHECKING_INVALID_USAGE:
105+
assert_type(s * left, Any)
106+
assert_type(d * left, "npt.NDArray[np.timedelta64]")
107+
108+
109+
def test_mul_pd_index(left: "pd.Index[complex]") -> None:
110+
"""Test pd.Index[complex] * pandas Indexes"""
111+
b = pd.Index([True, False, True])
112+
i = pd.Index([2, 3, 5])
113+
f = pd.Index([1.0, 2.0, 3.0])
114+
c = pd.Index([1.1j, 2.2j, 4.1j])
115+
s = pd.Index([datetime(2025, 9, d) for d in (27, 28, 29)])
116+
d = pd.Index([timedelta(seconds=s + 1) for s in range(3)])
117+
118+
check(assert_type(left * b, "pd.Index[complex]"), pd.Index, np.complexfloating)
119+
check(assert_type(left * i, "pd.Index[complex]"), pd.Index, np.complexfloating)
120+
check(assert_type(left * f, "pd.Index[complex]"), pd.Index, np.complexfloating)
121+
check(assert_type(left * c, "pd.Index[complex]"), pd.Index, np.complexfloating)
122+
if TYPE_CHECKING_INVALID_USAGE:
123+
_05 = left * s # type: ignore[operator] # pyright: ignore[reportOperatorIssue]
124+
_06 = left * d # type: ignore[operator] # pyright: ignore[reportOperatorIssue]
125+
126+
check(assert_type(b * left, "pd.Index[complex]"), pd.Index, np.complexfloating)
127+
check(assert_type(i * left, "pd.Index[complex]"), pd.Index, np.complexfloating)
128+
check(assert_type(f * left, "pd.Index[complex]"), pd.Index, np.complexfloating)
129+
check(assert_type(c * left, "pd.Index[complex]"), pd.Index, np.complexfloating)
130+
if TYPE_CHECKING_INVALID_USAGE:
131+
_15 = s * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue]
132+
_16 = d * left # type: ignore[operator] # pyright: ignore[reportOperatorIssue]
File renamed without changes.

tests/indexes/arithmetic/float/test_mul.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ def test_mul_numpy_array(left: "pd.Index[float]") -> None:
9090

9191
# `numpy` typing gives the corresponding `ndarray`s in the static type
9292
# checking, where our `__rmul__` cannot override. At runtime, they return
93-
# `Series` with the correct element type.
93+
# `Index` with the correct element type.
9494
check(assert_type(b * left, "npt.NDArray[np.bool_]"), pd.Index, np.floating)
9595
check(assert_type(i * left, "npt.NDArray[np.int64]"), pd.Index, np.floating)
9696
check(assert_type(f * left, "npt.NDArray[np.float64]"), pd.Index, np.floating)

tests/indexes/arithmetic/int/test_mul.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ def test_mul_numpy_array(left: "pd.Index[int]") -> None:
9292

9393
# `numpy` typing gives the corresponding `ndarray`s in the static type
9494
# checking, where our `__rmul__` cannot override. At runtime, they return
95-
# `Series` with the correct element type.
95+
# `Index` with the correct element type.
9696
check(assert_type(b * left, "npt.NDArray[np.bool_]"), pd.Index, np.integer)
9797
check(assert_type(i * left, "npt.NDArray[np.int64]"), pd.Index, np.integer)
9898
check(assert_type(f * left, "npt.NDArray[np.float64]"), pd.Index, np.floating)

0 commit comments

Comments
 (0)