diff --git a/CHANGELOG.md b/CHANGELOG.md index a95f31ed..099fbbe9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,6 +49,11 @@ Patch by Alex Waygood. - Speedup `isinstance(3, typing_extensions.SupportsIndex)` by >10x on Python <3.12. Patch by Alex Waygood. +- Add `typing_extensions` versions of `SupportsInt`, `SupportsFloat`, + `SupportsComplex`, `SupportsBytes`, `SupportsAbs` and `SupportsRound`. These + have the same semantics as the versions from the `typing` module, but + `isinstance()` checks against the `typing_extensions` versions are >10x faster + at runtime on Python <3.12. Patch by Alex Waygood. - Add `__orig_bases__` to non-generic TypedDicts, call-based TypedDicts, and call-based NamedTuples. Other TypedDicts and NamedTuples already had the attribute. Patch by Adrian Garcia Badaracco. diff --git a/src/test_typing_extensions.py b/src/test_typing_extensions.py index 77171100..37824cf9 100644 --- a/src/test_typing_extensions.py +++ b/src/test_typing_extensions.py @@ -3852,8 +3852,9 @@ def test_typing_extensions_defers_when_possible(self): exclude |= {'final', 'Any'} if sys.version_info < (3, 12): exclude |= { - 'Protocol', 'runtime_checkable', 'SupportsIndex', 'TypedDict', - 'is_typeddict', 'NamedTuple', + 'Protocol', 'runtime_checkable', 'SupportsAbs', 'SupportsBytes', + 'SupportsComplex', 'SupportsFloat', 'SupportsIndex', 'SupportsInt', + 'SupportsRound', 'TypedDict', 'is_typeddict', 'NamedTuple', } for item in typing_extensions.__all__: if item not in exclude and hasattr(typing, item): diff --git a/src/typing_extensions.py b/src/typing_extensions.py index 411ccd42..534eecbb 100644 --- a/src/typing_extensions.py +++ b/src/typing_extensions.py @@ -46,7 +46,13 @@ 'TypedDict', # Structural checks, a.k.a. protocols. + 'SupportsAbs', + 'SupportsBytes', + 'SupportsComplex', + 'SupportsFloat', 'SupportsIndex', + 'SupportsInt', + 'SupportsRound', # One-off things. 'Annotated', @@ -738,8 +744,49 @@ def runtime_checkable(cls): # Our version of runtime-checkable protocols is faster on Python 3.7-3.11 if sys.version_info >= (3, 12): + SupportsInt = typing.SupportsInt + SupportsFloat = typing.SupportsFloat + SupportsComplex = typing.SupportsComplex SupportsIndex = typing.SupportsIndex + SupportsAbs = typing.SupportsAbs + SupportsRound = typing.SupportsRound else: + @runtime_checkable + class SupportsInt(Protocol): + """An ABC with one abstract method __int__.""" + __slots__ = () + + @abc.abstractmethod + def __int__(self) -> int: + pass + + @runtime_checkable + class SupportsFloat(Protocol): + """An ABC with one abstract method __float__.""" + __slots__ = () + + @abc.abstractmethod + def __float__(self) -> float: + pass + + @runtime_checkable + class SupportsComplex(Protocol): + """An ABC with one abstract method __complex__.""" + __slots__ = () + + @abc.abstractmethod + def __complex__(self) -> complex: + pass + + @runtime_checkable + class SupportsBytes(Protocol): + """An ABC with one abstract method __bytes__.""" + __slots__ = () + + @abc.abstractmethod + def __bytes__(self) -> bytes: + pass + @runtime_checkable class SupportsIndex(Protocol): __slots__ = () @@ -748,6 +795,28 @@ class SupportsIndex(Protocol): def __index__(self) -> int: pass + @runtime_checkable + class SupportsAbs(Protocol[T_co]): + """ + An ABC with one abstract method __abs__ that is covariant in its return type. + """ + __slots__ = () + + @abc.abstractmethod + def __abs__(self) -> T_co: + pass + + @runtime_checkable + class SupportsRound(Protocol[T_co]): + """ + An ABC with one abstract method __round__ that is covariant in its return type. + """ + __slots__ = () + + @abc.abstractmethod + def __round__(self, ndigits: int = 0) -> T_co: + pass + if sys.version_info >= (3, 12): # The standard library TypedDict in Python 3.8 does not store runtime information