Skip to content

Commit f153140

Browse files
Explicitly disallow assignment expressions to a name inside parentheses, e.g.: ((x) := 0)
- Add check for LHS types to detect a parenthesis then a name (see note) - Add test for this scenario - Update tests for changed error message for named assignment to a tuple (also, see note) Note: This caused issues with the previous error handling for named assignment to a LHS that contained an expression, such as a tuple. Thus, the check for the LHS of a named expression must be changed to be more specific if we wish to maintain the previous error messages
1 parent d7e57a4 commit f153140

File tree

2 files changed

+58
-2
lines changed

2 files changed

+58
-2
lines changed

‎Lib/test/test_named_expressions.py‎

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@ def test_named_expression_invalid_01(self):
1010
with self.assertRaisesRegex(SyntaxError, "invalid syntax"):
1111
exec(code, {}, {})
1212

13+
def test_named_expression_invalid_01(self):
14+
code = """((x) := 0)"""
15+
16+
with self.assertRaisesRegex(SyntaxError,
17+
"can't use named assignment with expression"):
18+
exec(code, {}, {})
19+
1320
def test_named_expression_invalid_02(self):
1421
code = """x = y := 0"""
1522

@@ -28,10 +35,16 @@ def test_named_expression_invalid_04(self):
2835
with self.assertRaisesRegex(SyntaxError, "invalid syntax"):
2936
exec(code, {}, {})
3037

38+
# XXX this error message now follows the error message you get with
39+
# keyword args:
40+
# >>> spam((1,2)=1)
41+
# File "<stdin>", line 1
42+
# SyntaxError: keyword can't be an expression
3143
def test_named_expression_invalid_06(self):
3244
code = """((a, b) := (1, 2))"""
3345

34-
with self.assertRaisesRegex(SyntaxError, "can't use named assignment with tuple"):
46+
with self.assertRaisesRegex(SyntaxError,
47+
"can't use named assignment with expression"):
3548
exec(code, {}, {})
3649

3750
def test_named_expression_invalid_07(self):

‎Python/ast.c‎

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1715,8 +1715,51 @@ ast_for_namedexpr(struct compiling *c, const node *n)
17151715
'*' test )
17161716
*/
17171717
expr_ty target, value;
1718+
node *ch = CHILD(n, 0);
1719+
1720+
// To remain LL(1), the grammar accepts any test (basically, any
1721+
// expression) in the keyword slot of a call site. So, we need
1722+
// to manually enforce that the keyword is a NAME here.
1723+
static const int name_tree[] = {
1724+
namedexpr_test,
1725+
test,
1726+
or_test,
1727+
and_test,
1728+
not_test,
1729+
comparison,
1730+
expr,
1731+
xor_expr,
1732+
and_expr,
1733+
shift_expr,
1734+
arith_expr,
1735+
term,
1736+
factor,
1737+
power,
1738+
atom_expr,
1739+
atom,
1740+
};
1741+
1742+
node *expr_node = ch;
1743+
for (int i = 1; name_tree[i]; i++) {
1744+
if (TYPE(expr_node) != name_tree[i])
1745+
break;
1746+
if (TYPE(expr_node) == atom && expr_node->n_nchildren == 3) {
1747+
node *expr_node_ch = CHILD(CHILD(expr_node, 1), 0);
1748+
for (int i = 0; name_tree[i]; i++) {
1749+
if (TYPE(expr_node_ch) != name_tree[i])
1750+
break;
1751+
expr_node_ch = CHILD(expr_node_ch, 0);
1752+
}
1753+
if (TYPE(expr_node_ch) == NAME) {
1754+
ast_error(c, ch,
1755+
"can't use named assignment with expression");
1756+
return NULL;
1757+
}
1758+
}
1759+
expr_node = CHILD(expr_node, 0);
1760+
}
17181761

1719-
target = ast_for_expr(c, CHILD(n, 0));
1762+
target = ast_for_expr(c, ch);
17201763
if (!target)
17211764
return NULL;
17221765

0 commit comments

Comments
 (0)