Skip to content

Commit 7c3cb99

Browse files
authored
Further improve return types in the numbers module (#11375)
1 parent 48a0497 commit 7c3cb99

File tree

1 file changed

+113
-58
lines changed

1 file changed

+113
-58
lines changed

stdlib/numbers.pyi

Lines changed: 113 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,154 +1,209 @@
11
# Note: these stubs are incomplete. The more complex type
22
# signatures are currently omitted.
33
#
4-
# Use SupportsComplex, SupportsFloat and SupportsIndex for return types in this module
4+
# Use _ComplexLike, _RealLike and _IntegralLike for return types in this module
55
# rather than `numbers.Complex`, `numbers.Real` and `numbers.Integral`,
66
# to avoid an excessive number of `type: ignore`s in subclasses of these ABCs
77
# (since type checkers don't see `complex` as a subtype of `numbers.Complex`,
88
# nor `float` as a subtype of `numbers.Real`, etc.)
99

10-
import sys
1110
from _typeshed import Incomplete
1211
from abc import ABCMeta, abstractmethod
13-
from typing import Literal, SupportsFloat, SupportsIndex, overload
14-
from typing_extensions import TypeAlias
12+
from typing import Literal, Protocol, overload
1513

16-
if sys.version_info >= (3, 11):
17-
from typing import SupportsComplex as _SupportsComplex
18-
else:
19-
# builtins.complex didn't have a __complex__ method on older Pythons
20-
import typing
14+
__all__ = ["Number", "Complex", "Real", "Rational", "Integral"]
2115

22-
_SupportsComplex: TypeAlias = typing.SupportsComplex | complex
16+
############################
17+
# Protocols for return types
18+
############################
2319

24-
__all__ = ["Number", "Complex", "Real", "Rational", "Integral"]
20+
# `_ComplexLike` is a structural-typing approximation
21+
# of the `Complex` ABC, which is not (and cannot be) a protocol
22+
#
23+
# NOTE: We can't include `__complex__` here,
24+
# as we want `int` to be seen as a subtype of `_ComplexLike`,
25+
# and `int.__complex__` does not exist :(
26+
class _ComplexLike(Protocol):
27+
def __neg__(self) -> _ComplexLike: ...
28+
def __pos__(self) -> _ComplexLike: ...
29+
def __abs__(self) -> _RealLike: ...
30+
31+
# _RealLike is a structural-typing approximation
32+
# of the `Real` ABC, which is not (and cannot be) a protocol
33+
class _RealLike(_ComplexLike, Protocol):
34+
def __trunc__(self) -> _IntegralLike: ...
35+
def __floor__(self) -> _IntegralLike: ...
36+
def __ceil__(self) -> _IntegralLike: ...
37+
def __float__(self) -> float: ...
38+
# Overridden from `_ComplexLike`
39+
# for a more precise return type:
40+
def __neg__(self) -> _RealLike: ...
41+
def __pos__(self) -> _RealLike: ...
42+
43+
# _IntegralLike is a structural-typing approximation
44+
# of the `Integral` ABC, which is not (and cannot be) a protocol
45+
class _IntegralLike(_RealLike, Protocol):
46+
def __invert__(self) -> _IntegralLike: ...
47+
def __int__(self) -> int: ...
48+
def __index__(self) -> int: ...
49+
# Overridden from `_ComplexLike`
50+
# for a more precise return type:
51+
def __abs__(self) -> _IntegralLike: ...
52+
# Overridden from `RealLike`
53+
# for a more precise return type:
54+
def __neg__(self) -> _IntegralLike: ...
55+
def __pos__(self) -> _IntegralLike: ...
56+
57+
#################
58+
# Module "proper"
59+
#################
2560

2661
class Number(metaclass=ABCMeta):
2762
@abstractmethod
2863
def __hash__(self) -> int: ...
2964

3065
# See comment at the top of the file
3166
# for why some of these return types are purposefully vague
32-
class Complex(Number):
67+
class Complex(Number, _ComplexLike):
3368
@abstractmethod
3469
def __complex__(self) -> complex: ...
3570
def __bool__(self) -> bool: ...
3671
@property
3772
@abstractmethod
38-
def real(self) -> SupportsFloat: ...
73+
def real(self) -> _RealLike: ...
3974
@property
4075
@abstractmethod
41-
def imag(self) -> SupportsFloat: ...
76+
def imag(self) -> _RealLike: ...
4277
@abstractmethod
43-
def __add__(self, other) -> _SupportsComplex: ...
78+
def __add__(self, other) -> _ComplexLike: ...
4479
@abstractmethod
45-
def __radd__(self, other) -> _SupportsComplex: ...
80+
def __radd__(self, other) -> _ComplexLike: ...
4681
@abstractmethod
47-
def __neg__(self) -> _SupportsComplex: ...
82+
def __neg__(self) -> _ComplexLike: ...
4883
@abstractmethod
49-
def __pos__(self) -> _SupportsComplex: ...
50-
def __sub__(self, other) -> _SupportsComplex: ...
51-
def __rsub__(self, other) -> _SupportsComplex: ...
84+
def __pos__(self) -> _ComplexLike: ...
85+
def __sub__(self, other) -> _ComplexLike: ...
86+
def __rsub__(self, other) -> _ComplexLike: ...
5287
@abstractmethod
53-
def __mul__(self, other) -> _SupportsComplex: ...
88+
def __mul__(self, other) -> _ComplexLike: ...
5489
@abstractmethod
55-
def __rmul__(self, other) -> _SupportsComplex: ...
90+
def __rmul__(self, other) -> _ComplexLike: ...
5691
@abstractmethod
57-
def __truediv__(self, other) -> _SupportsComplex: ...
92+
def __truediv__(self, other) -> _ComplexLike: ...
5893
@abstractmethod
59-
def __rtruediv__(self, other) -> _SupportsComplex: ...
94+
def __rtruediv__(self, other) -> _ComplexLike: ...
6095
@abstractmethod
61-
def __pow__(self, exponent) -> _SupportsComplex: ...
96+
def __pow__(self, exponent) -> _ComplexLike: ...
6297
@abstractmethod
63-
def __rpow__(self, base) -> _SupportsComplex: ...
98+
def __rpow__(self, base) -> _ComplexLike: ...
6499
@abstractmethod
65-
def __abs__(self) -> SupportsFloat: ...
100+
def __abs__(self) -> _RealLike: ...
66101
@abstractmethod
67-
def conjugate(self) -> _SupportsComplex: ...
102+
def conjugate(self) -> _ComplexLike: ...
68103
@abstractmethod
69104
def __eq__(self, other: object) -> bool: ...
70105

71106
# See comment at the top of the file
72107
# for why some of these return types are purposefully vague
73-
class Real(Complex, SupportsFloat):
108+
class Real(Complex, _RealLike):
74109
@abstractmethod
75110
def __float__(self) -> float: ...
76111
@abstractmethod
77-
def __trunc__(self) -> SupportsIndex: ...
112+
def __trunc__(self) -> _IntegralLike: ...
78113
@abstractmethod
79-
def __floor__(self) -> SupportsIndex: ...
114+
def __floor__(self) -> _IntegralLike: ...
80115
@abstractmethod
81-
def __ceil__(self) -> SupportsIndex: ...
116+
def __ceil__(self) -> _IntegralLike: ...
82117
@abstractmethod
83118
@overload
84-
def __round__(self, ndigits: None = None) -> SupportsIndex: ...
119+
def __round__(self, ndigits: None = None) -> _IntegralLike: ...
85120
@abstractmethod
86121
@overload
87-
def __round__(self, ndigits: int) -> SupportsFloat: ...
88-
def __divmod__(self, other) -> tuple[SupportsFloat, SupportsFloat]: ...
89-
def __rdivmod__(self, other) -> tuple[SupportsFloat, SupportsFloat]: ...
122+
def __round__(self, ndigits: int) -> _RealLike: ...
123+
def __divmod__(self, other) -> tuple[_RealLike, _RealLike]: ...
124+
def __rdivmod__(self, other) -> tuple[_RealLike, _RealLike]: ...
90125
@abstractmethod
91-
def __floordiv__(self, other) -> SupportsFloat: ...
126+
def __floordiv__(self, other) -> _RealLike: ...
92127
@abstractmethod
93-
def __rfloordiv__(self, other) -> SupportsFloat: ...
128+
def __rfloordiv__(self, other) -> _RealLike: ...
94129
@abstractmethod
95-
def __mod__(self, other) -> SupportsFloat: ...
130+
def __mod__(self, other) -> _RealLike: ...
96131
@abstractmethod
97-
def __rmod__(self, other) -> SupportsFloat: ...
132+
def __rmod__(self, other) -> _RealLike: ...
98133
@abstractmethod
99134
def __lt__(self, other) -> bool: ...
100135
@abstractmethod
101136
def __le__(self, other) -> bool: ...
102137
def __complex__(self) -> complex: ...
103138
@property
104-
def real(self) -> SupportsFloat: ...
139+
def real(self) -> _RealLike: ...
105140
@property
106141
def imag(self) -> Literal[0]: ...
107-
def conjugate(self) -> SupportsFloat: ... # type: ignore[override]
142+
def conjugate(self) -> _RealLike: ...
143+
# Not actually overridden at runtime,
144+
# but we override these in the stub to give them more precise return types:
145+
@abstractmethod
146+
def __pos__(self) -> _RealLike: ...
147+
@abstractmethod
148+
def __neg__(self) -> _RealLike: ...
108149

109150
# See comment at the top of the file
110151
# for why some of these return types are purposefully vague
111152
class Rational(Real):
112153
@property
113154
@abstractmethod
114-
def numerator(self) -> SupportsIndex: ...
155+
def numerator(self) -> _IntegralLike: ...
115156
@property
116157
@abstractmethod
117-
def denominator(self) -> SupportsIndex: ...
158+
def denominator(self) -> _IntegralLike: ...
118159
def __float__(self) -> float: ...
119160

120161
# See comment at the top of the file
121162
# for why some of these return types are purposefully vague
122-
class Integral(Rational):
163+
class Integral(Rational, _IntegralLike):
123164
@abstractmethod
124165
def __int__(self) -> int: ...
125166
def __index__(self) -> int: ...
126167
@abstractmethod
127-
def __pow__(self, exponent, modulus: Incomplete | None = None) -> SupportsIndex: ... # type: ignore[override]
168+
def __pow__(self, exponent, modulus: Incomplete | None = None) -> _IntegralLike: ...
128169
@abstractmethod
129-
def __lshift__(self, other) -> SupportsIndex: ...
170+
def __lshift__(self, other) -> _IntegralLike: ...
130171
@abstractmethod
131-
def __rlshift__(self, other) -> SupportsIndex: ...
172+
def __rlshift__(self, other) -> _IntegralLike: ...
132173
@abstractmethod
133-
def __rshift__(self, other) -> SupportsIndex: ...
174+
def __rshift__(self, other) -> _IntegralLike: ...
134175
@abstractmethod
135-
def __rrshift__(self, other) -> SupportsIndex: ...
176+
def __rrshift__(self, other) -> _IntegralLike: ...
136177
@abstractmethod
137-
def __and__(self, other) -> SupportsIndex: ...
178+
def __and__(self, other) -> _IntegralLike: ...
138179
@abstractmethod
139-
def __rand__(self, other) -> SupportsIndex: ...
180+
def __rand__(self, other) -> _IntegralLike: ...
140181
@abstractmethod
141-
def __xor__(self, other) -> SupportsIndex: ...
182+
def __xor__(self, other) -> _IntegralLike: ...
142183
@abstractmethod
143-
def __rxor__(self, other) -> SupportsIndex: ...
184+
def __rxor__(self, other) -> _IntegralLike: ...
144185
@abstractmethod
145-
def __or__(self, other) -> SupportsIndex: ...
186+
def __or__(self, other) -> _IntegralLike: ...
146187
@abstractmethod
147-
def __ror__(self, other) -> SupportsIndex: ...
188+
def __ror__(self, other) -> _IntegralLike: ...
148189
@abstractmethod
149-
def __invert__(self) -> SupportsIndex: ...
190+
def __invert__(self) -> _IntegralLike: ...
150191
def __float__(self) -> float: ...
151192
@property
152-
def numerator(self) -> SupportsIndex: ...
193+
def numerator(self) -> _IntegralLike: ...
153194
@property
154195
def denominator(self) -> Literal[1]: ...
196+
# Not actually overridden at runtime,
197+
# but we override these in the stub to give them more precise return types:
198+
@abstractmethod
199+
def __pos__(self) -> _IntegralLike: ...
200+
@abstractmethod
201+
def __neg__(self) -> _IntegralLike: ...
202+
@abstractmethod
203+
def __abs__(self) -> _IntegralLike: ...
204+
@abstractmethod
205+
@overload
206+
def __round__(self, ndigits: None = None) -> _IntegralLike: ...
207+
@abstractmethod
208+
@overload
209+
def __round__(self, ndigits: int) -> _IntegralLike: ...

0 commit comments

Comments
 (0)