gh-137814: Fix __qualname__ of __annotate__ (#137842)

This commit is contained in:
Jelle Zijlstra
2026-04-15 21:52:30 -07:00
committed by GitHub
parent 54607eec34
commit 5b8cd314e2
6 changed files with 38 additions and 1 deletions
+2 -1
View 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 \
+1
View 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 */
+17
View 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 = [
@@ -0,0 +1,2 @@
Fix the ``__qualname__`` attribute of ``__annotate__`` functions on
functions.
+13
View 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) {
+3
View 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))) {