Repository navigation
gh-160948: Fix annotationlib.ForwardRef.evaluate() ignoring explicit globals/locals for closure-cell references - #160952
Conversation
…licit globals/locals for closure-cell references
|
Considering the issue is still under discussion, I'm closing this. |
|
We may keep it open and only change the locals handling. Because the globals one may not be wrong. |
…er the closure cell
|
Thanks @DavidCEllis for the demonstration, and @picnixz for the review. You are both right: the cell holds a nonlocal binding, and David's generic example shows that the multi-name path already places the nonlocals between the locals and the globals. The
On the question about past issues: I searched the tracker for prior reports of this behaviour (annotationlib / ForwardRef / evaluate combined with cell, closure, globals and locals) and could not find one. The closest matches are gh-141174 (ForwardRef test coverage) and the separate issue about |
Summary
ForwardRef.evaluate()had an early return for references holding a single closure cell (__cell__is aCellType) that ran before any namespace handling. As a result, an explicitly passedlocalswas silently ignored for such references, while every other path in the same method gives it precedence: for references holding a dict of cells, the cells are merged into the locals withsetdefault, so explicit locals win over the captured scope.As discussed in gh-160948, the cell holds a nonlocal binding, so it correctly takes precedence over
globals(on the dict-of-cells path, the nonlocals sit between the locals and the globals). Only thelocalsprecedence was wrong, and that is all this PR changes.Fix
In
Lib/annotationlib.py(ForwardRef.evaluate):globalsnorlocalswas passed, preserving the existing fast path and behaviour for the common no-argument call.localsand beforeglobals, mirroring the dict-of-cells path. An empty cell (ValueError) falls through exactly as before.No other paths are changed: the dict-of-cells and
__extra_names__handling, type-parameter injection, and the non-identifierevalpath are untouched.Tests
Two regression tests in
Lib/test/test_annotationlib.py(TestForwardRefClass):test_evaluate_cell_with_explicit_namespaces— an explicitly passedlocalswins over the cell, in both VALUE and FORWARDREF formats;globalsdoes not; the cell remains the fallback when the name is absent fromlocals.test_evaluate_empty_cell_with_explicit_namespaces— an emptied cell still raisesNameErrorwith no arguments, explicit namespaces are consulted, and a refilled cell resumes its place betweenlocalsandglobals.Both tests fail on the unmodified code and pass with this change.
Results (local build of main @ 83fe630):
test_annotationlib: SUCCESS.test_typing: SUCCESS.Fixes gh-160948.
Disclosure: this change was drafted with AI assistance and reviewed by the author, who takes responsibility for it.