Skip to content

gh-132261: Store annotations at hidden internal keys in the class dict#132345

Merged
JelleZijlstra merged 5 commits into
python:mainfrom
JelleZijlstra:annotatefunc
Apr 11, 2025
Merged

gh-132261: Store annotations at hidden internal keys in the class dict#132345
JelleZijlstra merged 5 commits into
python:mainfrom
JelleZijlstra:annotatefunc

Conversation

@JelleZijlstra

@JelleZijlstra JelleZijlstra commented Apr 10, 2025

Copy link
Copy Markdown
Member

This fixes #132261 and provides a more elegant solution for https://peps.python.org/pep-0749/#annotations-and-metaclasses. The trouble is that we store things at the __annotate__ and __annotations__ keys in the class dictionary and they interfere with the descriptors on type: the solution is to not store our internal objects at those keys. Instead, we now store the annotate function at __annotate_func__ and the annotations dict (once evaluated) at __annotations_cache__. These names remain undocumented implementation details.

Downsides:

  • If you look at the class dict, you'll see those new keys. typing.py needed some updates to deal with this.
  • The compatibility store with 3.13 changes in regards to looking directly in the class namespace for __annotations__. In 3.13, this always worked: if a class has annotations there's an __annotations__ key in the class dict. On current main, it sometimes works (only if somebody has accessed the .__annotations__ attribute). With this PR, it never works (unless the __annotations__ key was explicitly added). Breaking this pattern consistently seems better than breaking it nondeterministically though.
  • It becomes harder to get the annotate function in a metaclass __new__. I made annotationlib.get_annotate_function() handle this.
  • The internal names become accessible as attributes (e.g. you can do cls.__annotate_func__).

📚 Documentation preview 📚: https://cpython-previews--132345.org.readthedocs.build/

@JelleZijlstra JelleZijlstra requested a review from Eclips4 as a code owner April 10, 2025 14:22

@carljm carljm left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This looks pretty good to me! I expected it to be a lot gnarlier. It really just removes the ugly special handling of class annotations, everything it adds looks predictable and reasonably straightforward.

Thanks for your work on this!

Comment thread Lib/test/test_ast/test_ast.py
Comment thread Misc/NEWS.d/next/Core_and_Builtins/2025-04-09-21-51-37.gh-issue-132261.gL8thm.rst Outdated
…e-132261.gL8thm.rst

Co-authored-by: Carl Meyer <carl@oddbird.net>
@JelleZijlstra JelleZijlstra merged commit 07b8d31 into python:main Apr 11, 2025
cjwatson added a commit to cjwatson/zope.interface that referenced this pull request Jan 7, 2026
Following python/cpython#132345, type annotations
result in the annotate function being stored at `__annotate_func`.  This caused
test failures in klein along the lines of:

```
zope.interface.exceptions.BrokenMethodImplementation: The object FrozenHTTPHeaders(rawHeaders=()) has failed to implement interface klein._imessage.IHTTPHeaders: The contract of klein._imessage.IHTTPHeaders.__annotate_func__(format) is violated because 'FrozenHTTPHeaders.__annotate__()' doesn't allow enough arguments.
```

Ignore this new internal name.
icemac pushed a commit to zopefoundation/zope.interface that referenced this pull request Jan 9, 2026
Following python/cpython#132345, type annotations
result in the annotate function being stored at `__annotate_func`.  This caused
test failures in klein along the lines of:

```
zope.interface.exceptions.BrokenMethodImplementation: The object FrozenHTTPHeaders(rawHeaders=()) has failed to implement interface klein._imessage.IHTTPHeaders: The contract of klein._imessage.IHTTPHeaders.__annotate_func__(format) is violated because 'FrozenHTTPHeaders.__annotate__()' doesn't allow enough arguments.
```

Ignore this new internal name.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants