diff --git a/Lib/test/test_named_expression.py b/Lib/test/test_named_expression.py index cb91b140d..401fc791e 100644 --- a/Lib/test/test_named_expression.py +++ b/Lib/test/test_named_expression.py @@ -6,120 +6,116 @@ GLOBAL_VAR = None class NamedExpressionInvalidTest(unittest.TestCase): + # Test modified to accept syntax error with deviating message def test_named_expression_invalid_01(self): code = """x := 0""" - #with self.assertRaisesRegex(SyntaxError, "invalid syntax"): - with self.assertRaises(SyntaxError): # TODO RustPython + with self.assertRaises(SyntaxError): exec(code, {}, {}) + # Test modified to accept syntax error with deviating message def test_named_expression_invalid_02(self): code = """x = y := 0""" - #with self.assertRaisesRegex(SyntaxError, "invalid syntax"): - with self.assertRaises(SyntaxError): # TODO RustPython + with self.assertRaises(SyntaxError): exec(code, {}, {}) + # Test modified to accept syntax error with deviating message def test_named_expression_invalid_03(self): code = """y := f(x)""" - #with self.assertRaisesRegex(SyntaxError, "invalid syntax"): - with self.assertRaises(SyntaxError): # TODO RustPython + with self.assertRaises(SyntaxError): exec(code, {}, {}) + # Test modified to accept syntax error with deviating message def test_named_expression_invalid_04(self): code = """y0 = y1 := f(x)""" - #with self.assertRaisesRegex(SyntaxError, "invalid syntax"): - with self.assertRaises(SyntaxError): # TODO RustPython + with self.assertRaises(SyntaxError): exec(code, {}, {}) + # Test modified to accept syntax error with deviating message def test_named_expression_invalid_06(self): code = """((a, b) := (1, 2))""" - #with self.assertRaisesRegex(SyntaxError, "cannot use assignment expressions with tuple"): - with self.assertRaises(SyntaxError): # TODO RustPython + with self.assertRaises(SyntaxError): exec(code, {}, {}) + # Test modified to accept syntax error with deviating message def test_named_expression_invalid_07(self): code = """def spam(a = b := 42): pass""" - #with self.assertRaisesRegex(SyntaxError, "invalid syntax"): - with self.assertRaises(SyntaxError): # TODO RustPython + with self.assertRaises(SyntaxError): exec(code, {}, {}) + # Test modified to accept syntax error with deviating message def test_named_expression_invalid_08(self): code = """def spam(a: b := 42 = 5): pass""" - #with self.assertRaisesRegex(SyntaxError, "invalid syntax"): - with self.assertRaises(SyntaxError): # TODO RustPython + with self.assertRaises(SyntaxError): exec(code, {}, {}) + # Test modified to accept syntax error with deviating message def test_named_expression_invalid_09(self): code = """spam(a=b := 'c')""" - #with self.assertRaisesRegex(SyntaxError, "invalid syntax"): - with self.assertRaises(SyntaxError): # TODO RustPython + with self.assertRaises(SyntaxError): exec(code, {}, {}) + # Test modified to accept syntax error with deviating message def test_named_expression_invalid_10(self): code = """spam(x = y := f(x))""" - #with self.assertRaisesRegex(SyntaxError, "invalid syntax"): - with self.assertRaises(SyntaxError): # TODO RustPython + with self.assertRaises(SyntaxError): exec(code, {}, {}) + # Test modified to accept syntax error with deviating message def test_named_expression_invalid_11(self): code = """spam(a=1, b := 2)""" - #with self.assertRaisesRegex(SyntaxError, - # "positional argument follows keyword argument"): - with self.assertRaises(SyntaxError): # TODO RustPython + with self.assertRaises(SyntaxError): exec(code, {}, {}) + # Test modified to accept syntax error with deviating message def test_named_expression_invalid_12(self): code = """spam(a=1, (b := 2))""" - #with self.assertRaisesRegex(SyntaxError, - # "positional argument follows keyword argument"): - with self.assertRaises(SyntaxError): # TODO RustPython + with self.assertRaises(SyntaxError): exec(code, {}, {}) + # Test modified to accept syntax error with deviating message def test_named_expression_invalid_13(self): code = """spam(a=1, (b := 2))""" - #with self.assertRaisesRegex(SyntaxError, - # "positional argument follows keyword argument"): - with self.assertRaises(SyntaxError): # TODO RustPython + with self.assertRaises(SyntaxError): exec(code, {}, {}) + # Test modified to accept syntax error with deviating message def test_named_expression_invalid_14(self): code = """(x := lambda: y := 1)""" - #with self.assertRaisesRegex(SyntaxError, "invalid syntax"): - with self.assertRaises(SyntaxError): # TODO RustPython + with self.assertRaises(SyntaxError): exec(code, {}, {}) + # Test modified to accept syntax error with deviating message def test_named_expression_invalid_15(self): code = """(lambda: x := 1)""" - #with self.assertRaisesRegex(SyntaxError, - # "cannot use assignment expressions with lambda"): - with self.assertRaises(SyntaxError): # TODO RustPython + with self.assertRaises(SyntaxError): exec(code, {}, {}) + # Test modified to accept syntax error with deviating message def test_named_expression_invalid_16(self): code = "[i + 1 for i in i := [1,2]]" - #with self.assertRaisesRegex(SyntaxError, "invalid syntax"): - with self.assertRaises(SyntaxError): # TODO RustPython + with self.assertRaises(SyntaxError): exec(code, {}, {}) + # Test modified to accept syntax error with deviating message def test_named_expression_invalid_17(self): code = "[i := 0, j := 1 for i, j in [(1, 2), (3, 4)]]" - #with self.assertRaisesRegex(SyntaxError, "invalid syntax"): - with self.assertRaises(SyntaxError): # TODO RustPython + with self.assertRaises(SyntaxError): exec(code, {}, {}) def test_named_expression_invalid_in_class_body(self): @@ -131,6 +127,7 @@ class NamedExpressionInvalidTest(unittest.TestCase): "assignment expression within a comprehension cannot be used in a class body"): exec(code, {}, {}) + # Test modified to accept syntax error with deviating message def test_named_expression_invalid_rebinding_comprehension_iteration_variable(self): cases = [ ("Local reuse", 'i', "[i := 0 for i in range(5)]"), @@ -143,18 +140,17 @@ class NamedExpressionInvalidTest(unittest.TestCase): "[(i, j) for i in range(5) for j in range(5) if True or (i:=10)]"), ] for case, target, code in cases: - msg = f"assignment expression cannot rebind comprehension iteration variable '{target}'" with self.subTest(case=case): with self.assertRaises(SyntaxError): exec(code, {}, {}) + # Test modified to accept syntax error with deviating message def test_named_expression_invalid_rebinding_comprehension_inner_loop(self): cases = [ ("Inner reuse", 'j', "[i for i in range(5) if (j := 0) for j in range(5)]"), ("Inner unpacking reuse", 'j', "[i for i in range(5) if (j := 0) for j, k in [(0, 1)]]"), ] for case, target, code in cases: - #msg = f"comprehension inner loop cannot rebind assignment expression target '{target}'" with self.subTest(case=case): with self.assertRaises(SyntaxError): exec(code, {}) # Module scope @@ -163,6 +159,7 @@ class NamedExpressionInvalidTest(unittest.TestCase): with self.assertRaises(SyntaxError): exec(f"lambda: {code}", {}) # Function scope + # Test modified to accept syntax error with deviating message def test_named_expression_invalid_comprehension_iterable_expression(self): cases = [ ("Top level", "[i for i in (i := range(5))]"), @@ -175,7 +172,6 @@ class NamedExpressionInvalidTest(unittest.TestCase): ("Nested comprehension condition", "[i for i in [j for j in range(5) if (j := True)]]"), ("Nested comprehension body", "[i for i in [(j := True) for j in range(5)]]"), ] - #msg = "assignment expression cannot be used in a comprehension iterable expression" for case, code in cases: with self.subTest(case=case): with self.assertRaises(SyntaxError): @@ -339,10 +335,10 @@ print(a)""" self.assertEqual(res, [(1, 1, 1.0), (2, 2, 1.0), (3, 3, 1.0)]) self.assertEqual(y, 3) - # TODO RustPython, - @unittest.expectedFailure # TODO RustPython + # TODO RustPython, added test_named_expression_scope_06_rp_modified as + # minimaly modified variant of this test. + @unittest.expectedFailure def test_named_expression_scope_06(self): - #spam=1000 res = [[spam := i for i in range(3)] for j in range(2)] self.assertEqual(res, [[0, 1, 2], [0, 1, 2]]) @@ -388,7 +384,7 @@ print(a)""" self.assertEqual(res, [0, 2]) self.assertEqual(a, 2) - # TODO RustPython, + # TODO RustPython, added test_named_expression_scope_10_rp_modified @unittest.expectedFailure def test_named_expression_scope_10(self): res = [b := [a := 1 for i in range(2)] for j in range(2)] diff --git a/compiler/src/symboltable.rs b/compiler/src/symboltable.rs index 2d71ea0d4..e7bd6f0c8 100644 --- a/compiler/src/symboltable.rs +++ b/compiler/src/symboltable.rs @@ -271,74 +271,6 @@ impl<'a> SymbolTableAnalyzer<'a> { } } Ok(()) - - /*let mut handled=false; - if curr_st_typ == SymbolTableType::Comprehension - { - if symbol.is_assign_namedexpr_in_comprehension { - // propagate symbol to next higher level that can hold it, - // i.e., function or module. Comprehension is skipped and - // Class is not allowed and detected as error. - //symbol.scope = SymbolScope::Nonlocal; - handled = true; - self.analyze_symbol_comprehension(symbol, 0)? - } - if symbol.is_iter { - if let Some(parent_symbol) = self.tables.last().unwrap().0.get(&symbol.name) { - if parent_symbol.is_assigned { - return Err(SymbolTableError { - error: format!("assignment expression cannot rebind comprehension iteration variable {}", symbol.name), - location: Default::default(), - }); - } - } - } - } - - if !handled { - match symbol.scope { - SymbolScope::Nonlocal => { - let scope_depth = self.tables.len(); - if scope_depth > 0 { - // check if the name is already defined in any outer scope - // therefore - if scope_depth < 2 - || !self - .tables - .iter() - .skip(1) // omit the global scope as it is not non-local - .rev() // revert the order for better performance - .any(|t| t.0.contains_key(&symbol.name)) - // true when any of symbol tables contains the name -> then negate - { - return Err(SymbolTableError { - error: format!("no binding for nonlocal '{}' found", symbol.name), - location: Default::default(), - }); - } - } else { - return Err(SymbolTableError { - error: format!( - "nonlocal {} defined at place without an enclosing scope", - symbol.name - ), - location: Default::default(), - }); - } - } - SymbolScope::Global => { - // TODO: add more checks for globals? - } - SymbolScope::Local => { - // all is well - } - SymbolScope::Unknown => { - // Try hard to figure out what the scope of this symbol is. - self.analyze_unknown_symbol(symbol); - } - } - } - Ok(())*/ } fn analyze_unknown_symbol(&self, symbol: &mut Symbol) { @@ -381,9 +313,7 @@ impl<'a> SymbolTableAnalyzer<'a> { let table_type = last.1; // it is not allowed to use an iterator variable as assignee in a named expression - if - /*symbol.is_assign_namedexpr_in_comprehension && (maybe this could be asserted) */ - symbol.is_iter { + if symbol.is_iter { return Err(SymbolTableError { error: format!( "assignment expression cannot rebind comprehension iteration variable {}", @@ -903,7 +833,7 @@ impl SymbolTableBuilder { // named expressions are not allowed in the definiton of // comprehension iterator definitions if let ExpressionContext::IterDefinitionExp = *context { - return Err(SymbolTableError { + return Err(SymbolTableError { error: "assignment expression cannot be used in a comprehension iterable expression".to_string(), location: Default::default(), });