Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Have Protocol inherit from typing.Generic on 3.8+ #184

Merged
merged 7 commits into from
May 24, 2023

Conversation

AlexWaygood
Copy link
Member

@AlexWaygood AlexWaygood commented May 23, 2023

Fixes #181.

The diff GitHub is displaying for this PR is a mess to read, but it's actually a fairly simple change. We branch on whether sys.version_info >= (3, 8) or not. If we're on 3.8+, we inherit from typing.Generic (like Protocol does in CPython). If we're on 3.7, we don't (because typing.Generic won't let us on Python 3.7).

Inheriting from typing.Generic is very useful for us, because it means that we no longer have to worry about keeping typing_extensions.Protocol.__class_getitem__ in sync with the logic in typing.Generic.__class_getitem__. The root cause of #181 was that the two methods got out of sync; changes that were made to typing.Generic.__class_getitem__ were never backported to typing_extensions.Protocol.__class_getitem__.

Other than the fact that Protocol defines __class_getitem__ on 3.7, but doesn't on 3.8, the implementations of Protocol across the two branches are broadly the same (and broadly the same as they were before this PR).

The one complication is that I had to work around this line in CPython here: https://github.com/python/cpython/blob/08b4eb83aadcbdb389b5970b51cac9be95146c2a/Lib/typing.py#L1024. typing.py does this to determine whether a class is Generic or Protocol, and we go down the wrong code path if typing.py determines that typing_extensions.Protocol is a different class to typing.Protocol

is_generic_or_protocol = cls in (Generic, Protocol)

To work around that, I define __eq__ on _ProtocolMeta so that typing.Protocol == typing_extensions.Protocol evaluates to True. This is a ghastly hack, but, well, it works. (As a result of defining __eq__, I end up also having to define __hash__, or Python starts complaining that typing_extensions.Protocol isn't hashable, and loads of tests start failing.)

@JelleZijlstra JelleZijlstra merged commit 88a7f68 into python:main May 24, 2023
@AlexWaygood AlexWaygood deleted the inherit-from-generic branch May 24, 2023 06:17
@dolfinus
Copy link
Contributor

This PR caused #188

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Cannot parameterise typing_extensions.Protocol with a ParamSpec or TypeVarTuple on py310+
3 participants