From 6bb58cbf879f0b30fb2557c71fefd3d66e1a90e8 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Tue, 21 May 2024 23:43:06 -0400 Subject: [PATCH 1/3] Fix leaky mangling --- Lib/importlib/_bootstrap_external.py | 3 ++- Lib/test/test_type_params.py | 33 ++++++++++++++++++++++++++++ Python/compile.c | 2 ++ Python/symtable.c | 4 +++- 4 files changed, 40 insertions(+), 2 deletions(-) diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index b3abf380a82b11..d7932af79f345a 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -473,6 +473,7 @@ def _write_atomic(path, data, mode=0o666): # Python 3.13a5 3569 (Specialize CONTAINS_OP) # Python 3.13a6 3570 (Add __firstlineno__ class attribute) # Python 3.14a1 3600 (Add LOAD_COMMON_CONSTANT) +# Python 3.14a1 3601 (Fix leaky name mangling in generic classes) # Python 3.15 will start with 3700 @@ -489,7 +490,7 @@ def _write_atomic(path, data, mode=0o666): # Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array # in PC/launcher.c must also be updated. -MAGIC_NUMBER = (3571).to_bytes(2, 'little') + b'\r\n' +MAGIC_NUMBER = (3601).to_bytes(2, 'little') + b'\r\n' _RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c diff --git a/Lib/test/test_type_params.py b/Lib/test/test_type_params.py index 82f1007f9ac97b..a28276fc791526 100644 --- a/Lib/test/test_type_params.py +++ b/Lib/test/test_type_params.py @@ -823,6 +823,39 @@ def meth[__U](self, arg: __T, arg2: __U): self.assertEqual(Foo.Alias.__value__, (T, V)) + def test_no_leaky_mangling_in_module(self): + ns = run_code(""" + __before = "before" + class X[T]: pass + __after = "after" + """) + self.assertEqual(ns["__before"], "before") + self.assertEqual(ns["__after"], "after") + + def test_no_leaky_mangling_in_function(self): + ns = run_code(""" + def f(): + class X[T]: pass + _X_foo = 2 + __foo = 1 + assert locals()['__foo'] == 1 + return __foo + """) + self.assertEqual(ns["f"](), 1) + + def test_no_leaky_mangling_in_class(self): + ns = run_code(""" + class Outer: + __before = "before" + class Inner[T]: + __x = "inner" + __after = "after" + """) + Outer = ns["Outer"] + self.assertEqual(Outer._Outer__before, "before") + self.assertEqual(Outer.Inner._Inner__x, "inner") + self.assertEqual(Outer._Outer__after, "after") + class TypeParamsComplexCallsTest(unittest.TestCase): def test_defaults(self): diff --git a/Python/compile.c b/Python/compile.c index 6dacfc7cb55aa6..7995436eea8075 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -2624,6 +2624,7 @@ compiler_class(struct compiler *c, stmt_ty s) asdl_type_param_seq *type_params = s->v.ClassDef.type_params; int is_generic = asdl_seq_LEN(type_params) > 0; + PyObject *old_u_private = Py_XNewRef(c->u->u_private); if (is_generic) { Py_XSETREF(c->u->u_private, Py_NewRef(s->v.ClassDef.name)); PyObject *type_params_name = PyUnicode_FromFormat("", @@ -2701,6 +2702,7 @@ compiler_class(struct compiler *c, stmt_ty s) s->v.ClassDef.bases, s->v.ClassDef.keywords)); } + Py_XSETREF(c->u->u_private, old_u_private); /* 6. apply decorators */ RETURN_IF_ERROR(compiler_apply_decorators(c, decos)); diff --git a/Python/symtable.c b/Python/symtable.c index 2ec21a2d376da2..ecb49248ca5d1e 100644 --- a/Python/symtable.c +++ b/Python/symtable.c @@ -1508,7 +1508,6 @@ symtable_enter_type_param_block(struct symtable *st, identifier name, lineno, col_offset, end_lineno, end_col_offset)) { return 0; } - st->st_private = name; // This is used for setting the generic base _Py_DECLARE_STR(generic_base, ".generic_base"); if (!symtable_add_def(st, &_Py_STR(generic_base), DEF_LOCAL, @@ -1673,6 +1672,7 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s) VISIT_QUIT(st, 0); if (s->v.ClassDef.decorator_list) VISIT_SEQ(st, expr, s->v.ClassDef.decorator_list); + PyObject *old_st_private = st->st_private; if (asdl_seq_LEN(s->v.ClassDef.type_params) > 0) { if (!symtable_enter_type_param_block(st, s->v.ClassDef.name, (void *)s->v.ClassDef.type_params, @@ -1680,6 +1680,7 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s) LOCATION(s))) { VISIT_QUIT(st, 0); } + st->st_private = s->v.ClassDef.name; VISIT_SEQ(st, type_param, s->v.ClassDef.type_params); } VISIT_SEQ(st, expr, s->v.ClassDef.bases); @@ -1709,6 +1710,7 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s) if (!symtable_exit_block(st)) VISIT_QUIT(st, 0); } + st->st_private = old_st_private; break; } case TypeAlias_kind: { From 3a4e38744053be906008cfcd8cb92fdc67e3b9a9 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Wed, 22 May 2024 05:51:27 -0700 Subject: [PATCH 2/3] blurb --- .../2024-05-22-05-51-23.gh-issue-119399.4zmHsV.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-05-22-05-51-23.gh-issue-119399.4zmHsV.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-05-22-05-51-23.gh-issue-119399.4zmHsV.rst b/Misc/NEWS.d/next/Core and Builtins/2024-05-22-05-51-23.gh-issue-119399.4zmHsV.rst new file mode 100644 index 00000000000000..1cd571133468c0 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-05-22-05-51-23.gh-issue-119399.4zmHsV.rst @@ -0,0 +1,2 @@ +Fix bug where any names appearing after a generic class were mangled as if +they were part of the generic class. From ac26abc1f69370b0aad60dcc7e3df9092b739815 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Thu, 23 May 2024 06:51:39 -0700 Subject: [PATCH 3/3] simplify --- Python/compile.c | 4 +--- Python/symtable.c | 7 ++----- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/Python/compile.c b/Python/compile.c index 7995436eea8075..e7d94814325a7c 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -2624,9 +2624,7 @@ compiler_class(struct compiler *c, stmt_ty s) asdl_type_param_seq *type_params = s->v.ClassDef.type_params; int is_generic = asdl_seq_LEN(type_params) > 0; - PyObject *old_u_private = Py_XNewRef(c->u->u_private); if (is_generic) { - Py_XSETREF(c->u->u_private, Py_NewRef(s->v.ClassDef.name)); PyObject *type_params_name = PyUnicode_FromFormat("", s->v.ClassDef.name); if (!type_params_name) { @@ -2637,6 +2635,7 @@ compiler_class(struct compiler *c, stmt_ty s) Py_DECREF(type_params_name); return ERROR; } + Py_XSETREF(c->u->u_private, Py_NewRef(s->v.ClassDef.name)); Py_DECREF(type_params_name); RETURN_IF_ERROR_IN_SCOPE(c, compiler_type_params(c, type_params)); _Py_DECLARE_STR(type_params, ".type_params"); @@ -2702,7 +2701,6 @@ compiler_class(struct compiler *c, stmt_ty s) s->v.ClassDef.bases, s->v.ClassDef.keywords)); } - Py_XSETREF(c->u->u_private, old_u_private); /* 6. apply decorators */ RETURN_IF_ERROR(compiler_apply_decorators(c, decos)); diff --git a/Python/symtable.c b/Python/symtable.c index ecb49248ca5d1e..8840055d77fc73 100644 --- a/Python/symtable.c +++ b/Python/symtable.c @@ -1667,12 +1667,11 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s) } break; case ClassDef_kind: { - PyObject *tmp; if (!symtable_add_def(st, s->v.ClassDef.name, DEF_LOCAL, LOCATION(s))) VISIT_QUIT(st, 0); if (s->v.ClassDef.decorator_list) VISIT_SEQ(st, expr, s->v.ClassDef.decorator_list); - PyObject *old_st_private = st->st_private; + PyObject *tmp = st->st_private; if (asdl_seq_LEN(s->v.ClassDef.type_params) > 0) { if (!symtable_enter_type_param_block(st, s->v.ClassDef.name, (void *)s->v.ClassDef.type_params, @@ -1689,7 +1688,6 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s) (void *)s, s->lineno, s->col_offset, s->end_lineno, s->end_col_offset)) VISIT_QUIT(st, 0); - tmp = st->st_private; st->st_private = s->v.ClassDef.name; if (asdl_seq_LEN(s->v.ClassDef.type_params) > 0) { if (!symtable_add_def(st, &_Py_ID(__type_params__), @@ -1703,14 +1701,13 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s) } } VISIT_SEQ(st, stmt, s->v.ClassDef.body); - st->st_private = tmp; if (!symtable_exit_block(st)) VISIT_QUIT(st, 0); if (asdl_seq_LEN(s->v.ClassDef.type_params) > 0) { if (!symtable_exit_block(st)) VISIT_QUIT(st, 0); } - st->st_private = old_st_private; + st->st_private = tmp; break; } case TypeAlias_kind: {