8000 gh-132493: Avoid eager import of annotationlib in typing (again) by JelleZijlstra · Pull Request #132596 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

gh-132493: Avoid eager import of annotationlib in typing (again) #132596

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 10 commits into from
Apr 17, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions Lib/test/test_typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -4542,10 +4542,15 @@ class classproperty:
def __get__(self, instance, type):
raise CustomError

@runtime_checkable
class Commentable(Protocol):
evil = classproperty()

class Normal:
evil = None

with self.assertRaises(TypeError) as cm:
@runtime_checkable
class Commentable(Protocol):
evil = classproperty()
isinstance(Normal(), Commentable)

exc = cm.exception
self.assertEqual(
Expand Down
55 changes: 33 additions & 22 deletions Lib/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -1777,6 +1777,8 @@ class _TypingEllipsis:
'__parameters__', '__orig_bases__', '__orig_class__',
'_is_protocol', '_is_runtime_protocol', '__protocol_attrs__',
'__non_callable_proto_members__', '__type_params__',
'__protocol_attrs_cache__', '__non_callable_proto_members_cache__',
'__final__',
})

_SPECIAL_NAMES = frozenset({
Expand Down Expand Up @@ -1941,11 +1943,6 @@ def __new__(mcls, name, bases, namespace, /, **kwargs):
)
return super().__new__(mcls, name, bases, namespace, **kwargs)

def __init__(cls, *args, **kwargs):
super().__init__(*args, **kwargs)
if getattr(cls, "_is_protocol", False):
cls.__protocol_attrs__ = _get_protocol_attrs(cls)

def __subclasscheck__(cls, other):
if cls is Protocol:
return type.__subclasscheck__(cls, other)
Expand Down Expand Up @@ -1997,14 +1994,44 @@ def __instancecheck__(cls, instance):
val = getattr_static(instance, attr)
except AttributeError:
break
# this attribute is set by @runtime_checkable:
if val is None and attr not in cls.__non_callable_proto_members__:
break
else:
return True

return False

@property
def __protocol_attrs__(cls):
try:
return cls.__protocol_attrs_cache__
except AttributeError:
protocol_attrs = _get_protocol_attrs(cls)
cls.__protocol_attrs_cache__ = protocol_attrs
return protocol_attrs

@property
def __non_callable_proto_members__(cls):
# PEP 544 prohibits using issubclass()
# with protocols that have non-method members.
try:
return cls.__non_callable_proto_members_cache__
except AttributeError:
non_callable_members = set()
for attr in cls.__protocol_attrs__:
try:
is_callable = callable(getattr(cls, attr, None))
except Exception as e:
raise TypeError(
f"Failed to determine whether protocol member {attr!r} "
"is a method member"
) from e
else:
if not is_callable:
non_callable_members.add(attr)
cls.__non_callable_proto_members_cache__ = non_callable_members
return non_callable_members


@classmethod
def _proto_hook(cls, other):
Expand Down Expand Up @@ -2220,22 +2247,6 @@ def close(self): ...
raise TypeError('@runtime_checkable can be only applied to protocol classes,'
' got %r' % cls)
cls._is_runtime_protocol = True
# PEP 544 prohibits using issubclass()
# with protocols that have non-method members.
# See gh-113320 for why we compute this attribute here,
# rather than in `_ProtocolMeta.__init__`
cls.__non_callable_proto_members__ = set()
for attr in cls.__protocol_attrs__:
try:
is_callable = callable(getattr(cls, attr, None))
except Exception as e:
raise TypeError(
f"Failed to determine whether protocol member {attr!r} "
"is a method member"
) from e
else:
if not is_callable:
cls.__non_callable_proto_members__.add(attr)
return cls


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Speed up the creation of :class:`typing.Protocol` subclasses.
Loading
0