Skip to content

Commit c9a71dd

Browse files
authored
closes bpo-34641: Further restrict the LHS of keyword argument function call syntax. (GH-9212)
1 parent 6d9767f commit c9a71dd

File tree

4 files changed

+50
-14
lines changed

4 files changed

+50
-14
lines changed

Doc/whatsnew/3.8.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@ Other Language Changes
9494
* Added support of ``\N{name}`` escapes in :mod:`regular expressions <re>`.
9595
(Contributed by Jonathan Eunice and Serhiy Storchaka in :issue:`30688`.)
9696

97+
* The syntax allowed for keyword names in function calls was further
98+
restricted. In particular, ``f((keyword)=arg)`` is no longer allowed. It was
99+
never intended to permit more than a bare name on the left-hand side of a
100+
keyword argument assignment term. See :issue:`34641`.
97101

98102
New Modules
99103
===========

Lib/test/test_syntax.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,9 @@
269269
>>> f(x.y=1)
270270
Traceback (most recent call last):
271271
SyntaxError: keyword can't be an expression
272+
>>> f((x)=2)
273+
Traceback (most recent call last):
274+
SyntaxError: keyword can't be an expression
272275
273276
274277
More set_context():
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Further restrict the syntax of the left-hand side of keyword arguments in
2+
function calls. In particular, ``f((keyword)=arg)`` is now disallowed.

Python/ast.c

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2815,29 +2815,56 @@ ast_for_call(struct compiling *c, const node *n, expr_ty func, bool allowgen)
28152815
identifier key, tmp;
28162816
int k;
28172817

2818-
/* chch is test, but must be an identifier? */
2819-
e = ast_for_expr(c, chch);
2820-
if (!e)
2821-
return NULL;
2822-
/* f(lambda x: x[0] = 3) ends up getting parsed with
2823-
* LHS test = lambda x: x[0], and RHS test = 3.
2824-
* SF bug 132313 points out that complaining about a keyword
2825-
* then is very confusing.
2826-
*/
2827-
if (e->kind == Lambda_kind) {
2818+
// To remain LL(1), the grammar accepts any test (basically, any
2819+
// expression) in the keyword slot of a call site. So, we need
2820+
// to manually enforce that the keyword is a NAME here.
2821+
static const int name_tree[] = {
2822+
test,
2823+
or_test,
2824+
and_test,
2825+
not_test,
2826+
comparison,
2827+
expr,
2828+
xor_expr,
2829+
and_expr,
2830+
shift_expr,
2831+
arith_expr,
2832+
term,
2833+
factor,
2834+
power,
2835+
atom_expr,
2836+
atom,
2837+
0,
2838+
};
2839+
node *expr_node = chch;
2840+
for (int i = 0; name_tree[i]; i++) {
2841+
if (TYPE(expr_node) != name_tree[i])
2842+
break;
2843+
if (NCH(expr_node) != 1)
2844+
break;
2845+
expr_node = CHILD(expr_node, 0);
2846+
}
2847+
if (TYPE(expr_node) == lambdef) {
2848+
// f(lambda x: x[0] = 3) ends up getting parsed with LHS
2849+
// test = lambda x: x[0], and RHS test = 3. Issue #132313
2850+
// points out that complaining about a keyword then is very
2851+
// confusing.
28282852
ast_error(c, chch,
28292853
"lambda cannot contain assignment");
28302854
return NULL;
28312855
}
2832-
else if (e->kind != Name_kind) {
2856+
else if (TYPE(expr_node) != NAME) {
28332857
ast_error(c, chch,
2834-
"keyword can't be an expression");
2858+
"keyword can't be an expression");
2859+
return NULL;
2860+
}
2861+
key = new_identifier(STR(expr_node), c);
2862+
if (key == NULL) {
28352863
return NULL;
28362864
}
2837-
else if (forbidden_name(c, e->v.Name.id, ch, 1)) {
2865+
if (forbidden_name(c, key, chch, 1)) {
28382866
return NULL;
28392867
}
2840-
key = e->v.Name.id;
28412868
for (k = 0; k < nkeywords; k++) {
28422869
tmp = ((keyword_ty)asdl_seq_GET(keywords, k))->arg;
28432870
if (tmp && !PyUnicode_Compare(tmp, key)) {

0 commit comments

Comments
 (0)