From 8cc20e60e14e1d5f634e20b692a0064776bd232a Mon Sep 17 00:00:00 2001 From: AlexWaygood Date: Thu, 6 Apr 2023 11:31:54 +0100 Subject: [PATCH] gh-74690: typing._ProtocolMeta.__instancecheck__: Exit early for protocols that only have callable members --- Lib/test/test_typing.py | 14 ++++++++++++++ Lib/typing.py | 6 +++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 7d2e6a6a9f6287..82fff86e423529 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -2577,6 +2577,18 @@ def meth(x): ... class PG(Protocol[T]): def meth(x): ... + @runtime_checkable + class WeirdProto(Protocol): + meth = str.maketrans + + class CustomCallable: + def __call__(self, *args, **kwargs): + pass + + @runtime_checkable + class WeirderProto(Protocol): + meth = CustomCallable() + class BadP(Protocol): def meth(x): ... @@ -2588,6 +2600,8 @@ def meth(x): ... self.assertIsInstance(C(), P) self.assertIsInstance(C(), PG) + self.assertIsInstance(C(), WeirdProto) + self.assertIsInstance(C(), WeirderProto) with self.assertRaises(TypeError): isinstance(C(), PG[T]) with self.assertRaises(TypeError): diff --git a/Lib/typing.py b/Lib/typing.py index 1f1c4ffa2566ab..133c72cfbcdba7 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -2032,7 +2032,11 @@ def __instancecheck__(cls, instance): if super().__instancecheck__(instance): return True - if is_protocol_cls: + # Skip the below loop for protocols where `cls.__callable_proto_members_only__ == True`. + # For these protocols, this just duplicates checks that have already been done + # in the `_proto_hook` function, + # which is called as part of the super().__instancecheck__ method above. + if is_protocol_cls and not cls.__callable_proto_members_only__: getattr_static = _lazy_load_getattr_static() for attr in cls.__protocol_attrs__: try: