summaryrefslogtreecommitdiffstats
path: root/Lib/typing.py
diff options
context:
space:
mode:
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>2023-06-23 15:26:37 (GMT)
committerGitHub <noreply@github.com>2023-06-23 15:26:37 (GMT)
commit7d6ee298e96559e13d343b47ac489fcb4f219cd4 (patch)
tree4052abfbf39dec2a3666f2a3841efbcd95eb5051 /Lib/typing.py
parent1ffcd49be2a090375a77b3ad9dc5ccd8bfc2944f (diff)
downloadcpython-7d6ee298e96559e13d343b47ac489fcb4f219cd4.zip
cpython-7d6ee298e96559e13d343b47ac489fcb4f219cd4.tar.gz
cpython-7d6ee298e96559e13d343b47ac489fcb4f219cd4.tar.bz2
[3.12] gh-105974: Revert unintentional behaviour change for protocols with non-callable members and custom `__subclasshook__` methods (GH-105976) (#106032)
gh-105974: Revert unintentional behaviour change for protocols with non-callable members and custom `__subclasshook__` methods (GH-105976) (cherry picked from commit 9499b0f138cc53b9a2590350d0b545d2f69ee126) Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
Diffstat (limited to 'Lib/typing.py')
-rw-r--r--Lib/typing.py65
1 files changed, 33 insertions, 32 deletions
diff --git a/Lib/typing.py b/Lib/typing.py
index 0b1d968..9e2adbe 100644
--- a/Lib/typing.py
+++ b/Lib/typing.py
@@ -1822,14 +1822,17 @@ class _ProtocolMeta(ABCMeta):
def __subclasscheck__(cls, other):
if cls is Protocol:
return type.__subclasscheck__(cls, other)
- if not isinstance(other, type):
- # Same error message as for issubclass(1, int).
- raise TypeError('issubclass() arg 1 must be a class')
if (
getattr(cls, '_is_protocol', False)
and not _allow_reckless_class_checks()
):
- if not cls.__callable_proto_members_only__:
+ if not isinstance(other, type):
+ # Same error message as for issubclass(1, int).
+ raise TypeError('issubclass() arg 1 must be a class')
+ if (
+ not cls.__callable_proto_members_only__
+ and cls.__dict__.get("__subclasshook__") is _proto_hook
+ ):
raise TypeError(
"Protocols with non-method members don't support issubclass()"
)
@@ -1873,6 +1876,30 @@ class _ProtocolMeta(ABCMeta):
return False
+@classmethod
+def _proto_hook(cls, other):
+ if not cls.__dict__.get('_is_protocol', False):
+ return NotImplemented
+
+ 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__:
+ if base.__dict__[attr] is None:
+ return NotImplemented
+ break
+
+ # ...or in annotations, if it is a sub-protocol.
+ annotations = getattr(base, '__annotations__', {})
+ if (isinstance(annotations, collections.abc.Mapping) and
+ attr in annotations and
+ issubclass(other, Generic) and getattr(other, '_is_protocol', False)):
+ break
+ else:
+ return NotImplemented
+ return True
+
+
class Protocol(Generic, metaclass=_ProtocolMeta):
"""Base class for protocol classes.
@@ -1918,37 +1945,11 @@ class Protocol(Generic, metaclass=_ProtocolMeta):
cls._is_protocol = any(b is Protocol for b in cls.__bases__)
# Set (or override) the protocol subclass hook.
- def _proto_hook(other):
- if not cls.__dict__.get('_is_protocol', False):
- return NotImplemented
-
- 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__:
- if base.__dict__[attr] is None:
- return NotImplemented
- break
-
- # ...or in annotations, if it is a sub-protocol.
- annotations = getattr(base, '__annotations__', {})
- if (isinstance(annotations, collections.abc.Mapping) and
- attr in annotations and
- issubclass(other, Generic) and getattr(other, '_is_protocol', False)):
- break
- else:
- return NotImplemented
- return True
-
if '__subclasshook__' not in cls.__dict__:
cls.__subclasshook__ = _proto_hook
- # We have nothing more to do for non-protocols...
- if not cls._is_protocol:
- return
-
- # ... otherwise prohibit instantiation.
- if cls.__init__ is Protocol.__init__:
+ # Prohibit instantiation for protocol classes
+ if cls._is_protocol and cls.__init__ is Protocol.__init__:
cls.__init__ = _no_init_or_replace_init