Add faster versions of various runtime-checkable protocols (#146)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b8d8b62..f117f39 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 7f3c0ef..4a5d3a1 100644
--- a/src/test_typing_extensions.py
+++ b/src/test_typing_extensions.py
@@ -3852,8 +3852,9 @@
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 2ee36eb..cce31f8 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',
@@ -739,9 +745,50 @@
# 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__ = ()
@@ -749,6 +796,28 @@
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