Skip to content

Commit 927f07c

Browse files
miss-islingtonpablogsal
authored andcommitted
bpo-38469: Handle named expression scope with global/nonlocal keywords (GH-16755) (#16760)
The symbol table handing of PEP572's assignment expressions is not resolving correctly the scope of some variables in presence of global/nonlocal keywords in conjunction with comprehensions. (cherry picked from commit fd5c414) Co-authored-by: Pablo Galindo <[email protected]>
1 parent ac53ba6 commit 927f07c

File tree

3 files changed

+56
-3
lines changed

3 files changed

+56
-3
lines changed

Lib/test/test_named_expressions.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import os
22
import unittest
33

4+
GLOBAL_VAR = None
45

56
class NamedExpressionInvalidTest(unittest.TestCase):
67

@@ -470,5 +471,49 @@ def test_named_expression_variable_reuse_in_comprehensions(self):
470471
self.assertEqual(ns["x"], 2)
471472
self.assertEqual(ns["result"], [0, 1, 2])
472473

474+
def test_named_expression_global_scope(self):
475+
sentinel = object()
476+
global GLOBAL_VAR
477+
def f():
478+
global GLOBAL_VAR
479+
[GLOBAL_VAR := sentinel for _ in range(1)]
480+
self.assertEqual(GLOBAL_VAR, sentinel)
481+
try:
482+
f()
483+
self.assertEqual(GLOBAL_VAR, sentinel)
484+
finally:
485+
GLOBAL_VAR = None
486+
487+
def test_named_expression_global_scope_no_global_keyword(self):
488+
sentinel = object()
489+
def f():
490+
GLOBAL_VAR = None
491+
[GLOBAL_VAR := sentinel for _ in range(1)]
492+
self.assertEqual(GLOBAL_VAR, sentinel)
493+
f()
494+
self.assertEqual(GLOBAL_VAR, None)
495+
496+
def test_named_expression_nonlocal_scope(self):
497+
sentinel = object()
498+
def f():
499+
nonlocal_var = None
500+
def g():
501+
nonlocal nonlocal_var
502+
[nonlocal_var := sentinel for _ in range(1)]
503+
g()
504+
self.assertEqual(nonlocal_var, sentinel)
505+
f()
506+
507+
def test_named_expression_nonlocal_scope_no_nonlocal_keyword(self):
508+
sentinel = object()
509+
def f():
510+
nonlocal_var = None
511+
def g():
512+
[nonlocal_var := sentinel for _ in range(1)]
513+
g()
514+
self.assertEqual(nonlocal_var, None)
515+
f()
516+
517+
473518
if __name__ == "__main__":
474519
unittest.main()
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fixed a bug where the scope of named expressions was not being resolved
2+
correctly in the presence of the *global* keyword. Patch by Pablo Galindo.

Python/symtable.c

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1467,10 +1467,16 @@ symtable_extend_namedexpr_scope(struct symtable *st, expr_ty e)
14671467
continue;
14681468
}
14691469

1470-
/* If we find a FunctionBlock entry, add as NONLOCAL/LOCAL */
1470+
/* If we find a FunctionBlock entry, add as GLOBAL/LOCAL or NONLOCAL/LOCAL */
14711471
if (ste->ste_type == FunctionBlock) {
1472-
if (!symtable_add_def(st, target_name, DEF_NONLOCAL))
1473-
VISIT_QUIT(st, 0);
1472+
long target_in_scope = _PyST_GetSymbol(ste, target_name);
1473+
if (target_in_scope & DEF_GLOBAL) {
1474+
if (!symtable_add_def(st, target_name, DEF_GLOBAL))
1475+
VISIT_QUIT(st, 0);
1476+
} else {
1477+
if (!symtable_add_def(st, target_name, DEF_NONLOCAL))
1478+
VISIT_QUIT(st, 0);
1479+
}
14741480
if (!symtable_record_directive(st, target_name, e->lineno, e->col_offset))
14751481
VISIT_QUIT(st, 0);
14761482

0 commit comments

Comments
 (0)