Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add faster versions of various runtime-checkable protocols #146

Merged
merged 4 commits into from
Apr 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
5 changes: 3 additions & 2 deletions src/test_typing_extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
69 changes: 69 additions & 0 deletions src/typing_extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,13 @@
'TypedDict',

# Structural checks, a.k.a. protocols.
'SupportsAbs',
'SupportsBytes',
'SupportsComplex',
'SupportsFloat',
'SupportsIndex',
'SupportsInt',
'SupportsRound',

# One-off things.
'Annotated',
Expand Down Expand Up @@ -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__ = ()
Expand All @@ -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
Expand Down