diff options
author | Miss Islington (bot) <31488909+miss-islington@users.noreply.github.com> | 2023-06-23 15:26:37 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-06-23 15:26:37 (GMT) |
commit | 7d6ee298e96559e13d343b47ac489fcb4f219cd4 (patch) | |
tree | 4052abfbf39dec2a3666f2a3841efbcd95eb5051 /Lib/typing.py | |
parent | 1ffcd49be2a090375a77b3ad9dc5ccd8bfc2944f (diff) | |
download | cpython-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.py | 65 |
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 |