]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-137814: Fix `__qualname__` of `__annotate__` (#137842)
authorJelle Zijlstra <jelle.zijlstra@gmail.com>
Thu, 16 Apr 2026 04:52:30 +0000 (21:52 -0700)
committerGitHub <noreply@github.com>
Thu, 16 Apr 2026 04:52:30 +0000 (21:52 -0700)
Include/internal/pycore_magic_number.h
Include/internal/pycore_symtable.h
Lib/test/test_type_annotations.py
Misc/NEWS.d/next/Core_and_Builtins/2025-08-15-21-33-16.gh-issue-137814.6yRTeu.rst [new file with mode: 0644]
Python/compile.c
Python/symtable.c

index 9d36165c8a8ffb77fc52cd6aad25f26cc66982ce..c6a2e72fe7b0db7f49958d66c77ec26b764970a1 100644 (file)
@@ -294,6 +294,7 @@ Known values:
     Python 3.15a4 3661 (Lazy imports IMPORT_NAME opcode changes)
     Python 3.15a8 3662 (Add counter to RESUME)
     Python 3.15a8 3663 (Merge GET_ITER and GET_YIELD_FROM_ITER. Modify SEND to make it a bit more like FOR_ITER)
+    Python 3.15a8 3664 (Fix __qualname__ for __annotate__ functions)
 
 
     Python 3.16 will start with 3700
@@ -307,7 +308,7 @@ PC/launcher.c must also be updated.
 
 */
 
-#define PYC_MAGIC_NUMBER 3663
+#define PYC_MAGIC_NUMBER 3664
 /* This is equivalent to converting PYC_MAGIC_NUMBER to 2 bytes
    (little-endian) and then appending b'\r\n'. */
 #define PYC_MAGIC_NUMBER_TOKEN \
index e1176c65c5ca9e619bb1ad1d89b10653c7eb1c1f..c650a94a1eab2e17356024ebd49a68e66c5ee5da 100644 (file)
@@ -90,6 +90,7 @@ typedef struct _symtable_entry {
     PyObject *ste_id;        /* int: key in ste_table->st_blocks */
     PyObject *ste_symbols;   /* dict: variable names to flags */
     PyObject *ste_name;      /* string: name of current block */
+    PyObject *ste_function_name;  /* string or NULL: for annotation blocks: name of the corresponding functions */
     PyObject *ste_varnames;  /* list of function parameters */
     PyObject *ste_children;  /* list of child blocks */
     PyObject *ste_directives;/* locations of global and nonlocal statements */
index d40eb382c536d92a29329698c888eb294e4dfbf8..d459f497e333e645b23950fb7cfc2edc02f42cb7 100644 (file)
@@ -836,6 +836,23 @@ class RegressionTests(unittest.TestCase):
         lamb = list(genexp)[0]
         self.assertEqual(lamb(), 42)
 
+    def test_annotate_qualname(self):
+        code = """
+        def f() -> None:
+            def nested() -> None: pass
+            return nested
+        class Outer:
+            x: int
+            def method(self, x: int):
+                pass
+        """
+        ns = run_code(code)
+        method = ns["Outer"].method
+        self.assertEqual(method.__annotate__.__qualname__, "Outer.method.__annotate__")
+        self.assertEqual(ns["f"].__annotate__.__qualname__, "f.__annotate__")
+        self.assertEqual(ns["f"]().__annotate__.__qualname__, "f.<locals>.nested.__annotate__")
+        self.assertEqual(ns["Outer"].__annotate__.__qualname__, "Outer.__annotate__")
+
     # gh-138349
     def test_module_level_annotation_plus_listcomp(self):
         cases = [
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-08-15-21-33-16.gh-issue-137814.6yRTeu.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-08-15-21-33-16.gh-issue-137814.6yRTeu.rst
new file mode 100644 (file)
index 0000000..8356131
--- /dev/null
@@ -0,0 +1,2 @@
+Fix the ``__qualname__`` attribute of ``__annotate__`` functions on
+functions.
index 365b118cc71b44671cb8e1541885cb6442556997..5f82641a3948c6dcd2905ac061434ceba1652b6b 100644 (file)
@@ -297,6 +297,19 @@ compiler_set_qualname(compiler *c)
                 base = Py_NewRef(parent->u_metadata.u_qualname);
             }
         }
+        if (u->u_ste->ste_function_name != NULL) {
+            PyObject *tmp = base;
+            base = PyUnicode_FromFormat("%U.%U",
+                base,
+                u->u_ste->ste_function_name);
+            Py_DECREF(tmp);
+            if (base == NULL) {
+                return ERROR;
+            }
+        }
+    }
+    else if (u->u_ste->ste_function_name != NULL) {
+        base = Py_NewRef(u->u_ste->ste_function_name);
     }
 
     if (base != NULL) {
index 4b695e4b2588d8a9f17b7f1c59ec30775ac519ed..2263a2d8db9097d1bb240d193961f228b20339c4 100644 (file)
@@ -108,6 +108,7 @@ ste_new(struct symtable *st, identifier name, _Py_block_ty block,
     ste->ste_id = k; /* ste owns reference to k */
 
     ste->ste_name = Py_NewRef(name);
+    ste->ste_function_name = NULL;
 
     ste->ste_symbols = NULL;
     ste->ste_varnames = NULL;
@@ -186,6 +187,7 @@ ste_dealloc(PyObject *op)
     ste->ste_table = NULL;
     Py_XDECREF(ste->ste_id);
     Py_XDECREF(ste->ste_name);
+    Py_XDECREF(ste->ste_function_name);
     Py_XDECREF(ste->ste_symbols);
     Py_XDECREF(ste->ste_varnames);
     Py_XDECREF(ste->ste_children);
@@ -2918,6 +2920,7 @@ symtable_visit_annotations(struct symtable *st, stmt_ty o, arguments_ty a, expr_
                               (void *)a, LOCATION(o))) {
         return 0;
     }
+    Py_XSETREF(st->st_cur->ste_function_name, Py_NewRef(function_ste->ste_name));
     if (is_in_class || current_type == ClassBlock) {
         st->st_cur->ste_can_see_class_scope = 1;
         if (!symtable_add_def(st, &_Py_ID(__classdict__), USE, LOCATION(o))) {