8000 gh-74690: typing: Call `_get_protocol_attrs` and `_callable_members_only` at protocol class creation time, not during `isinstance()` checks by AlexWaygood · Pull Request #103160 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

gh-74690: typing: Call _get_protocol_attrs and _callable_members_only at protocol class creation time, not during isinstance() checks #103160

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Apr 5, 2023
Prev Previous commit
Next Next commit
Move to class-creation time
  • Loading branch information
AlexWaygood committed Apr 1, 2023
commit a34f7005abc35f7da745e45aed3043d9c273837a
24 changes: 11 additions & 13 deletions Lib/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down Expand Up @@ -1996,14 +1997,15 @@ def _allow_reckless_class_checks(depth=3):
class _ProtocolMeta(ABCMeta):
# This metaclass is really unfortunate and exists only because of
# the lack of __instancehook__.
def __lazy_protocol_init_subclass__(cls):
if not hasattr(cls, "__protocol_attrs__"):
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 __new__(metacls, name, bases, namespace, **kwargs):
cls = super().__new__(metacls, name, bases, namespace, **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__
)
return cls

def __instancecheck__(cls, instance):
# We need this method for situations where attributes are
Expand All @@ -2020,8 +2022,6 @@ def __instancecheck__(cls, instance):
if not is_protocol_cls and issubclass(instance.__class__, cls):
return True

cls.__lazy_protocol_init_subclass__()

if cls.__callable_proto_members_only__ and issubclass(instance.__class__, cls):
return True

Expand Down Expand Up @@ -2088,8 +2088,6 @@ def _proto_hook(other):
raise TypeError("Instance and class checks can only be used with"
" @runtime_checkable protocols")

cls.__lazy_protocol_init_subclass__()

if not cls.__callable_proto_members_only__ :
if _allow_reckless_class_checks():
return NotImplemented
Expand Down
0