Repository navigation
annotationlib: ref.evaluate(format=Format.FORWARDREF) returns a ForwardRef with a copied __globals__ that no longer updates #137969
Description
Activity
- addedtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
on Aug 19, 2025 - addedstdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directory
on Aug 19, 2025 A simple (possibly naive?) fix for this is just to add a new dictionary to store type param values instead of copying the globals.
Then, a check against this dict is added where globals is accessed later inevaluate(), logically between the locals and globals.This seemingly fixes the provided example and works with type parameters (as far as my simple tests have shown).
Happy to open a PR, just wanting to make sure I haven't missed the problem (or created a new one).Make sure you run the annotationlib tests with:
./python -m test -v test_annotationlibIt looks like your commit breaks the tests as you shadow the
typesmodule with your new variable which breaks the check a few lines up forisinstance(owner, types.ModuleType).I thought I'd tried putting the
type_paramsat the start of the locals and had test failures I didn't have time to investigate, but I think I had some logic in the wrong order.Reacted by dr-carlosThanks for the heads up re the test command!
I've renamed the variable to something much more sensible (forgot I was going to do that before posting earlier 🤦 )
All tests are now passing. A new test would possibly be warranted for this use case, but that should probably be discussed in some future PR.That sounds reasonable, please send a PR and I'll take a look.
Reacted by dr-carlos- added a commit that references this issue
on Nov 3, 2025 - added 4 commits that reference this issue
on Nov 3, 2025 - added a commit that references this issue
on Nov 3, 2025 @dr-carlos your fix had to be reverted because of failures on some buildbots. Are you able to make another PR and try to fix the issues?
Reacted by dr-carlos@dr-carlos your fix had to be reverted because of failures on some buildbots. Are you able to make another PR and try to fix the issues?
Yep, I'll take a look!
Reacted by Jelle Zijlstra@dr-carlos your fix had to be reverted because of failures on some buildbots. Are you able to make another PR and try to fix the issues?
It seems that the issue was with the test:
test_re_evaluate_genericstests globals, so uses aglobaldeclaration, but this shadows a name used in other test functions' scopes.- Renaming this variable to something unique fixed the other tests breaking
- For some reason, if you run the tests with the same command as the buildbots use, it seems that the global scope is shared across test runs? Anyway, if you just check whether the global is still set and then delete it, it fixes the issue.
Anyway, I've made a PR which re-implements the fix now that #138164 is implemented and includse a working test.
It's a slightly different approach to last time, but I haven't found any issues with it so far, so let me know if you'd prefer it implemented with a separate dict instead of injecting intolocals.Thanks, I think the difference is that buildbots run the same test twice in the same process. Should be able to repro with something like
./python -m test test_annotationlib test_annotationlib.- added a commit that references this issue
on Nov 13, 2025 - added a commit that references this issue
on Nov 13, 2025 - added a commit that references this issue
on Nov 13, 2025 - added 3 commits that reference this issue
on Dec 6, 2025
Bug report
Bug description:
This can happen internally in
get_annotationsand means subsequent attempts to evaluate it will fail even if the names have since been defined.Underlying logic:
This
evaluatecall happens internally ifget_annotationshas to rely on the fallback behaviour for an unexpected exception, such as anAttributeError:This appears to be caused by the creation of a new globals dict here:
cpython/Lib/annotationlib.py
Lines 164 to 167 in e39255e
Commenting this out makes these examples succeed, but obviously breaks type parameters.
CPython versions tested on:
CPython main branch, 3.14
Operating systems tested on:
No response
Linked PRs
ref.evaluate(format=Format.FORWARDREF)objects #138075ref.evaluate(format=Format.FORWARDREF)objects (GH-138075) #140929ref.evaluate(format=Format.FORWARDREF)objects (#138075)" #140930ref.evaluate(format=Format.FORWARDREF)objects (GH-138075) (#140929)" #140931ForwardRefs which rely on globals #140974ForwardRefs which rely on globals (GH-140974) #141527