Skip to content

Commit d3719ac

Browse files
authored
Add faster versions of various runtime-checkable protocols (#146)
1 parent 48b6855 commit d3719ac

File tree

3 files changed

+77
-2
lines changed

3 files changed

+77
-2
lines changed

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@
4949
Patch by Alex Waygood.
5050
- Speedup `isinstance(3, typing_extensions.SupportsIndex)` by >10x on Python
5151
<3.12. Patch by Alex Waygood.
52+
- Add `typing_extensions` versions of `SupportsInt`, `SupportsFloat`,
53+
`SupportsComplex`, `SupportsBytes`, `SupportsAbs` and `SupportsRound`. These
54+
have the same semantics as the versions from the `typing` module, but
55+
`isinstance()` checks against the `typing_extensions` versions are >10x faster
56+
at runtime on Python <3.12. Patch by Alex Waygood.
5257
- Add `__orig_bases__` to non-generic TypedDicts, call-based TypedDicts, and
5358
call-based NamedTuples. Other TypedDicts and NamedTuples already had the attribute.
5459
Patch by Adrian Garcia Badaracco.

src/test_typing_extensions.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -3852,8 +3852,9 @@ def test_typing_extensions_defers_when_possible(self):
38523852
exclude |= {'final', 'Any'}
38533853
if sys.version_info < (3, 12):
38543854
exclude |= {
3855-
'Protocol', 'runtime_checkable', 'SupportsIndex', 'TypedDict',
3856-
'is_typeddict', 'NamedTuple',
3855+
'Protocol', 'runtime_checkable', 'SupportsAbs', 'SupportsBytes',
3856+
'SupportsComplex', 'SupportsFloat', 'SupportsIndex', 'SupportsInt',
3857+
'SupportsRound', 'TypedDict', 'is_typeddict', 'NamedTuple',
38573858
}
38583859
for item in typing_extensions.__all__:
38593860
if item not in exclude and hasattr(typing, item):

src/typing_extensions.py

+69
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,13 @@
4646
'TypedDict',
4747

4848
# Structural checks, a.k.a. protocols.
49+
'SupportsAbs',
50+
'SupportsBytes',
51+
'SupportsComplex',
52+
'SupportsFloat',
4953
'SupportsIndex',
54+
'SupportsInt',
55+
'SupportsRound',
5056

5157
# One-off things.
5258
'Annotated',
@@ -739,8 +745,49 @@ def runtime_checkable(cls):
739745

740746
# Our version of runtime-checkable protocols is faster on Python 3.7-3.11
741747
if sys.version_info >= (3, 12):
748+
SupportsInt = typing.SupportsInt
749+
SupportsFloat = typing.SupportsFloat
750+
SupportsComplex = typing.SupportsComplex
742751
SupportsIndex = typing.SupportsIndex
752+
SupportsAbs = typing.SupportsAbs
753+
SupportsRound = typing.SupportsRound
743754
else:
755+
@runtime_checkable
756+
class SupportsInt(Protocol):
757+
"""An ABC with one abstract method __int__."""
758+
__slots__ = ()
759+
760+
@abc.abstractmethod
761+
def __int__(self) -> int:
762+
pass
763+
764+
@runtime_checkable
765+
class SupportsFloat(Protocol):
766+
"""An ABC with one abstract method __float__."""
767+
__slots__ = ()
768+
769+
@abc.abstractmethod
770+
def __float__(self) -> float:
771+
pass
772+
773+
@runtime_checkable
774+
class SupportsComplex(Protocol):
775+
"""An ABC with one abstract method __complex__."""
776+
__slots__ = ()
777+
778+
@abc.abstractmethod
779+
def __complex__(self) -> complex:
780+
pass
781+
782+
@runtime_checkable
783+
class SupportsBytes(Protocol):
784+
"""An ABC with one abstract method __bytes__."""
785+
__slots__ = ()
786+
787+
@abc.abstractmethod
788+
def __bytes__(self) -> bytes:
789+
pass
790+
744791
@runtime_checkable
745792
class SupportsIndex(Protocol):
746793
__slots__ = ()
@@ -749,6 +796,28 @@ class SupportsIndex(Protocol):
749796
def __index__(self) -> int:
750797
pass
751798

799+
@runtime_checkable
800+
class SupportsAbs(Protocol[T_co]):
801+
"""
802+
An ABC with one abstract method __abs__ that is covariant in its return type.
803+
"""
804+
__slots__ = ()
805+
806+
@abc.abstractmethod
807+
def __abs__(self) -> T_co:
808+
pass
809+
810+
@runtime_checkable
811+
class SupportsRound(Protocol[T_co]):
812+
"""
813+
An ABC with one abstract method __round__ that is covariant in its return type.
814+
"""
815+
__slots__ = ()
816+
817+
@abc.abstractmethod
818+
def __round__(self, ndigits: int = 0) -> T_co:
819+
pass
820+
752821

753822
if sys.version_info >= (3, 12):
754823
# The standard library TypedDict in Python 3.8 does not store runtime information

0 commit comments

Comments
 (0)