You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
typing_extensions.Protocol and typing.Protocol don't always play well together #245
If you're just using typing_extensions.Protocol, we backport python/cpython#31628 from Python 3.11. That means you can do this (the __init__ method on a protocol is retained, and can be used in concrete subclasses of that protocol):
>>> import typing_extensions as te
>>> classP(te.Protocol):
... x: int
... def__init__(self, x: int) -> None:
... self.x = x
...
>>> classC(P): pass
...
>>> C(1).x
1
But, if you have both typing.Protocol and typing_extensions.Protocol in the mro, it doesn't work. On Python 3.8 (and, I assume, 3.9/3.10), there's this behaviour currently:
>>> import typing as t, typing_extensions as te
>>> classP(t.Protocol): pass
...
>>> classQ(P, te.Protocol):
... x: int
... def__init__(self, x: int) -> None:
... self.x = x
...
>>> classC(Q): pass
...
>>> C(1).x
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'C' object has no attribute 'x'
I've been looking at it, and I think this may not be fixable, unfortunately. Should we document that this is a known limitation?
It does look hard to fix. typing.Protocol before 3.11 clobbered __init__, so if you inherit from it, that's the behavior you'll get. We should simply document it, unless someone comes up with a clever way to prevent this behavior.
Another limitation: with typing_extensions.Protocol, it is possible to create a protocol that extends typing_extensions.Buffer:
>>> import typing_extensions as te
>>> classQ(te.Buffer, te.Protocol): pass
...
>>>
But if you have both typing.Protocol and typing_extensions.Protocol in the mro, it doesn't work:
>>> import typing as t, typing_extensions as te
>>> classP(t.Protocol): pass
...
>>> classQ(P, te.Buffer, te.Protocol): pass
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Users\alexw\coding\typing_extensions\src\typing_extensions.py", line 632, in __new__
return abc.ABCMeta.__new__(mcls, name, bases, namespace, **kwargs)
File "C:\Users\alexw\.conda\envs\py38\lib\abc.py", line 85, in __new__
cls = super().__new__(mcls, name, bases, namespace, **kwargs)
File "C:\Users\alexw\.conda\envs\py38\lib\typing.py", line 1119, in __init_subclass__
raise TypeError('Protocols can only inherit from other'
TypeError: Protocols can only inherit from other protocols, got <class 'typing_extensions.Buffer'>
This is because we have typing_extensions.Buffer in our version of _PROTO_ALLOWLIST, but it obviously doesn't exist in the typing version of _PROTO_ALLOWLIST:
Activity
JelleZijlstra commentedon Jun 17, 2023
Thanks for finding this.
It does look hard to fix.
typing.Protocolbefore 3.11 clobbered__init__, so if you inherit from it, that's the behavior you'll get. We should simply document it, unless someone comes up with a clever way to prevent this behavior.AlexWaygood commentedon Jun 17, 2023
Another limitation: with
typing_extensions.Protocol, it is possible to create a protocol that extendstyping_extensions.Buffer:But if you have both
typing.Protocolandtyping_extensions.Protocolin the mro, it doesn't work:This is because we have
typing_extensions.Bufferin our version of_PROTO_ALLOWLIST, but it obviously doesn't exist in thetypingversion of_PROTO_ALLOWLIST:typing_extensions/src/typing_extensions.py
Lines 493 to 500 in fd1f853