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: runtime-checkable protocols are broken on main #104555
I'm getting very confused here. The fix for the original issue is simple, and I've fixed it locally. But as part of the PR, I tried adding this test, and the last assertion fails (the test reports that TypeErrorwasn't raised):
>>> from typing import*
>>> @runtime_checkable
... class Spam[T](Protocol):
... x: T
...
...
>>> class Eggs[T]:
... def__init__(self, x: T) -> None:
... self.x = x
...
...
>>> issubclass(Eggs, Spam)
Traceback (most recent call last):
File "<pyshell#5>", line 1, in <module>
issubclass(Eggs, Spam)
File "C:\Users\alexw\coding\cpython\Lib\abc.py", line 123, in __subclasscheck__
return _abc_subclasscheck(cls, subclass)
File "C:\Users\alexw\coding\cpython\Lib\typing.py", line 1875, in _proto_hook
raise TypeError("Protocols with non-method members"
TypeError: Protocols with non-method members don't support issubclass()
The issue you saw also reproduces for me without PEP 695 syntax. It seems to be due to the _allow_reckless_class_checks check, which somehow thinks the caller is abc if we're in a test, but not if we're in the REPL.
Python 3.12.0a7+ (heads/main:1163782868, May 16 2023, 18:09:28) [MSC v.1932 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from typing import*
>>> @runtime_checkable
... classFoo(Protocol):
... x: int
...
>>> classBar:
... x =42
...
>>> issubclass(Bar, Foo)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Users\alexw\coding\cpython\Lib\abc.py", line 123, in __subclasscheck__
return _abc_subclasscheck(cls, subclass)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\alexw\coding\cpython\Lib\typing.py", line 1875, in _proto_hook
raise TypeError("Protocols with non-method members"
TypeError: Protocols with non-method members don't support issubclass()
>>> isinstance(Bar(), Foo)
True
>>> issubclass(Bar, Foo)
False
@sunmy2019 I was just about to file 2601138 as a PR, which fixes things, but do you think we could fix it by just adjusting the depth value we pass to _allow_reckless_class_checks()?
Activity
AlexWaygood commentedon May 16, 2023
(I'm working on a fix)
JelleZijlstra commentedon May 16, 2023
Thanks for catching! I suppose this is about
__type_params__?AlexWaygood commentedon May 16, 2023
Yes.
AlexWaygood commentedon May 16, 2023
I'm getting very confused here. The fix for the original issue is simple, and I've fixed it locally. But as part of the PR, I tried adding this test, and the last assertion fails (the test reports that
TypeErrorwasn't raised):But I can't reproduce the failure in the REPL:
AlexWaygood commentedon May 16, 2023
Anyway, I'll submit a PR without that test for now...
JelleZijlstra commentedon May 16, 2023
I'll look into this more.
AlexWaygood commentedon May 16, 2023
Okay, cool. I'll enable automerge on my PR for now, as it definitely gets us closer to correct behaviour.
JelleZijlstra commentedon May 16, 2023
The issue you saw also reproduces for me without PEP 695 syntax. It seems to be due to the
_allow_reckless_class_checkscheck, which somehow thinks the caller isabcif we're in a test, but not if we're in the REPL.AlexWaygood commentedon May 16, 2023
Aha, so we've discovered a bug, it just isn't a new bug.
JelleZijlstra commentedon May 16, 2023
It's possible this bug was introduced by the fact that Generic is now a C class, haven't tried on 3.11 yet.
AlexWaygood commentedon May 16, 2023
Yes, looks like the bug is new in 3.12. Running this test file passes on 3.11, but not on
main:6 remaining items
JelleZijlstra commentedon May 16, 2023
47753ec
AlexWaygood commentedon May 16, 2023
(I am, again, working on a fix :)
pythongh-104555: Sidestep the ABCMeta.__instancecheck__ cache in typi…
AlexWaygood commentedon May 16, 2023
Incredible stuff:
sunmy2019 commentedon May 16, 2023
AlexWaygood commentedon May 16, 2023
@sunmy2019 I was just about to file 2601138 as a PR, which fixes things, but do you think we could fix it by just adjusting the
depthvalue we pass to_allow_reckless_class_checks()?sunmy2019 commentedon May 16, 2023
Oh sorry, ignore my previous comment.
sunmy2019 commentedon May 16, 2023
Confirmed that the code path that does not raise TypeError goes through L606.
cpython/Modules/_abc.c
Lines 589 to 608 in 03e3c34
@AlexWaygood You are right!
AlexWaygood commentedon May 16, 2023
@sunmy2019 thanks for checking!
sunmy2019 commentedon May 16, 2023
Oh, now I see the whole picture!
abc.ABCMetacache__subclasscheck__results if no exception occurs.Protocolassigns a__subclasshook__, which raises or does not raise exceptions depending on the caller._ProtocolMeta.__instancecheck__calls the__subclasshook__in (2) (not raising exceptions), and the result is cached by (1)sunmy2019 commentedon May 16, 2023
More details for (2) (3)
The
__subclasshook__inProtocolutilizes_allow_reckless_class_checks(). In normal cases, it isIt then raises an exception.
In the problematic scenarios (3):
It does raise an exception.
Merge branch 'main' into 709funconly
gh-104555: Runtime-checkable protocols: Don't let previous calls to `…
pythongh-104555: Runtime-checkable protocols: Don't let previous call…