Skip to content
Prev Previous commit
Next Next commit
Use __init__, not __new__
  • Loading branch information
AlexWaygood committed Apr 1, 2023
commit 4d050749087ea86a7cacc9ce96b867057e655788
4 changes: 1 addition & 3 deletions Lib/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -1997,15 +1997,13 @@ 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 __new__(metacls, name, bases, namespace, **kwargs):
cls = super().__new__(metacls, name, bases, namespace, **kwargs)
def __init__(cls, *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__
)
Comment on lines +2000 to +2018
Copy link
Copy Markdown
Member Author

@AlexWaygood AlexWaygood Apr 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only reason I'm adding this method here rather than doing this work in Protocol.__init_subclass__ is that it seems slightly more backwards-compatible. With this PR, _ProtcolMeta.__instancecheck__ assumes that all classes with _ProtocolMeta as their metaclass will have a __protocol_attrs__ attribute. Since _ProtocolMeta is an undocumented implementation detail, it should only be Protocol and Protocol subclasses using _ProtocolMeta as their metaclass, and if we could count on that, then it would be safe to do this work in Protocol.__init_subclass__. But it's possible users might have been reaching into the internals of typing.py and creating other classes that use _ProtocolMeta as their metaclass, and I don't want to risk breaking their code unnecessarily.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return cls

def __instancecheck__(cls, instance):
# We need this method for situations where attributes are
Expand Down