diff --git a/Lib/annotationlib.py b/Lib/annotationlib.py index bee019cd51591e..4f762d96429c13 100644 --- a/Lib/annotationlib.py +++ b/Lib/annotationlib.py @@ -167,6 +167,13 @@ def evaluate( globals[param.__name__] = param if self.__extra_names__: locals = {**locals, **self.__extra_names__} + if (annotate := getattr(owner, "__annotate__", None)) and annotate.__closure__: + for name, cell in zip(annotate.__code__.co_freevars, annotate.__closure__): + if name != "__classdict__": + try: + locals[name] = cell.cell_contents + except ValueError: + pass arg = self.__forward_arg__ if arg.isidentifier() and not keyword.iskeyword(arg): diff --git a/Lib/test/test_annotationlib.py b/Lib/test/test_annotationlib.py index 88e0d611647f28..b1962ad0358b58 100644 --- a/Lib/test/test_annotationlib.py +++ b/Lib/test/test_annotationlib.py @@ -1617,6 +1617,33 @@ def test_evaluate_with_type_params_and_scope_conflict(self): TypeParamsSample.TypeParamsAlias2, ) + def test_evaluate_local_generic(self): + class Cls: + nonlocal alias, alias2, alias3 + x: alias[int] + y: alias[alias] + z: alias[alias2, alias3] + + fwdref = get_annotations(Cls, format=Format.FORWARDREF)["x"] + with self.assertRaises(NameError): + fwdref.evaluate() + + alias = list + self.assertEqual(fwdref.evaluate(), list[int]) + + del alias + fwdref = get_annotations(Cls, format=Format.FORWARDREF)["y"] + alias = list + self.assertEqual(fwdref.evaluate(), list[list]) + + del alias + fwdref = get_annotations(Cls, format=Format.FORWARDREF)["z"] + alias = dict + alias2 = int + alias3 = str + self.assertEqual(fwdref.evaluate(), dict[int, str]) + + def test_fwdref_with_module(self): self.assertIs(ForwardRef("Format", module="annotationlib").evaluate(), Format) self.assertIs( diff --git a/Misc/NEWS.d/next/Library/2025-08-27-12-06-52.gh-issue-138151.ajQpH5.rst b/Misc/NEWS.d/next/Library/2025-08-27-12-06-52.gh-issue-138151.ajQpH5.rst new file mode 100644 index 00000000000000..01ddef6914b642 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-08-27-12-06-52.gh-issue-138151.ajQpH5.rst @@ -0,0 +1,2 @@ +Fix :meth:`annotationlib.ForwardRef.evaluate` not handling generics with +nonlocal variables that are defined after the annotation.