From 5502b386480773733f3d9ce6a7737694f8d822ad Mon Sep 17 00:00:00 2001 From: Pablo Galindo Date: Sun, 13 Oct 2019 23:39:46 +0100 Subject: [PATCH 1/4] bpo-38469: Handle named expression scope whith global/nonlocal keywords --- Lib/test/test_named_expressions.py | 48 +++++++++++++++++++ .../2019-10-13-23-41-38.bpo-38469.9kmuQj.rst | 2 + Python/symtable.c | 12 +++-- 3 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2019-10-13-23-41-38.bpo-38469.9kmuQj.rst diff --git a/Lib/test/test_named_expressions.py b/Lib/test/test_named_expressions.py index b1027ce78006dec..9aae51ccc38ef6f 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,52 @@ def test_named_expression_variable_reuse_in_comprehensions(self): 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 = None + [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 = None + [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 = None + [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 000000000000000..9cf89ab9ca71664 --- /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 present of the *global* keyword. Patch by Pablo Galindo. diff --git a/Python/symtable.c b/Python/symtable.c index f2453db69dd7dd0..5e238730e6b9a97 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); From 156ac1cf7b9e67bcf01700c6e5c097daf4c78168 Mon Sep 17 00:00:00 2001 From: Pablo Galindo Date: Mon, 14 Oct 2019 01:03:39 +0100 Subject: [PATCH 2/4] Don't initialize variables when is not needed --- Lib/test/test_named_expressions.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/Lib/test/test_named_expressions.py b/Lib/test/test_named_expressions.py index 9aae51ccc38ef6f..01e26c8dfaf259c 100644 --- a/Lib/test/test_named_expressions.py +++ b/Lib/test/test_named_expressions.py @@ -476,7 +476,6 @@ def test_named_expression_global_scope(self): global GLOBAL_VAR def f(): global GLOBAL_VAR - GLOBAL_VAR = None [GLOBAL_VAR := sentinel for _ in range(1)] self.assertEqual(GLOBAL_VAR, sentinel) try: @@ -500,7 +499,6 @@ def f(): nonlocal_var = None def g(): nonlocal nonlocal_var - nonlocal_var = None [nonlocal_var := sentinel for _ in range(1)] g() self.assertEqual(nonlocal_var, sentinel) @@ -511,7 +509,6 @@ def test_named_expression_nonlocal_scope_no_nonlocal_keyword(self): def f(): nonlocal_var = None def g(): - nonlocal_var = None [nonlocal_var := sentinel for _ in range(1)] g() self.assertEqual(nonlocal_var, None) From 44b71aa4f55174f5e60db582962dfe0cb9e8b61a Mon Sep 17 00:00:00 2001 From: Pablo Galindo Date: Mon, 14 Oct 2019 01:03:46 +0100 Subject: [PATCH 3/4] Fix style --- Python/symtable.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/symtable.c b/Python/symtable.c index 5e238730e6b9a97..b8713588b9a9149 100644 --- a/Python/symtable.c +++ b/Python/symtable.c @@ -1470,7 +1470,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); - if (target_in_scope & DEF_GLOBAL){ + if (target_in_scope & DEF_GLOBAL) { if (!symtable_add_def(st, target_name, DEF_GLOBAL)) VISIT_QUIT(st, 0); } else { From df21d120c2b19fc77366ae8431f97d6edcd8ba8b Mon Sep 17 00:00:00 2001 From: Pablo Galindo Date: Mon, 14 Oct 2019 04:55:35 +0100 Subject: [PATCH 4/4] Update Misc/NEWS.d/next/Core and Builtins/2019-10-13-23-41-38.bpo-38469.9kmuQj.rst Co-Authored-By: Nick Coghlan --- .../Core and Builtins/2019-10-13-23-41-38.bpo-38469.9kmuQj.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 9cf89ab9ca71664..328a1b70825afa8 100644 --- 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 @@ -1,2 +1,2 @@ Fixed a bug where the scope of named expressions was not being resolved -correctly in the present of the *global* keyword. Patch by Pablo Galindo. +correctly in the presence of the *global* keyword. Patch by Pablo Galindo.