8000 gh-132493: Support deferred annotations in Protocols by JelleZijlstra · Pull Request #132494 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

gh-132493: Support deferred annotations in Protocols #132494

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

Merged
merged 1 commit into from
Apr 15, 2025

Conversation

JelleZijlstra
Copy link
Member
@JelleZijlstra JelleZijlstra commented Apr 14, 2025

class DeferredProto(Protocol):
x: DoesNotExist
self.assertEqual(get_protocol_members(DeferredProto), {"x"})
self.assertEqual(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: when should we test all formats and when just one is enough?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess in this case Protocol is not doing anything very sophisticated with its annotations, so we can just run a basic test to make sure it works.

Copy link
Member
@AlexWaygood AlexWaygood left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this might slow down protocol class creation a bit? Which might in turn slow down the import time of typing.py, since typing defines a lot of protocols... still, this feature seems worth it.

@JelleZijlstra
Copy link
Member Author

I wonder if we could lazily create the protocol members list? But we can explore that separately.

@JelleZijlstra JelleZijlstra merged commit 666eeda into python:main Apr 15, 2025
45 checks passed
JelleZijlstra added a commit to JelleZijlstra/cpython that referenced this pull request Apr 16, 2025
pythongh-132494 made typing.py eagerly import annotationlib again because
typing contains several protocols. Avoid this by determining annotations
lazily. This should also make protocol creation faster:

Unpatched:

$ ./python.exe -m timeit -s 'from typing import Protocol, runtime_checkable' '''@runtime_checkable
class MyProtocol(Protocol):
    def meth(self): pass
'''
50000 loops, best of 5: 9.28 usec per loop
$ ./python.exe -m timeit -s 'from typing import Protocol, runtime_checkable' '''class MyProtocol(Protocol):
    def meth(self): pass
'''
50000 loops, best of 5: 9.05 usec per loop

Patched:

$ ./python.exe -m timeit -s 'from typing import Protocol, runtime_checkable' '''@runtime_checkable
class MyProtocol(Protocol):
    def meth(self): pass
'''
50000 loops, best of 5: 7.69 usec per loop
$ ./python.exe -m timeit -s 'from typing import Protocol, runtime_checkable' '''class MyProtocol(Protocol):
    def meth(self): pass
'''
50000 loops, best of 5: 7.78 usec per loop

This was on a debug build though and I haven't compared it with versions where Protocol just accessed
`.__annotations__` directly, and it's not a huge difference, so I don't think it's worth calling out the
optimization too much.

A downside of this change is that any errors that happen during the determination of attributes now
happen only the first time isinstance() is called. This seems OK since these errors happen only in
fairly exotic circumstances.

Another downside is that any attributes added after class initialization now get picked up as protocol
members. This came up in the typing test suite due to `@final`, but may cause issues elsewhere too.
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.

3 participants
0