From 890d2d8196c0b4fde1007863cb75da14464b5b46 Mon Sep 17 00:00:00 2001 From: AlexWaygood Date: Tue, 6 Feb 2024 15:30:48 +0000 Subject: [PATCH 01/10] Further improve return types in the `numbers` module --- stdlib/numbers.pyi | 48 +++++++++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/stdlib/numbers.pyi b/stdlib/numbers.pyi index 9f507d8335cf..1dca039ff7c6 100644 --- a/stdlib/numbers.pyi +++ b/stdlib/numbers.pyi @@ -10,7 +10,7 @@ import sys from _typeshed import Incomplete from abc import ABCMeta, abstractmethod -from typing import Literal, SupportsFloat, SupportsIndex, overload +from typing import Literal, Protocol, SupportsFloat, overload from typing_extensions import TypeAlias if sys.version_info >= (3, 11): @@ -74,14 +74,14 @@ class Real(Complex, SupportsFloat): @abstractmethod def __float__(self) -> float: ... @abstractmethod - def __trunc__(self) -> SupportsIndex: ... + def __trunc__(self) -> _IntegralLike: ... @abstractmethod - def __floor__(self) -> SupportsIndex: ... + def __floor__(self) -> _IntegralLike: ... @abstractmethod - def __ceil__(self) -> SupportsIndex: ... + def __ceil__(self) -> _IntegralLike: ... @abstractmethod @overload - def __round__(self, ndigits: None = None) -> SupportsIndex: ... + def __round__(self, ndigits: None = None) -> _IntegralLike: ... @abstractmethod @overload def __round__(self, ndigits: int) -> SupportsFloat: ... @@ -111,12 +111,20 @@ class Real(Complex, SupportsFloat): class Rational(Real): @property @abstractmethod - def numerator(self) -> SupportsIndex: ... + def numerator(self) -> _IntegralLike: ... @property @abstractmethod - def denominator(self) -> SupportsIndex: ... + def denominator(self) -> _IntegralLike: ... def __float__(self) -> float: ... +# An approximation of numbers.Integral +# that works with structural typing +class _IntegralLike(Protocol): + def __int__(self) -> int: ... + def __index__(self) -> int: ... + def __float__(self) -> float: ... + def __invert__(self) -> _IntegralLike: ... + # See comment at the top of the file # for why some of these return types are purposefully vague class Integral(Rational): @@ -124,31 +132,31 @@ class Integral(Rational): def __int__(self) -> int: ... def __index__(self) -> int: ... @abstractmethod - def __pow__(self, exponent, modulus: Incomplete | None = None) -> SupportsIndex: ... # type: ignore[override] + def __pow__(self, exponent, modulus: Incomplete | None = None) -> _IntegralLike: ... # type: ignore[override] @abstractmethod - def __lshift__(self, other) -> SupportsIndex: ... + def __lshift__(self, other) -> _IntegralLike: ... @abstractmethod - def __rlshift__(self, other) -> SupportsIndex: ... + def __rlshift__(self, other) -> _IntegralLike: ... @abstractmethod - def __rshift__(self, other) -> SupportsIndex: ... + def __rshift__(self, other) -> _IntegralLike: ... @abstractmethod - def __rrshift__(self, other) -> SupportsIndex: ... + def __rrshift__(self, other) -> _IntegralLike: ... @abstractmethod - def __and__(self, other) -> SupportsIndex: ... + def __and__(self, other) -> _IntegralLike: ... @abstractmethod - def __rand__(self, other) -> SupportsIndex: ... + def __rand__(self, other) -> _IntegralLike: ... @abstractmethod - def __xor__(self, other) -> SupportsIndex: ... + def __xor__(self, other) -> _IntegralLike: ... @abstractmethod - def __rxor__(self, other) -> SupportsIndex: ... + def __rxor__(self, other) -> _IntegralLike: ... @abstractmethod - def __or__(self, other) -> SupportsIndex: ... + def __or__(self, other) -> _IntegralLike: ... @abstractmethod - def __ror__(self, other) -> SupportsIndex: ... + def __ror__(self, other) -> _IntegralLike: ... @abstractmethod - def __invert__(self) -> SupportsIndex: ... + def __invert__(self) -> _IntegralLike: ... def __float__(self) -> float: ... @property - def numerator(self) -> SupportsIndex: ... + def numerator(self) -> _IntegralLike: ... @property def denominator(self) -> Literal[1]: ... From 0e620c935d78ae1c69fab3c2a320868907fc71f0 Mon Sep 17 00:00:00 2001 From: AlexWaygood Date: Tue, 6 Feb 2024 15:39:46 +0000 Subject: [PATCH 02/10] Also add `_RealLike` --- stdlib/numbers.pyi | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/stdlib/numbers.pyi b/stdlib/numbers.pyi index 1dca039ff7c6..a2c127a319ec 100644 --- a/stdlib/numbers.pyi +++ b/stdlib/numbers.pyi @@ -1,7 +1,7 @@ # Note: these stubs are incomplete. The more complex type # signatures are currently omitted. # -# Use SupportsComplex, SupportsFloat and SupportsIndex for return types in this module +# Use SupportsComplex, _RealLike and _IntegralLike for return types in this module # rather than `numbers.Complex`, `numbers.Real` and `numbers.Integral`, # to avoid an excessive number of `type: ignore`s in subclasses of these ABCs # (since type checkers don't see `complex` as a subtype of `numbers.Complex`, @@ -10,7 +10,7 @@ import sys from _typeshed import Incomplete from abc import ABCMeta, abstractmethod -from typing import Literal, Protocol, SupportsFloat, overload +from typing import Literal, Protocol, overload from typing_extensions import TypeAlias if sys.version_info >= (3, 11): @@ -35,10 +35,10 @@ class Complex(Number): def __bool__(self) -> bool: ... @property @abstractmethod - def real(self) -> SupportsFloat: ... + def real(self) -> _RealLike: ... @property @abstractmethod - def imag(self) -> SupportsFloat: ... + def imag(self) -> _RealLike: ... @abstractmethod def __add__(self, other) -> _SupportsComplex: ... @abstractmethod @@ -62,15 +62,23 @@ class Complex(Number): @abstractmethod def __rpow__(self, base) -> _SupportsComplex: ... @abstractmethod - def __abs__(self) -> SupportsFloat: ... + def __abs__(self) -> _RealLike: ... @abstractmethod def conjugate(self) -> _SupportsComplex: ... @abstractmethod def __eq__(self, other: object) -> bool: ... +# An approximation of numbers.Real +# that works with structural typing +class _RealLike(Protocol): + def __float__(self) -> float: ... + def __trunc__(self) -> _IntegralLike: ... + def __floor__(self) -> _IntegralLike: ... + def __ceil__(self) -> _IntegralLike: ... + # See comment at the top of the file # for why some of these return types are purposefully vague -class Real(Complex, SupportsFloat): +class Real(Complex): @abstractmethod def __float__(self) -> float: ... @abstractmethod @@ -84,27 +92,27 @@ class Real(Complex, SupportsFloat): def __round__(self, ndigits: None = None) -> _IntegralLike: ... @abstractmethod @overload - def __round__(self, ndigits: int) -> SupportsFloat: ... - def __divmod__(self, other) -> tuple[SupportsFloat, SupportsFloat]: ... - def __rdivmod__(self, other) -> tuple[SupportsFloat, SupportsFloat]: ... + def __round__(self, ndigits: int) -> _RealLike: ... + def __divmod__(self, other) -> tuple[_RealLike, _RealLike]: ... + def __rdivmod__(self, other) -> tuple[_RealLike, _RealLike]: ... @abstractmethod - def __floordiv__(self, other) -> SupportsFloat: ... + def __floordiv__(self, other) -> _RealLike: ... @abstractmethod - def __rfloordiv__(self, other) -> SupportsFloat: ... + def __rfloordiv__(self, other) -> _RealLike: ... @abstractmethod - def __mod__(self, other) -> SupportsFloat: ... + def __mod__(self, other) -> _RealLike: ... @abstractmethod - def __rmod__(self, other) -> SupportsFloat: ... + def __rmod__(self, other) -> _RealLike: ... @abstractmethod def __lt__(self, other) -> bool: ... @abstractmethod def __le__(self, other) -> bool: ... def __complex__(self) -> complex: ... @property - def real(self) -> SupportsFloat: ... + def real(self) -> _RealLike: ... @property def imag(self) -> Literal[0]: ... - def conjugate(self) -> SupportsFloat: ... # type: ignore[override] + def conjugate(self) -> _RealLike: ... # type: ignore[override] # See comment at the top of the file # for why some of these return types are purposefully vague @@ -119,10 +127,9 @@ class Rational(Real): # An approximation of numbers.Integral # that works with structural typing -class _IntegralLike(Protocol): +class _IntegralLike(_RealLike, Protocol): def __int__(self) -> int: ... def __index__(self) -> int: ... - def __float__(self) -> float: ... def __invert__(self) -> _IntegralLike: ... # See comment at the top of the file From 3df1761a12c317cc19e90d45704435749910f3e6 Mon Sep 17 00:00:00 2001 From: AlexWaygood Date: Tue, 6 Feb 2024 15:52:59 +0000 Subject: [PATCH 03/10] Add in `_ComplexLike` --- stdlib/numbers.pyi | 49 ++++++++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/stdlib/numbers.pyi b/stdlib/numbers.pyi index a2c127a319ec..1d1f080a8ad7 100644 --- a/stdlib/numbers.pyi +++ b/stdlib/numbers.pyi @@ -13,20 +13,23 @@ from abc import ABCMeta, abstractmethod from typing import Literal, Protocol, overload from typing_extensions import TypeAlias -if sys.version_info >= (3, 11): - from typing import SupportsComplex as _SupportsComplex -else: - # builtins.complex didn't have a __complex__ method on older Pythons - import typing - - _SupportsComplex: TypeAlias = typing.SupportsComplex | complex - __all__ = ["Number", "Complex", "Real", "Rational", "Integral"] class Number(metaclass=ABCMeta): @abstractmethod def __hash__(self) -> int: ... +# _ComplexLike is an approximation of numbers.Complex that works with structural typing +class _SupportsComplex(Protocol): + def __complex__(self) -> complex: ... + def __abs__(self) -> _RealLike: ... + +if sys.version_info >= (3, 11): + _ComplexLike: TypeAlias = _SupportsComplex +else: + # builtins.complex didn't have a __complex__ method on older Pythons + _ComplexLike: TypeAlias = _SupportsComplex | complex + # See comment at the top of the file # for why some of these return types are purposefully vague class Complex(Number): @@ -40,37 +43,41 @@ class Complex(Number): @abstractmethod def imag(self) -> _RealLike: ... @abstractmethod - def __add__(self, other) -> _SupportsComplex: ... + def __add__(self, other) -> _ComplexLike: ... @abstractmethod - def __radd__(self, other) -> _SupportsComplex: ... + def __radd__(self, other) -> _ComplexLike: ... @abstractmethod - def __neg__(self) -> _SupportsComplex: ... + def __neg__(self) -> _ComplexLike: ... @abstractmethod - def __pos__(self) -> _SupportsComplex: ... - def __sub__(self, other) -> _SupportsComplex: ... - def __rsub__(self, other) -> _SupportsComplex: ... + def __pos__(self) -> _ComplexLike: ... + def __sub__(self, other) -> _ComplexLike: ... + def __rsub__(self, other) -> _ComplexLike: ... @abstractmethod - def __mul__(self, other) -> _SupportsComplex: ... + def __mul__(self, other) -> _ComplexLike: ... @abstractmethod - def __rmul__(self, other) -> _SupportsComplex: ... + def __rmul__(self, other) -> _ComplexLike: ... @abstractmethod - def __truediv__(self, other) -> _SupportsComplex: ... + def __truediv__(self, other) -> _ComplexLike: ... @abstractmethod - def __rtruediv__(self, other) -> _SupportsComplex: ... + def __rtruediv__(self, other) -> _ComplexLike: ... @abstractmethod - def __pow__(self, exponent) -> _SupportsComplex: ... + def __pow__(self, exponent) -> _ComplexLike: ... @abstractmethod - def __rpow__(self, base) -> _SupportsComplex: ... + def __rpow__(self, base) -> _ComplexLike: ... @abstractmethod def __abs__(self) -> _RealLike: ... @abstractmethod - def conjugate(self) -> _SupportsComplex: ... + def conjugate(self) -> _ComplexLike: ... @abstractmethod def __eq__(self, other: object) -> bool: ... # An approximation of numbers.Real # that works with structural typing +# NOTE: this can't inherit from _SupportsComplex, +# because not all builtin types that we want to be understood +# as subtypes of _RealLike have a `__complex__` method class _RealLike(Protocol): + def __abs__(self) -> _RealLike: ... def __float__(self) -> float: ... def __trunc__(self) -> _IntegralLike: ... def __floor__(self) -> _IntegralLike: ... From eca43130aeb47580afb02dd6781ad8b0fe3e23f1 Mon Sep 17 00:00:00 2001 From: AlexWaygood Date: Tue, 6 Feb 2024 16:04:24 +0000 Subject: [PATCH 04/10] Cleanup --- stdlib/numbers.pyi | 59 +++++++++++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/stdlib/numbers.pyi b/stdlib/numbers.pyi index 1d1f080a8ad7..5e78e9dcd452 100644 --- a/stdlib/numbers.pyi +++ b/stdlib/numbers.pyi @@ -1,7 +1,7 @@ # Note: these stubs are incomplete. The more complex type # signatures are currently omitted. # -# Use SupportsComplex, _RealLike and _IntegralLike for return types in this module +# Use _ComplexLike, _RealLike and _IntegralLike for return types in this module # rather than `numbers.Complex`, `numbers.Real` and `numbers.Integral`, # to avoid an excessive number of `type: ignore`s in subclasses of these ABCs # (since type checkers don't see `complex` as a subtype of `numbers.Complex`, @@ -15,11 +15,12 @@ from typing_extensions import TypeAlias __all__ = ["Number", "Complex", "Real", "Rational", "Integral"] -class Number(metaclass=ABCMeta): - @abstractmethod - def __hash__(self) -> int: ... +############################ +# Protocols for return types +############################ -# _ComplexLike is an approximation of numbers.Complex that works with structural typing +# `_ComplexLike` is a structural-typing approximation +# of the `Complex` ABC, which is not (and cannot be) a protocol class _SupportsComplex(Protocol): def __complex__(self) -> complex: ... def __abs__(self) -> _RealLike: ... @@ -30,6 +31,35 @@ else: # builtins.complex didn't have a __complex__ method on older Pythons _ComplexLike: TypeAlias = _SupportsComplex | complex +# _RealLike is a structural-typing approximation +# of the `Real` ABC, which is not (and cannot be) a protocol +# +# NOTE: _RealLike can't inherit from _SupportsComplex, +# because not all builtin types that we want to be understood +# as subtypes of _RealLike have a `__complex__` method +# (e.g. `builtins.int.__complex__` does not exist) +class _RealLike(Protocol): + def __abs__(self) -> _RealLike: ... + def __float__(self) -> float: ... + def __trunc__(self) -> _IntegralLike: ... + def __floor__(self) -> _IntegralLike: ... + def __ceil__(self) -> _IntegralLike: ... + +# _IntegralLike is a structural-typing approximation +# of the `Integral` ABC, which is not (and cannot be) a protocol +class _IntegralLike(_RealLike, Protocol): + def __int__(self) -> int: ... + def __index__(self) -> int: ... + def __invert__(self) -> _IntegralLike: ... + +################# +# Module "proper" +################# + +class Number(metaclass=ABCMeta): + @abstractmethod + def __hash__(self) -> int: ... + # See comment at the top of the file # for why some of these return types are purposefully vague class Complex(Number): @@ -71,18 +101,6 @@ class Complex(Number): @abstractmethod def __eq__(self, other: object) -> bool: ... -# An approximation of numbers.Real -# that works with structural typing -# NOTE: this can't inherit from _SupportsComplex, -# because not all builtin types that we want to be understood -# as subtypes of _RealLike have a `__complex__` method -class _RealLike(Protocol): - def __abs__(self) -> _RealLike: ... - def __float__(self) -> float: ... - def __trunc__(self) -> _IntegralLike: ... - def __floor__(self) -> _IntegralLike: ... - def __ceil__(self) -> _IntegralLike: ... - # See comment at the top of the file # for why some of these return types are purposefully vague class Real(Complex): @@ -132,13 +150,6 @@ class Rational(Real): def denominator(self) -> _IntegralLike: ... def __float__(self) -> float: ... -# An approximation of numbers.Integral -# that works with structural typing -class _IntegralLike(_RealLike, Protocol): - def __int__(self) -> int: ... - def __index__(self) -> int: ... - def __invert__(self) -> _IntegralLike: ... - # See comment at the top of the file # for why some of these return types are purposefully vague class Integral(Rational): From b190c000c9f45dd5e1faa1f0dc29e02d0b53f9b7 Mon Sep 17 00:00:00 2001 From: AlexWaygood Date: Tue, 6 Feb 2024 16:27:27 +0000 Subject: [PATCH 05/10] more --- stdlib/numbers.pyi | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/stdlib/numbers.pyi b/stdlib/numbers.pyi index 5e78e9dcd452..9f185b520d2a 100644 --- a/stdlib/numbers.pyi +++ b/stdlib/numbers.pyi @@ -24,6 +24,8 @@ __all__ = ["Number", "Complex", "Real", "Rational", "Integral"] class _SupportsComplex(Protocol): def __complex__(self) -> complex: ... def __abs__(self) -> _RealLike: ... + def __neg__(self) -> _ComplexLike: ... + def __pos__(self) -> _ComplexLike: ... if sys.version_info >= (3, 11): _ComplexLike: TypeAlias = _SupportsComplex @@ -39,6 +41,8 @@ else: # as subtypes of _RealLike have a `__complex__` method # (e.g. `builtins.int.__complex__` does not exist) class _RealLike(Protocol): + def __neg__(self) -> _ComplexLike: ... # TODO: ideally would be more precise + def __pos__(self) -> _ComplexLike: ... # TODO: ideally would be more precise def __abs__(self) -> _RealLike: ... def __float__(self) -> float: ... def __trunc__(self) -> _IntegralLike: ... From eb75374013e64b7ae316f558626e5d3bf3311304 Mon Sep 17 00:00:00 2001 From: AlexWaygood Date: Tue, 6 Feb 2024 16:45:57 +0000 Subject: [PATCH 06/10] more --- stdlib/numbers.pyi | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/stdlib/numbers.pyi b/stdlib/numbers.pyi index 9f185b520d2a..722384b42f15 100644 --- a/stdlib/numbers.pyi +++ b/stdlib/numbers.pyi @@ -55,6 +55,7 @@ class _IntegralLike(_RealLike, Protocol): def __int__(self) -> int: ... def __index__(self) -> int: ... def __invert__(self) -> _IntegralLike: ... + def __abs__(self) -> _IntegralLike: ... ################# # Module "proper" @@ -189,3 +190,7 @@ class Integral(Rational): def numerator(self) -> _IntegralLike: ... @property def denominator(self) -> Literal[1]: ... + # Not actually overridden at runtime, + # but we override it in the stub to give it a more precise return type: + @abstractmethod + def __abs__(self) -> _IntegralLike: ... From 59eb3a435bfd577996e09f7761265dc8367c631c Mon Sep 17 00:00:00 2001 From: AlexWaygood Date: Wed, 7 Feb 2024 10:23:34 +0000 Subject: [PATCH 07/10] Further improve return types in the `numbers` module [alt] --- stdlib/numbers.pyi | 46 +++++++++++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/stdlib/numbers.pyi b/stdlib/numbers.pyi index 722384b42f15..a6d67397c9ba 100644 --- a/stdlib/numbers.pyi +++ b/stdlib/numbers.pyi @@ -7,11 +7,9 @@ # (since type checkers don't see `complex` as a subtype of `numbers.Complex`, # nor `float` as a subtype of `numbers.Real`, etc.) -import sys from _typeshed import Incomplete from abc import ABCMeta, abstractmethod from typing import Literal, Protocol, overload -from typing_extensions import TypeAlias __all__ = ["Number", "Complex", "Real", "Rational", "Integral"] @@ -21,28 +19,20 @@ __all__ = ["Number", "Complex", "Real", "Rational", "Integral"] # `_ComplexLike` is a structural-typing approximation # of the `Complex` ABC, which is not (and cannot be) a protocol -class _SupportsComplex(Protocol): - def __complex__(self) -> complex: ... +# +# NOTE: We can't include `__complex__` here, +# as we want `int` to be seen as a subtype of `_ComplexLike`, +# and `int.__complex__` does not exist :( +class _ComplexLike(Protocol): def __abs__(self) -> _RealLike: ... def __neg__(self) -> _ComplexLike: ... def __pos__(self) -> _ComplexLike: ... -if sys.version_info >= (3, 11): - _ComplexLike: TypeAlias = _SupportsComplex -else: - # builtins.complex didn't have a __complex__ method on older Pythons - _ComplexLike: TypeAlias = _SupportsComplex | complex - # _RealLike is a structural-typing approximation # of the `Real` ABC, which is not (and cannot be) a protocol -# -# NOTE: _RealLike can't inherit from _SupportsComplex, -# because not all builtin types that we want to be understood -# as subtypes of _RealLike have a `__complex__` method -# (e.g. `builtins.int.__complex__` does not exist) -class _RealLike(Protocol): - def __neg__(self) -> _ComplexLike: ... # TODO: ideally would be more precise - def __pos__(self) -> _ComplexLike: ... # TODO: ideally would be more precise +class _RealLike(_ComplexLike, Protocol): + def __neg__(self) -> _RealLike: ... + def __pos__(self) -> _RealLike: ... def __abs__(self) -> _RealLike: ... def __float__(self) -> float: ... def __trunc__(self) -> _IntegralLike: ... @@ -52,6 +42,8 @@ class _RealLike(Protocol): # _IntegralLike is a structural-typing approximation # of the `Integral` ABC, which is not (and cannot be) a protocol class _IntegralLike(_RealLike, Protocol): + def __neg__(self) -> _IntegralLike: ... + def __pos__(self) -> _IntegralLike: ... def __int__(self) -> int: ... def __index__(self) -> int: ... def __invert__(self) -> _IntegralLike: ... @@ -143,6 +135,12 @@ class Real(Complex): @property def imag(self) -> Literal[0]: ... def conjugate(self) -> _RealLike: ... # type: ignore[override] + # Not actually overridden at runtime, + # but we override these in the stub to give them more precise return types: + @abstractmethod + def __pos__(self) -> _RealLike: ... + @abstractmethod + def __neg__(self) -> _RealLike: ... # See comment at the top of the file # for why some of these return types are purposefully vague @@ -191,6 +189,16 @@ class Integral(Rational): @property def denominator(self) -> Literal[1]: ... # Not actually overridden at runtime, - # but we override it in the stub to give it a more precise return type: + # but we override these in the stub to give them more precise return types: + @abstractmethod + def __pos__(self) -> _IntegralLike: ... + @abstractmethod + def __neg__(self) -> _IntegralLike: ... @abstractmethod def __abs__(self) -> _IntegralLike: ... + @abstractmethod + @overload + def __round__(self, ndigits: None = None) -> _IntegralLike: ... + @abstractmethod + @overload + def __round__(self, ndigits: int) -> _IntegralLike: ... From 4195ac31dce6f930c5135bee5e2e2355f440c928 Mon Sep 17 00:00:00 2001 From: AlexWaygood Date: Wed, 7 Feb 2024 10:34:06 +0000 Subject: [PATCH 08/10] cleanup --- stdlib/numbers.pyi | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/stdlib/numbers.pyi b/stdlib/numbers.pyi index a6d67397c9ba..5e58c8d6d719 100644 --- a/stdlib/numbers.pyi +++ b/stdlib/numbers.pyi @@ -24,30 +24,35 @@ __all__ = ["Number", "Complex", "Real", "Rational", "Integral"] # as we want `int` to be seen as a subtype of `_ComplexLike`, # and `int.__complex__` does not exist :( class _ComplexLike(Protocol): - def __abs__(self) -> _RealLike: ... def __neg__(self) -> _ComplexLike: ... def __pos__(self) -> _ComplexLike: ... + def __abs__(self) -> _RealLike: ... # _RealLike is a structural-typing approximation # of the `Real` ABC, which is not (and cannot be) a protocol class _RealLike(_ComplexLike, Protocol): - def __neg__(self) -> _RealLike: ... - def __pos__(self) -> _RealLike: ... - def __abs__(self) -> _RealLike: ... - def __float__(self) -> float: ... def __trunc__(self) -> _IntegralLike: ... def __floor__(self) -> _IntegralLike: ... def __ceil__(self) -> _IntegralLike: ... + def __float__(self) -> float: ... + # Overridden from `_ComplexLike` + # for a more precise return type: + def __neg__(self) -> _RealLike: ... + def __pos__(self) -> _RealLike: ... # _IntegralLike is a structural-typing approximation # of the `Integral` ABC, which is not (and cannot be) a protocol class _IntegralLike(_RealLike, Protocol): - def __neg__(self) -> _IntegralLike: ... - def __pos__(self) -> _IntegralLike: ... + def __invert__(self) -> _IntegralLike: ... def __int__(self) -> int: ... def __index__(self) -> int: ... - def __invert__(self) -> _IntegralLike: ... + # Overridden from `_ComplexLike` + # for a more precise return type: def __abs__(self) -> _IntegralLike: ... + # Overridden from `RealLike` + # for a more precise return type: + def __neg__(self) -> _IntegralLike: ... + def __pos__(self) -> _IntegralLike: ... ################# # Module "proper" From 28f52d27f7d6af2d4958b2d023371418ff2b981b Mon Sep 17 00:00:00 2001 From: AlexWaygood Date: Wed, 7 Feb 2024 10:36:54 +0000 Subject: [PATCH 09/10] Remove type ignores --- stdlib/numbers.pyi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/numbers.pyi b/stdlib/numbers.pyi index 5e58c8d6d719..dc3d4e1e12eb 100644 --- a/stdlib/numbers.pyi +++ b/stdlib/numbers.pyi @@ -139,7 +139,7 @@ class Real(Complex): def real(self) -> _RealLike: ... @property def imag(self) -> Literal[0]: ... - def conjugate(self) -> _RealLike: ... # type: ignore[override] + def conjugate(self) -> _RealLike: ... # Not actually overridden at runtime, # but we override these in the stub to give them more precise return types: @abstractmethod @@ -165,7 +165,7 @@ class Integral(Rational): def __int__(self) -> int: ... def __index__(self) -> int: ... @abstractmethod - def __pow__(self, exponent, modulus: Incomplete | None = None) -> _IntegralLike: ... # type: ignore[override] + def __pow__(self, exponent, modulus: Incomplete | None = None) -> _IntegralLike: ... @abstractmethod def __lshift__(self, other) -> _IntegralLike: ... @abstractmethod From 921c16c81efcfaf5791cdc311dcf17de7882c70c Mon Sep 17 00:00:00 2001 From: AlexWaygood Date: Wed, 7 Feb 2024 11:21:07 +0000 Subject: [PATCH 10/10] Ensure the ABCs are understood as subtypes of the protocols --- stdlib/numbers.pyi | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stdlib/numbers.pyi b/stdlib/numbers.pyi index dc3d4e1e12eb..e129de2cdc67 100644 --- a/stdlib/numbers.pyi +++ b/stdlib/numbers.pyi @@ -64,7 +64,7 @@ class Number(metaclass=ABCMeta): # See comment at the top of the file # for why some of these return types are purposefully vague -class Complex(Number): +class Complex(Number, _ComplexLike): @abstractmethod def __complex__(self) -> complex: ... def __bool__(self) -> bool: ... @@ -105,7 +105,7 @@ class Complex(Number): # See comment at the top of the file # for why some of these return types are purposefully vague -class Real(Complex): +class Real(Complex, _RealLike): @abstractmethod def __float__(self) -> float: ... @abstractmethod @@ -160,7 +160,7 @@ class Rational(Real): # See comment at the top of the file # for why some of these return types are purposefully vague -class Integral(Rational): +class Integral(Rational, _IntegralLike): @abstractmethod def __int__(self) -> int: ... def __index__(self) -> int: ...