Skip to content
57 changes: 42 additions & 15 deletions pandas-stubs/core/series.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ from pandas.core.dtypes.dtypes import CategoricalDtype

from pandas.plotting import PlotAccessor

_T_NUM_NON_BOOL = TypeVar("_T_NUM_NON_BOOL", int, float, complex)
_T_INT = TypeVar("_T_INT", bound=int)
_T_COMPLEX = TypeVar("_T_COMPLEX", bound=complex)

Expand Down Expand Up @@ -2100,17 +2101,22 @@ class Series(IndexOpsMixin[S1], NDFrame):
self: Series[Never], other: complex | _ListLike | Series
) -> Series: ...
@overload
def __truediv__(self, other: Series[Never]) -> Series: ...
def __truediv__(self, other: Series[Never]) -> Series: ... # type: ignore[overload-overlap]
@overload
def __truediv__(
self: Series[bool],
other: bool | np_ndarray_bool | Series[bool],
) -> Never: ...
@overload
def __truediv__(
self: Series[bool],
other: int | Sequence[int] | np_ndarray_anyint | np_ndarray_float | Series[int],
) -> Series[float]: ...
@overload
def __truediv__(
def __truediv__( # type: ignore[overload-overlap]
self: Series[bool],
other: _T_COMPLEX | Sequence[_T_COMPLEX] | Series[_T_COMPLEX],
) -> Series[_T_COMPLEX]: ...
other: _T_NUM_NON_BOOL | Sequence[_T_NUM_NON_BOOL] | Series[_T_NUM_NON_BOOL],
) -> Series[_T_NUM_NON_BOOL]: ...
@overload
def __truediv__(
self: Series[int],
Expand Down Expand Up @@ -2172,14 +2178,22 @@ class Series(IndexOpsMixin[S1], NDFrame):
axis: AxisIndex = 0,
) -> Series: ...
@overload
def truediv(
def truediv( # type: ignore[overload-overlap]
self,
other: Series[Never],
level: Level | None = None,
fill_value: float | None = None,
axis: AxisIndex = 0,
) -> Series: ...
@overload
def truediv(
self: Series[bool],
other: bool | np_ndarray_bool | Series[bool],
level: Level | None = None,
fill_value: float | None = None,
axis: AxisIndex = 0,
) -> Never: ...
@overload
def truediv(
self: Series[bool],
other: int | Sequence[int] | np_ndarray_anyint | np_ndarray_float | Series[int],
Expand All @@ -2188,13 +2202,13 @@ class Series(IndexOpsMixin[S1], NDFrame):
axis: AxisIndex = 0,
) -> Series[float]: ...
@overload
def truediv(
def truediv( # type: ignore[overload-overlap]
self: Series[bool],
other: _T_COMPLEX | Sequence[_T_COMPLEX] | Series[_T_COMPLEX],
other: _T_NUM_NON_BOOL | Sequence[_T_NUM_NON_BOOL] | Series[_T_NUM_NON_BOOL],
level: Level | None = None,
fill_value: float | None = None,
axis: AxisIndex = 0,
) -> Series[_T_COMPLEX]: ...
) -> Series[_T_NUM_NON_BOOL]: ...
@overload
def truediv(
self: Series[int],
Expand Down Expand Up @@ -2271,12 +2285,17 @@ class Series(IndexOpsMixin[S1], NDFrame):
@overload
def __rtruediv__(
self: Series[bool],
other: _T_INT | Sequence[_T_INT] | np_ndarray_anyint | np_ndarray_float,
) -> Series[float]: ...
other: bool | np_ndarray_bool,
) -> Never: ...
@overload
def __rtruediv__(
self: Series[bool], other: _T_COMPLEX | Sequence[_T_COMPLEX]
) -> Series[_T_COMPLEX]: ...
self: Series[bool],
other: int | Sequence[int] | np_ndarray_anyint | np_ndarray_float,
) -> Series[float]: ...
@overload
def __rtruediv__( # type: ignore[overload-overlap]
self: Series[bool], other: _T_NUM_NON_BOOL | Sequence[_T_NUM_NON_BOOL]
) -> Series[_T_NUM_NON_BOOL]: ...
@overload
def __rtruediv__(
self: Series[int],
Expand Down Expand Up @@ -2327,6 +2346,14 @@ class Series(IndexOpsMixin[S1], NDFrame):
axis: AxisIndex = 0,
) -> Series: ...
@overload
def rtruediv(
self: Series[bool],
other: bool | np_ndarray_bool | Series[bool],
level: Level | None = None,
fill_value: float | None = None,
axis: AxisIndex = 0,
) -> Never: ...
@overload
def rtruediv(
self: Series[bool],
other: int | Sequence[int] | np_ndarray_anyint | np_ndarray_float | Series[int],
Expand All @@ -2335,13 +2362,13 @@ class Series(IndexOpsMixin[S1], NDFrame):
axis: AxisIndex = 0,
) -> Series[float]: ...
@overload
def rtruediv(
def rtruediv( # type: ignore[overload-overlap]
self: Series[bool],
other: _T_COMPLEX | Sequence[_T_COMPLEX] | Series[_T_COMPLEX],
other: _T_NUM_NON_BOOL | Sequence[_T_NUM_NON_BOOL] | Series[_T_NUM_NON_BOOL],
level: Level | None = None,
fill_value: float | None = None,
axis: AxisIndex = 0,
) -> Series[_T_COMPLEX]: ...
) -> Series[_T_NUM_NON_BOOL]: ...
@overload
def rtruediv(
self: Series[int],
Expand Down
58 changes: 54 additions & 4 deletions tests/series/arithmetic/bool/test_truediv.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,37 @@
import numpy as np
from numpy import typing as npt # noqa: F401
import pandas as pd
from typing_extensions import assert_type
from typing_extensions import (
Never,
assert_type,
)

from tests import check
from tests import (
TYPE_CHECKING_INVALID_USAGE,
check,
)

left = pd.Series([True, False, True]) # left operand


def test_truediv_py_scalar() -> None:
"""Test pd.Series[bool] / Python native scalars"""
i, f, c = 1, 1.0, 1j
b, i, f, c = True, 1, 1.0, 1j

if TYPE_CHECKING_INVALID_USAGE:
assert_type(left / b, Never)
check(assert_type(left / i, "pd.Series[float]"), pd.Series, np.floating)
check(assert_type(left / f, "pd.Series[float]"), pd.Series, np.floating)
check(assert_type(left / c, "pd.Series[complex]"), pd.Series, np.complexfloating)

if TYPE_CHECKING_INVALID_USAGE:
assert_type(b / left, Never)
check(assert_type(i / left, "pd.Series[float]"), pd.Series, np.floating)
check(assert_type(f / left, "pd.Series[float]"), pd.Series, np.floating)
check(assert_type(c / left, "pd.Series[complex]"), pd.Series, np.complexfloating)

if TYPE_CHECKING_INVALID_USAGE:
assert_type(left.truediv(b), Never)
check(assert_type(left.truediv(i), "pd.Series[float]"), pd.Series, np.floating)
check(assert_type(left.truediv(f), "pd.Series[float]"), pd.Series, np.floating)
check(
Expand All @@ -28,10 +40,14 @@ def test_truediv_py_scalar() -> None:
np.complexfloating,
)

if TYPE_CHECKING_INVALID_USAGE:
assert_type(left.div(b), Never)
check(assert_type(left.div(i), "pd.Series[float]"), pd.Series, np.floating)
check(assert_type(left.div(f), "pd.Series[float]"), pd.Series, np.floating)
check(assert_type(left.div(c), "pd.Series[complex]"), pd.Series, np.complexfloating)

if TYPE_CHECKING_INVALID_USAGE:
assert_type(left.rtruediv(b), Never)
check(assert_type(left.rtruediv(i), "pd.Series[float]"), pd.Series, np.floating)
check(assert_type(left.rtruediv(f), "pd.Series[float]"), pd.Series, np.floating)
check(
Expand All @@ -40,6 +56,8 @@ def test_truediv_py_scalar() -> None:
np.complexfloating,
)

if TYPE_CHECKING_INVALID_USAGE:
assert_type(left.rdiv(b), Never)
check(assert_type(left.rdiv(i), "pd.Series[float]"), pd.Series, np.floating)
check(assert_type(left.rdiv(f), "pd.Series[float]"), pd.Series, np.floating)
check(
Expand All @@ -49,16 +67,19 @@ def test_truediv_py_scalar() -> None:

def test_truediv_py_sequence() -> None:
"""Test pd.Series[bool] / Python native sequence"""
i, f, c = [2, 3, 5], [1.0, 2.0, 3.0], [1j, 1j, 4j]
b, i, f, c = [True, False, True], [2, 3, 5], [1.0, 2.0, 3.0], [1j, 1j, 4j]

check(assert_type(left / b, "pd.Series[float]"), pd.Series, np.floating)
check(assert_type(left / i, "pd.Series[float]"), pd.Series, np.floating)
check(assert_type(left / f, "pd.Series[float]"), pd.Series, np.floating)
check(assert_type(left / c, "pd.Series[complex]"), pd.Series, np.complexfloating)

check(assert_type(b / left, "pd.Series[float]"), pd.Series, np.floating)
check(assert_type(i / left, "pd.Series[float]"), pd.Series, np.floating)
check(assert_type(f / left, "pd.Series[float]"), pd.Series, np.floating)
check(assert_type(c / left, "pd.Series[complex]"), pd.Series, np.complexfloating)

check(assert_type(left.truediv(b), "pd.Series[float]"), pd.Series, np.floating)
check(assert_type(left.truediv(i), "pd.Series[float]"), pd.Series, np.floating)
check(assert_type(left.truediv(f), "pd.Series[float]"), pd.Series, np.floating)
check(
Expand All @@ -67,10 +88,12 @@ def test_truediv_py_sequence() -> None:
np.complexfloating,
)

check(assert_type(left.div(b), "pd.Series[float]"), pd.Series, np.floating)
check(assert_type(left.div(i), "pd.Series[float]"), pd.Series, np.floating)
check(assert_type(left.div(f), "pd.Series[float]"), pd.Series, np.floating)
check(assert_type(left.div(c), "pd.Series[complex]"), pd.Series, np.complexfloating)

check(assert_type(left.rtruediv(b), "pd.Series[float]"), pd.Series, np.floating)
check(assert_type(left.rtruediv(i), "pd.Series[float]"), pd.Series, np.floating)
check(assert_type(left.rtruediv(f), "pd.Series[float]"), pd.Series, np.floating)
check(
Expand All @@ -79,6 +102,7 @@ def test_truediv_py_sequence() -> None:
np.complexfloating,
)

check(assert_type(left.rdiv(b), "pd.Series[float]"), pd.Series, np.floating)
check(assert_type(left.rdiv(i), "pd.Series[float]"), pd.Series, np.floating)
check(assert_type(left.rdiv(f), "pd.Series[float]"), pd.Series, np.floating)
check(
Expand All @@ -88,17 +112,22 @@ def test_truediv_py_sequence() -> None:

def test_truediv_numpy_array() -> None:
"""Test pd.Series[bool] / numpy array"""
b = np.array([True, False, True], np.bool_)
i = np.array([2, 3, 5], np.int64)
f = np.array([1.0, 2.0, 3.0], np.float64)
c = np.array([1.1j, 2.2j, 4.1j], np.complex128)

if TYPE_CHECKING_INVALID_USAGE:
assert_type(left / b, Never)
check(assert_type(left / i, "pd.Series[float]"), pd.Series, np.floating)
check(assert_type(left / f, "pd.Series[float]"), pd.Series, np.floating)
check(assert_type(left / c, "pd.Series[complex]"), pd.Series, np.complexfloating)

# `numpy` typing gives the corresponding `ndarray`s in the static type
# checking, where our `__rtruediv__` cannot override. At runtime, they return
# `Series`s with the correct element type.
if TYPE_CHECKING_INVALID_USAGE:
assert_type(b / left, "npt.NDArray[np.float64]")
check(assert_type(i / left, "npt.NDArray[np.float64]"), pd.Series, np.floating)
check(assert_type(f / left, "npt.NDArray[np.float64]"), pd.Series, np.floating)
check(
Expand All @@ -107,6 +136,8 @@ def test_truediv_numpy_array() -> None:
np.complexfloating,
)

if TYPE_CHECKING_INVALID_USAGE:
assert_type(left.truediv(b), Never)
check(assert_type(left.truediv(i), "pd.Series[float]"), pd.Series, np.floating)
check(assert_type(left.truediv(f), "pd.Series[float]"), pd.Series, np.floating)
check(
Expand All @@ -115,10 +146,14 @@ def test_truediv_numpy_array() -> None:
np.complexfloating,
)

if TYPE_CHECKING_INVALID_USAGE:
assert_type(left.div(b), Never)
check(assert_type(left.div(i), "pd.Series[float]"), pd.Series, np.floating)
check(assert_type(left.div(f), "pd.Series[float]"), pd.Series, np.floating)
check(assert_type(left.div(c), "pd.Series[complex]"), pd.Series, np.complexfloating)

if TYPE_CHECKING_INVALID_USAGE:
assert_type(left.rtruediv(b), Never)
check(assert_type(left.rtruediv(i), "pd.Series[float]"), pd.Series, np.floating)
check(assert_type(left.rtruediv(f), "pd.Series[float]"), pd.Series, np.floating)
check(
Expand All @@ -127,6 +162,8 @@ def test_truediv_numpy_array() -> None:
np.complexfloating,
)

if TYPE_CHECKING_INVALID_USAGE:
assert_type(left.rdiv(b), Never)
check(assert_type(left.rdiv(i), "pd.Series[float]"), pd.Series, np.floating)
check(assert_type(left.rdiv(f), "pd.Series[float]"), pd.Series, np.floating)
check(
Expand All @@ -136,18 +173,25 @@ def test_truediv_numpy_array() -> None:

def test_truediv_pd_series() -> None:
"""Test pd.Series[bool] / pandas series"""
b = pd.Series([True, False, True])
i = pd.Series([2, 3, 5])
f = pd.Series([1.0, 2.0, 3.0])
c = pd.Series([1.1j, 2.2j, 4.1j])

if TYPE_CHECKING_INVALID_USAGE:
assert_type(left / b, Never)
check(assert_type(left / i, "pd.Series[float]"), pd.Series, np.floating)
check(assert_type(left / f, "pd.Series[float]"), pd.Series, np.floating)
check(assert_type(left / c, "pd.Series[complex]"), pd.Series, np.complexfloating)

if TYPE_CHECKING_INVALID_USAGE:
assert_type(b / left, Never)
check(assert_type(i / left, "pd.Series[float]"), pd.Series, np.floating)
check(assert_type(f / left, "pd.Series[float]"), pd.Series, np.floating)
check(assert_type(c / left, "pd.Series[complex]"), pd.Series, np.complexfloating)

if TYPE_CHECKING_INVALID_USAGE:
assert_type(left.truediv(b), Never)
check(assert_type(left.truediv(i), "pd.Series[float]"), pd.Series, np.floating)
check(assert_type(left.truediv(f), "pd.Series[float]"), pd.Series, np.floating)
check(
Expand All @@ -156,10 +200,14 @@ def test_truediv_pd_series() -> None:
np.complexfloating,
)

if TYPE_CHECKING_INVALID_USAGE:
assert_type(left.div(b), Never)
check(assert_type(left.div(i), "pd.Series[float]"), pd.Series, np.floating)
check(assert_type(left.div(f), "pd.Series[float]"), pd.Series, np.floating)
check(assert_type(left.div(c), "pd.Series[complex]"), pd.Series, np.complexfloating)

if TYPE_CHECKING_INVALID_USAGE:
assert_type(left.rtruediv(b), Never)
check(assert_type(left.rtruediv(i), "pd.Series[float]"), pd.Series, np.floating)
check(assert_type(left.rtruediv(f), "pd.Series[float]"), pd.Series, np.floating)
check(
Expand All @@ -168,6 +216,8 @@ def test_truediv_pd_series() -> None:
np.complexfloating,
)

if TYPE_CHECKING_INVALID_USAGE:
assert_type(left.rdiv(b), Never)
check(assert_type(left.rdiv(i), "pd.Series[float]"), pd.Series, np.floating)
check(assert_type(left.rdiv(f), "pd.Series[float]"), pd.Series, np.floating)
check(
Expand Down
2 changes: 1 addition & 1 deletion tests/series/arithmetic/test_truediv.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ def test_truediv_pd_series() -> None:
check(assert_type(left / f, pd.Series), pd.Series)
check(assert_type(left / c, pd.Series), pd.Series)

check(assert_type(b / left, pd.Series), pd.Series)
check(assert_type(b / left, pd.Series), pd.Series) # type: ignore[assert-type]
check(assert_type(i / left, pd.Series), pd.Series)
check(assert_type(f / left, pd.Series), pd.Series)
check(assert_type(c / left, pd.Series), pd.Series)
Expand Down
2 changes: 1 addition & 1 deletion tests/series/test_series.py
Original file line number Diff line number Diff line change
Expand Up @@ -1607,7 +1607,7 @@ def test_series_min_max_sub_axis() -> None:
check(assert_type(sa, pd.Series), pd.Series)
check(assert_type(ss, pd.Series), pd.Series)
check(assert_type(sm, pd.Series), pd.Series)
check(assert_type(sd, pd.Series), pd.Series)
check(assert_type(sd, pd.Series), pd.Series) # type: ignore[assert-type]


def test_series_index_isin() -> None:
Expand Down
Loading