Troubles with @runtime_checkable protocols #83089
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
assignee = None closed_at = <Date 2021-05-12.17:29:07.094> created_at = <Date 2019-11-24.17:39:56.423> labels = ['type-bug', 'library', '3.9', '3.10', '3.11'] title = 'Troubles with @runtime_checkable protocols' updated_at = <Date 2021-05-27.13:51:05.584> user = 'https://github.com/ilevkivskyi'
activity = <Date 2021-05-27.13:51:05.584> actor = 'miss-islington' assignee = 'none' closed = True closed_date = <Date 2021-05-12.17:29:07.094> closer = 'kj' components = ['Library (Lib)'] creation = <Date 2019-11-24.17:39:56.423> creator = 'levkivskyi' dependencies =  files =  hgrepos =  issue_num = 38908 keywords = ['patch'] message_count = 12.0 messages = ['357400', '357555', '391292', '391318', '393511', '393516', '393517', '393528', '393532', '393536', '393537', '394540'] nosy_count = 6.0 nosy_names = ['gvanrossum', 'levkivskyi', 'Kevin Shweh', 'miss-islington', 'uriyyo', 'kj'] pr_nums = ['26067', '26073', '26075', '26077', '26096', '26337'] priority = 'normal' resolution = 'fixed' stage = 'resolved' status = 'closed' superseder = None type = 'behavior' url = 'https://bugs.python.org/issue38908' versions = ['Python 3.9', 'Python 3.10', 'Python 3.11']
The text was updated successfully, but these errors were encountered:
The PEP-544 specifies that:
A protocol can be used as a second argument in isinstance() and issubclass() only if it is explicitly opt-in by @runtime_checkable decorator.
It is not specified exactly whether this should be enforced by static type checkers or at runtime. Currently it is enforced in both cases: mypy flags this as error, and a TypeError is raised at runtime.
There is however a problem with current runtime implementation: abc and functools modules may call issubclass() on non-runtime checkable protocols if they appear as explicit superclasses. This is currently solved by a sys._getframe() hack in typing module.
The problem is that current solution is incomplete. For example, the TypeError is not raised in the case of non-method protocols:
>>> class P(Protocol): ... x: int ... >>> >>> class C: ... ... >>> isinstance(C(), P) False # should be TypeError
I tried to fix it this morning but after an hour of attempts to tweak the existing hack I gave up. It looks like there are only two reasonable solutions:
It seems like the straightforward, minimal fix would be to just add
if (getattr(cls, '_is_protocol', False) and
to _ProtocolMeta.__instancecheck__. Does that fail on some edge case (that the current implementation works on)? It's a little weird that _ProtocolMeta.__instancecheck__ doesn't explicitly check that the protocol is runtime-checkable.
If you want to work on this, could you come up with some test cases that
Yurii and Kevin, thanks for pushing the patch forward! Thanks for the review too Guido.
I'm closing this issue as all bugfix PRs have landed to bugfix branches. This will make its way into the next versions of Python for those respective branches.
One point of contention is whether to introduce this in 3.9.6. This will cause code previously working in 3.9.5 to throw an error if people were using it incorrectly. So it might be better to only enforce this in 3.10 onwards.
I created #70265 just in case the decision is made to revert. Sorry for any inconvenience caused everyone!