Skip to content

Commit 1c9de6b

Browse files
[3.14] gh-137814: Fix __qualname__ of __annotate__ functions in the interpreter (#148221)
gh-137814: [3.14] Fix __qualname__ of __annotate__ functions in the interpreter I'd still like to do #137842 on 3.15+, but that requires changing bytecode and we can't really afford to do that in 3.14. So to fix this in 3.14, let's patch things up in the ceval loop instead. This is safe because the compiler only sets __annotate__ to just-created dedicated annotate functions.
1 parent 0a6bc08 commit 1c9de6b

File tree

7 files changed

+60
-2
lines changed

7 files changed

+60
-2
lines changed

Include/internal/pycore_opcode_metadata.h

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_uop_metadata.h

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/test/test_type_annotations.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -836,6 +836,23 @@ def test_complex_comprehension_inlining_exec(self):
836836
lamb = list(genexp)[0]
837837
self.assertEqual(lamb(), 42)
838838

839+
def test_annotate_qualname(self):
840+
code = """
841+
def f() -> None:
842+
def nested() -> None: pass
843+
return nested
844+
class Outer:
845+
x: int
846+
def method(self, x: int):
847+
pass
848+
"""
849+
ns = run_code(code)
850+
method = ns["Outer"].method
851+
self.assertEqual(ns["f"].__annotate__.__qualname__, "f.__annotate__")
852+
self.assertEqual(ns["f"]().__annotate__.__qualname__, "f.<locals>.nested.__annotate__")
853+
self.assertEqual(method.__annotate__.__qualname__, "Outer.method.__annotate__")
854+
self.assertEqual(ns["Outer"].__annotate__.__qualname__, "Outer.__annotate__")
855+
839856
# gh-138349
840857
def test_module_level_annotation_plus_listcomp(self):
841858
cases = [
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix the ``__qualname__`` attribute of ``__annotate__`` functions on
2+
functions.

Python/bytecodes.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4970,6 +4970,13 @@ dummy_func(
49704970
PyObject **ptr = (PyObject **)(((char *)func) + offset);
49714971
assert(*ptr == NULL);
49724972
*ptr = attr;
4973+
if (oparg == MAKE_FUNCTION_ANNOTATE && PyFunction_Check(attr)) {
4974+
// gh-137814: Fix the qualname of __annotate__ functions
4975+
PyFunctionObject *func_obj = (PyFunctionObject *)attr;
4976+
PyObject *fixed_qualname = PyUnicode_FromFormat("%U.__annotate__", ((PyFunctionObject *)func)->func_qualname);
4977+
ERROR_IF(fixed_qualname == NULL);
4978+
Py_SETREF(func_obj->func_qualname, fixed_qualname);
4979+
}
49734980
}
49744981

49754982
inst(RETURN_GENERATOR, (-- res)) {

Python/executor_cases.c.h

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/generated_cases.c.h

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)