diff --git a/CHANGELOG.md b/CHANGELOG.md index 43838db0..16775856 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ signature of `typing.NewType.__call__`. Patch by Alex Waygood. - `typing.deprecated` now gives a better error message if you pass a non-`str` argument to the `msg` parameter. Patch by Alex Waygood. +- Exclude `__match_args__` from `Protocol` members, + this is a backport of https://github.com/python/cpython/pull/110683 # Release 4.8.0 (September 17, 2023) diff --git a/src/test_typing_extensions.py b/src/test_typing_extensions.py index 151d44c6..ffc84266 100644 --- a/src/test_typing_extensions.py +++ b/src/test_typing_extensions.py @@ -2521,6 +2521,39 @@ class Bad: pass self.assertNotIsInstance(Other(), Concrete) self.assertIsInstance(NT(1, 2), Position) + def test_runtime_checkable_with_match_args(self): + @runtime_checkable + class P_regular(Protocol): + x: int + y: int + + @runtime_checkable + class P_match(Protocol): + __match_args__ = ("x", "y") + x: int + y: int + + class Regular: + def __init__(self, x: int, y: int): + self.x = x + self.y = y + + class WithMatch: + __match_args__ = ("x", "y", "z") + def __init__(self, x: int, y: int, z: int): + self.x = x + self.y = y + self.z = z + + class Nope: ... + + self.assertIsInstance(Regular(1, 2), P_regular) + self.assertIsInstance(Regular(1, 2), P_match) + self.assertIsInstance(WithMatch(1, 2, 3), P_regular) + self.assertIsInstance(WithMatch(1, 2, 3), P_match) + self.assertNotIsInstance(Nope(), P_regular) + self.assertNotIsInstance(Nope(), P_match) + def test_protocols_isinstance_init(self): T = TypeVar('T') @runtime_checkable @@ -5062,12 +5095,12 @@ def test_typing_extensions_defers_when_possible(self): exclude |= {'final', 'Any', 'NewType'} if sys.version_info < (3, 12): exclude |= { - 'Protocol', 'SupportsAbs', 'SupportsBytes', + 'SupportsAbs', 'SupportsBytes', 'SupportsComplex', 'SupportsFloat', 'SupportsIndex', 'SupportsInt', 'SupportsRound', 'Unpack', } if sys.version_info < (3, 13): - exclude |= {'NamedTuple', 'TypedDict', 'is_typeddict'} + exclude |= {'NamedTuple', 'Protocol', 'TypedDict', 'is_typeddict'} for item in typing_extensions.__all__: if item not in exclude and hasattr(typing, item): self.assertIs( diff --git a/src/typing_extensions.py b/src/typing_extensions.py index 34affa97..78ae1635 100644 --- a/src/typing_extensions.py +++ b/src/typing_extensions.py @@ -473,6 +473,7 @@ def clear_overloads(): "__orig_bases__", "__module__", "_MutableMapping__marker", "__doc__", "__subclasshook__", "__orig_class__", "__init__", "__new__", "__protocol_attrs__", "__callable_proto_members_only__", + "__match_args__", } if sys.version_info >= (3, 9): @@ -503,9 +504,9 @@ def _caller(depth=2): return None -# The performance of runtime-checkable protocols is significantly improved on Python 3.12, -# so we backport the 3.12 version of Protocol to Python <=3.11 -if sys.version_info >= (3, 12): +# `__match_args__` attribute was removed from protocol members in 3.13, +# we want to backport this change to older Python versions. +if sys.version_info >= (3, 13): Protocol = typing.Protocol else: def _allow_reckless_class_checks(depth=3):