Skip to content

Commit cdb6fe8

Browse files
miss-islingtondr-carlosJelleZijlstra
authored
[3.14] gh-137969: Fix evaluation of ref.evaluate(format=Format.FORWARDREF) objects (GH-138075) (#140929)
gh-137969: Fix evaluation of `ref.evaluate(format=Format.FORWARDREF)` objects (GH-138075) (cherry picked from commit 63e01d6) Co-authored-by: dr-carlos <[email protected]> Co-authored-by: Jelle Zijlstra <[email protected]>
1 parent 23e3771 commit cdb6fe8

File tree

3 files changed

+19
-6
lines changed

3 files changed

+19
-6
lines changed

Lib/annotationlib.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -159,19 +159,21 @@ def evaluate(
159159
type_params = getattr(owner, "__type_params__", None)
160160

161161
# Type parameters exist in their own scope, which is logically
162-
# between the locals and the globals. We simulate this by adding
163-
# them to the globals.
162+
# between the locals and the globals.
163+
type_param_scope = {}
164164
if type_params is not None:
165-
globals = dict(globals)
166165
for param in type_params:
167-
globals[param.__name__] = param
166+
type_param_scope[param.__name__] = param
167+
168168
if self.__extra_names__:
169169
locals = {**locals, **self.__extra_names__}
170170

171171
arg = self.__forward_arg__
172172
if arg.isidentifier() and not keyword.iskeyword(arg):
173173
if arg in locals:
174174
return locals[arg]
175+
elif arg in type_param_scope:
176+
return type_param_scope[arg]
175177
elif arg in globals:
176178
return globals[arg]
177179
elif hasattr(builtins, arg):
@@ -183,15 +185,15 @@ def evaluate(
183185
else:
184186
code = self.__forward_code__
185187
try:
186-
return eval(code, globals=globals, locals=locals)
188+
return eval(code, globals=globals, locals={**type_param_scope, **locals})
187189
except Exception:
188190
if not is_forwardref_format:
189191
raise
190192

191193
# All variables, in scoping order, should be checked before
192194
# triggering __missing__ to create a _Stringifier.
193195
new_locals = _StringifierDict(
194-
{**builtins.__dict__, **globals, **locals},
196+
{**builtins.__dict__, **globals, **type_param_scope, **locals},
195197
globals=globals,
196198
owner=owner,
197199
is_class=self.__forward_is_class__,

Lib/test/test_annotationlib.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1911,6 +1911,15 @@ def test_fwdref_invalid_syntax(self):
19111911
with self.assertRaises(SyntaxError):
19121912
fr.evaluate()
19131913

1914+
def test_re_evaluate_generics(self):
1915+
global alias
1916+
class C:
1917+
x: alias[int]
1918+
1919+
evaluated = get_annotations(C, format=Format.FORWARDREF)["x"].evaluate(format=Format.FORWARDREF)
1920+
alias = list
1921+
self.assertEqual(evaluated.evaluate(), list[int])
1922+
19141923

19151924
class TestAnnotationLib(unittest.TestCase):
19161925
def test__all__(self):
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix :meth:`annotationlib.ForwardRef.evaluate` returning :class:`annotationlib.ForwardRef`
2+
objects which do not update in new contexts.

0 commit comments

Comments
 (0)