Open
Description
At runtime, the following members are currently excluded by Protocol
: (source)
_TYPING_INTERNALS = frozenset({
'__parameters__', '__orig_bases__', '__orig_class__',
'_is_protocol', '_is_runtime_protocol', '__protocol_attrs__',
'__non_callable_proto_members__', '__type_params__',
})
_SPECIAL_NAMES = frozenset({
'__abstractmethods__', '__annotations__', '__dict__', '__doc__',
'__init__', '__module__', '__new__', '__slots__',
'__subclasshook__', '__weakref__', '__class_getitem__',
'__match_args__', '__static_attributes__', '__firstlineno__',
'__annotate__',
})
# These special attributes will be not collected as protocol members.
EXCLUDED_ATTRIBUTES = _TYPING_INTERNALS | _SPECIAL_NAMES | {'_MutableMapping__marker'}
However, neither PEP 544 nor the typing spec formalize this list, which leads to diverging behavior between different type checkers:
Code sample in pyright playground, mypy playground
from typing import TypeIs, Protocol
class MyProtocol(Protocol):
@classmethod
def __subclasshook__(cls, other: type, /) -> TypeIs[type["MyProtocol"]]:
...
x: MyProtocol = int(1) # mypy: ✅ pyright: ❌
Code sample in pyright playground, mypy playground
from typing import Protocol
class MyProtocol(Protocol):
__abstractmethods__: frozenset[str]
x: MyProtocol = int(1) # mypy: ✅ pyright: ❌
A real-world example where this matters is for instance a Protocol
for NamedTuple
Instances, that checks whether types.get_original_bases(cls)
includes typing.NamedTuple
inside __subclasshook__
.