From 5a8cdc5dbb5ceda9930f6db67d4f32af2b743371 Mon Sep 17 00:00:00 2001 From: AlexWaygood Date: Thu, 18 May 2023 21:02:32 +0100 Subject: [PATCH] Don't set special protocol attributes on non-protocol subclasses of protocols --- Lib/test/test_typing.py | 15 +++++++++++++++ Lib/typing.py | 13 +++++++------ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index bf038bf143a6c8..450c85967dd75a 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -3167,6 +3167,21 @@ def meth(self): pass self.assertIsInstance(NonPR(), PR) self.assertIsSubclass(NonPR, PR) + self.assertNotIn("__protocol_attrs__", vars(NonP)) + self.assertNotIn("__protocol_attrs__", vars(NonPR)) + self.assertNotIn("__callable_proto_members_only__", vars(NonP)) + self.assertNotIn("__callable_proto_members_only__", vars(NonPR)) + + acceptable_extra_attrs = { + '_is_protocol', '_is_runtime_protocol', '__parameters__', + '__subclasshook__', '__abstractmethods__', '_abc_impl', + '__init__', '__annotations__', + } + self.assertLessEqual(vars(NonP).keys(), vars(C).keys() | acceptable_extra_attrs) + self.assertLessEqual( + vars(NonPR).keys(), vars(D).keys() | acceptable_extra_attrs + ) + def test_custom_subclasshook(self): class P(Protocol): x = 1 diff --git a/Lib/typing.py b/Lib/typing.py index 91b5fe5b87e669..b60eb94351f9bb 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -1779,12 +1779,13 @@ class _ProtocolMeta(ABCMeta): # but is necessary for several reasons... def __init__(cls, *args, **kwargs): super().__init__(*args, **kwargs) - cls.__protocol_attrs__ = _get_protocol_attrs(cls) - # PEP 544 prohibits using issubclass() - # with protocols that have non-method members. - cls.__callable_proto_members_only__ = all( - callable(getattr(cls, attr, None)) for attr in cls.__protocol_attrs__ - ) + if getattr(cls, "_is_protocol", False): + cls.__protocol_attrs__ = _get_protocol_attrs(cls) + # PEP 544 prohibits using issubclass() + # with protocols that have non-method members. + cls.__callable_proto_members_only__ = all( + callable(getattr(cls, attr, None)) for attr in cls.__protocol_attrs__ + ) def __subclasscheck__(cls, other): if (