Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[3.11] gh-96497: Mangle name before symtable lookup in 'symtable_extend_namedexpr_scope' (GH-96561) #115604

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
22 changes: 22 additions & 0 deletions Lib/test/test_named_expressions.py
Expand Up @@ -222,6 +222,16 @@ def test_named_expression_invalid_set_comprehension_iterable_expression(self):
with self.assertRaisesRegex(SyntaxError, msg):
exec(f"lambda: {code}", {}) # Function scope

def test_named_expression_invalid_mangled_class_variables(self):
code = """class Foo:
def bar(self):
[[(__x:=2) for _ in range(2)] for __x in range(2)]
"""

with self.assertRaisesRegex(SyntaxError,
"assignment expression cannot rebind comprehension iteration variable '__x'"):
exec(code, {}, {})


class NamedExpressionAssignmentTest(unittest.TestCase):

Expand Down Expand Up @@ -598,6 +608,18 @@ def test_named_expression_scope_in_genexp(self):
for idx, elem in enumerate(genexp):
self.assertEqual(elem, b[idx] + a)

def test_named_expression_scope_mangled_names(self):
class Foo:
def f(self_):
global __x1
__x1 = 0
[_Foo__x1 := 1 for a in [2]]
self.assertEqual(__x1, 1)
[__x1 := 2 for a in [3]]
self.assertEqual(__x1, 2)

Foo().f()
self.assertEqual(_Foo__x1, 2)

if __name__ == "__main__":
unittest.main()
@@ -0,0 +1,2 @@
Fix incorrect resolution of mangled class variables used in assignment
expressions in comprehensions.
14 changes: 10 additions & 4 deletions Python/symtable.c
Expand Up @@ -1042,16 +1042,22 @@ symtable_enter_block(struct symtable *st, identifier name, _Py_block_ty block,
}

static long
symtable_lookup(struct symtable *st, PyObject *name)
symtable_lookup_entry(struct symtable *st, PySTEntryObject *ste, PyObject *name)
{
PyObject *mangled = _Py_Mangle(st->st_private, name);
if (!mangled)
return 0;
long ret = _PyST_GetSymbol(st->st_cur, mangled);
long ret = _PyST_GetSymbol(ste, mangled);
Py_DECREF(mangled);
return ret;
}

static long
symtable_lookup(struct symtable *st, PyObject *name)
{
return symtable_lookup_entry(st, st->st_cur, name);
}

static int
symtable_add_def_helper(struct symtable *st, PyObject *name, int flag, struct _symtable_entry *ste,
int lineno, int col_offset, int end_lineno, int end_col_offset)
Expand Down Expand Up @@ -1525,7 +1531,7 @@ symtable_extend_namedexpr_scope(struct symtable *st, expr_ty e)
* binding conflict with iteration variables, otherwise skip it
*/
if (ste->ste_comprehension) {
long target_in_scope = _PyST_GetSymbol(ste, target_name);
long target_in_scope = symtable_lookup_entry(st, ste, target_name);
if (target_in_scope & DEF_COMP_ITER) {
PyErr_Format(PyExc_SyntaxError, NAMED_EXPR_COMP_CONFLICT, target_name);
PyErr_RangedSyntaxLocationObject(st->st_filename,
Expand All @@ -1540,7 +1546,7 @@ symtable_extend_namedexpr_scope(struct symtable *st, expr_ty e)

/* If we find a FunctionBlock entry, add as GLOBAL/LOCAL or NONLOCAL/LOCAL */
if (ste->ste_type == FunctionBlock) {
long target_in_scope = _PyST_GetSymbol(ste, target_name);
long target_in_scope = symtable_lookup_entry(st, ste, target_name);
if (target_in_scope & DEF_GLOBAL) {
if (!symtable_add_def(st, target_name, DEF_GLOBAL, LOCATION(e)))
VISIT_QUIT(st, 0);
Expand Down