]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-74690: typing: Call `_get_protocol_attrs` and `_callable_members_only` at protocol...
authorAlex Waygood <Alex.Waygood@Gmail.com>
Wed, 5 Apr 2023 11:19:03 +0000 (12:19 +0100)
committerGitHub <noreply@github.com>
Wed, 5 Apr 2023 11:19:03 +0000 (12:19 +0100)
Lib/typing.py

index f50e9b0a93b061a6d16dd0f61162360b7ef0babb..b8420f619a1d05055809aecad6a014feb44df9a4 100644 (file)
@@ -1905,7 +1905,8 @@ class _TypingEllipsis:
 
 _TYPING_INTERNALS = frozenset({
     '__parameters__', '__orig_bases__',  '__orig_class__',
-    '_is_protocol', '_is_runtime_protocol'
+    '_is_protocol', '_is_runtime_protocol', '__protocol_attrs__',
+    '__callable_proto_members_only__',
 })
 
 _SPECIAL_NAMES = frozenset({
@@ -1935,11 +1936,6 @@ def _get_protocol_attrs(cls):
     return attrs
 
 
-def _is_callable_members_only(cls, protocol_attrs):
-    # PEP 544 prohibits using issubclass() with protocols that have non-method members.
-    return all(callable(getattr(cls, attr, None)) for attr in protocol_attrs)
-
-
 def _no_init_or_replace_init(self, *args, **kwargs):
     cls = type(self)
 
@@ -2012,6 +2008,15 @@ _cleanups.append(_lazy_load_getattr_static.cache_clear)
 class _ProtocolMeta(ABCMeta):
     # This metaclass is really unfortunate and exists only because of
     # the lack of __instancehook__.
+    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__
+        )
+
     def __instancecheck__(cls, instance):
         # We need this method for situations where attributes are
         # assigned in __init__.
@@ -2029,7 +2034,7 @@ class _ProtocolMeta(ABCMeta):
 
         if is_protocol_cls:
             getattr_static = _lazy_load_getattr_static()
-            for attr in _get_protocol_attrs(cls):
+            for attr in cls.__protocol_attrs__:
                 try:
                     val = getattr_static(instance, attr)
                 except AttributeError:
@@ -2095,9 +2100,7 @@ class Protocol(Generic, metaclass=_ProtocolMeta):
                 raise TypeError("Instance and class checks can only be used with"
                                 " @runtime_checkable protocols")
 
-            protocol_attrs = _get_protocol_attrs(cls)
-
-            if not _is_callable_members_only(cls, protocol_attrs):
+            if not cls.__callable_proto_members_only__ :
                 if _allow_reckless_class_checks():
                     return NotImplemented
                 raise TypeError("Protocols with non-method members"
@@ -2107,7 +2110,7 @@ class Protocol(Generic, metaclass=_ProtocolMeta):
                 raise TypeError('issubclass() arg 1 must be a class')
 
             # Second, perform the actual structural compatibility check.
-            for attr in protocol_attrs:
+            for attr in cls.__protocol_attrs__:
                 for base in other.__mro__:
                     # Check if the members appears in the class dictionary...
                     if attr in base.__dict__: