summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>2019-10-14 11:40:15 (GMT)
committerƁukasz Langa <lukasz@langa.pl>2019-10-14 11:40:15 (GMT)
commit927f07c816aad9f8ed961d7ace6a367837f7fa8f (patch)
tree6fdf864d74436422335c89ebdff2f3afbc66866f
parentac53ba6c7c0c43100bab8bc427d54563d9b65a0e (diff)
downloadcpython-927f07c816aad9f8ed961d7ace6a367837f7fa8f.zip
cpython-927f07c816aad9f8ed961d7ace6a367837f7fa8f.tar.gz
cpython-927f07c816aad9f8ed961d7ace6a367837f7fa8f.tar.bz2
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 fd5c414880b2e05720b9cf14ab0b0d7ae2b7d925) Co-authored-by: Pablo Galindo <Pablogsal@gmail.com>
-rw-r--r--Lib/test/test_named_expressions.py45
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2019-10-13-23-41-38.bpo-38469.9kmuQj.rst2
-rw-r--r--Python/symtable.c12
3 files changed, 56 insertions, 3 deletions
diff --git a/Lib/test/test_named_expressions.py b/Lib/test/test_named_expressions.py
index b1027ce..01e26c8 100644
--- a/Lib/test/test_named_expressions.py
+++ b/Lib/test/test_named_expressions.py
@@ -1,6 +1,7 @@
import os
import unittest
+GLOBAL_VAR = None
class NamedExpressionInvalidTest(unittest.TestCase):
@@ -470,5 +471,49 @@ spam()"""
self.assertEqual(ns["x"], 2)
self.assertEqual(ns["result"], [0, 1, 2])
+ def test_named_expression_global_scope(self):
+ sentinel = object()
+ global GLOBAL_VAR
+ def f():
+ global GLOBAL_VAR
+ [GLOBAL_VAR := sentinel for _ in range(1)]
+ self.assertEqual(GLOBAL_VAR, sentinel)
+ try:
+ f()
+ self.assertEqual(GLOBAL_VAR, sentinel)
+ finally:
+ GLOBAL_VAR = None
+
+ def test_named_expression_global_scope_no_global_keyword(self):
+ sentinel = object()
+ def f():
+ GLOBAL_VAR = None
+ [GLOBAL_VAR := sentinel for _ in range(1)]
+ self.assertEqual(GLOBAL_VAR, sentinel)
+ f()
+ self.assertEqual(GLOBAL_VAR, None)
+
+ def test_named_expression_nonlocal_scope(self):
+ sentinel = object()
+ def f():
+ nonlocal_var = None
+ def g():
+ nonlocal nonlocal_var
+ [nonlocal_var := sentinel for _ in range(1)]
+ g()
+ self.assertEqual(nonlocal_var, sentinel)
+ f()
+
+ def test_named_expression_nonlocal_scope_no_nonlocal_keyword(self):
+ sentinel = object()
+ def f():
+ nonlocal_var = None
+ def g():
+ [nonlocal_var := sentinel for _ in range(1)]
+ g()
+ self.assertEqual(nonlocal_var, None)
+ f()
+
+
if __name__ == "__main__":
unittest.main()
diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-10-13-23-41-38.bpo-38469.9kmuQj.rst b/Misc/NEWS.d/next/Core and Builtins/2019-10-13-23-41-38.bpo-38469.9kmuQj.rst
new file mode 100644
index 0000000..328a1b7
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2019-10-13-23-41-38.bpo-38469.9kmuQj.rst
@@ -0,0 +1,2 @@
+Fixed a bug where the scope of named expressions was not being resolved
+correctly in the presence of the *global* keyword. Patch by Pablo Galindo.
diff --git a/Python/symtable.c b/Python/symtable.c
index f2453db..b871358 100644
--- a/Python/symtable.c
+++ b/Python/symtable.c
@@ -1467,10 +1467,16 @@ symtable_extend_namedexpr_scope(struct symtable *st, expr_ty e)
continue;
}
- /* If we find a FunctionBlock entry, add as NONLOCAL/LOCAL */
+ /* If we find a FunctionBlock entry, add as GLOBAL/LOCAL or NONLOCAL/LOCAL */
if (ste->ste_type == FunctionBlock) {
- if (!symtable_add_def(st, target_name, DEF_NONLOCAL))
- VISIT_QUIT(st, 0);
+ long target_in_scope = _PyST_GetSymbol(ste, target_name);
+ if (target_in_scope & DEF_GLOBAL) {
+ if (!symtable_add_def(st, target_name, DEF_GLOBAL))
+ VISIT_QUIT(st, 0);
+ } else {
+ if (!symtable_add_def(st, target_name, DEF_NONLOCAL))
+ VISIT_QUIT(st, 0);
+ }
if (!symtable_record_directive(st, target_name, e->lineno, e->col_offset))
VISIT_QUIT(st, 0);