Skip to content

Commit 23e3771

Browse files
miss-islingtondr-carlossobolevn
authored
[3.14] pythongh-138425: Correctly partially evaluate global generics with undefined params in ref.evaluate(format=Format.FORWARDREF) (pythonGH-138430) (python#140927)
pythongh-138425: Correctly partially evaluate global generics with undefined params in `ref.evaluate(format=Format.FORWARDREF)` (pythonGH-138430) (cherry picked from commit e66f87c) Co-authored-by: dr-carlos <[email protected]> Co-authored-by: sobolevn <[email protected]>
1 parent 7e1bac6 commit 23e3771

File tree

3 files changed

+32
-1
lines changed

3 files changed

+32
-1
lines changed

Lib/annotationlib.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,8 +187,11 @@ def evaluate(
187187
except Exception:
188188
if not is_forwardref_format:
189189
raise
190+
191+
# All variables, in scoping order, should be checked before
192+
# triggering __missing__ to create a _Stringifier.
190193
new_locals = _StringifierDict(
191-
{**builtins.__dict__, **locals},
194+
{**builtins.__dict__, **globals, **locals},
192195
globals=globals,
193196
owner=owner,
194197
is_class=self.__forward_is_class__,

Lib/test/test_annotationlib.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1877,6 +1877,32 @@ def test_name_lookup_without_eval(self):
18771877

18781878
self.assertEqual(exc.exception.name, "doesntexist")
18791879

1880+
def test_evaluate_undefined_generic(self):
1881+
# Test the codepath where have to eval() with undefined variables.
1882+
class C:
1883+
x: alias[int, undef]
1884+
1885+
generic = get_annotations(C, format=Format.FORWARDREF)["x"].evaluate(
1886+
format=Format.FORWARDREF,
1887+
globals={"alias": dict}
1888+
)
1889+
self.assertNotIsInstance(generic, ForwardRef)
1890+
self.assertIs(generic.__origin__, dict)
1891+
self.assertEqual(len(generic.__args__), 2)
1892+
self.assertIs(generic.__args__[0], int)
1893+
self.assertIsInstance(generic.__args__[1], ForwardRef)
1894+
1895+
generic = get_annotations(C, format=Format.FORWARDREF)["x"].evaluate(
1896+
format=Format.FORWARDREF,
1897+
globals={"alias": Union},
1898+
locals={"alias": dict}
1899+
)
1900+
self.assertNotIsInstance(generic, ForwardRef)
1901+
self.assertIs(generic.__origin__, dict)
1902+
self.assertEqual(len(generic.__args__), 2)
1903+
self.assertIs(generic.__args__[0], int)
1904+
self.assertIsInstance(generic.__args__[1], ForwardRef)
1905+
18801906
def test_fwdref_invalid_syntax(self):
18811907
fr = ForwardRef("if")
18821908
with self.assertRaises(SyntaxError):
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix partial evaluation of :class:`annotationlib.ForwardRef` objects which rely
2+
on names defined as globals.

0 commit comments

Comments
 (0)