Skip to content

Commit ce21cfc

Browse files
bpo-40618: Disallow invalid targets in augassign and except clauses (GH-20083)
This commit fixes the new parser to disallow invalid targets in the following scenarios: - Augmented assignments must only accept a single target (Name, Attribute or Subscript), but no tuples or lists. - `except` clauses should only accept a single `Name` as a target. Co-authored-by: Pablo Galindo <Pablogsal@gmail.com>
1 parent bcc3036 commit ce21cfc

File tree

5 files changed

+82
-46
lines changed

5 files changed

+82
-46
lines changed

Diff for: Grammar/python.gram

+8-9
Original file line numberDiff line numberDiff line change
@@ -89,12 +89,12 @@ assignment[stmt_ty]:
8989
"Variable annotation syntax is",
9090
_Py_AnnAssign(CHECK(_PyPegen_set_expr_context(p, a, Store)), b, c, 1, EXTRA)
9191
) }
92-
| a=('(' b=inside_paren_ann_assign_target ')' { b }
93-
| ann_assign_subscript_attribute_target) ':' b=expression c=['=' d=annotated_rhs { d }] {
92+
| a=('(' b=single_target ')' { b }
93+
| single_subscript_attribute_target) ':' b=expression c=['=' d=annotated_rhs { d }] {
9494
CHECK_VERSION(6, "Variable annotations syntax is", _Py_AnnAssign(a, b, c, 0, EXTRA)) }
9595
| a=(z=star_targets '=' { z })+ b=(yield_expr | star_expressions) tc=[TYPE_COMMENT] {
9696
_Py_Assign(a, b, NEW_TYPE_COMMENT(p, tc), EXTRA) }
97-
| a=target b=augassign c=(yield_expr | star_expressions) {
97+
| a=single_target b=augassign c=(yield_expr | star_expressions) {
9898
_Py_AugAssign(a, b->kind, c, EXTRA) }
9999
| invalid_assignment
100100

@@ -185,7 +185,7 @@ try_stmt[stmt_ty]:
185185
| 'try' ':' b=block f=finally_block { _Py_Try(b, NULL, NULL, f, EXTRA) }
186186
| 'try' ':' b=block ex=except_block+ el=[else_block] f=[finally_block] { _Py_Try(b, ex, el, f, EXTRA) }
187187
except_block[excepthandler_ty]:
188-
| 'except' e=expression t=['as' z=target { z }] ':' b=block {
188+
| 'except' e=expression t=['as' z=NAME { z }] ':' b=block {
189189
_Py_ExceptHandler(e, (t) ? ((expr_ty) t)->v.Name.id : NULL, b, EXTRA) }
190190
| 'except' ':' b=block { _Py_ExceptHandler(NULL, NULL, b, EXTRA) }
191191
finally_block[asdl_seq*]: 'finally' ':' a=block { a }
@@ -573,12 +573,11 @@ star_atom[expr_ty]:
573573
| '(' a=[star_targets_seq] ')' { _Py_Tuple(a, Store, EXTRA) }
574574
| '[' a=[star_targets_seq] ']' { _Py_List(a, Store, EXTRA) }
575575

576-
inside_paren_ann_assign_target[expr_ty]:
577-
| ann_assign_subscript_attribute_target
576+
single_target[expr_ty]:
577+
| single_subscript_attribute_target
578578
| a=NAME { _PyPegen_set_expr_context(p, a, Store) }
579-
| '(' a=inside_paren_ann_assign_target ')' { a }
580-
581-
ann_assign_subscript_attribute_target[expr_ty]:
579+
| '(' a=single_target ')' { a }
580+
single_subscript_attribute_target[expr_ty]:
582581
| a=t_primary '.' b=NAME !t_lookahead { _Py_Attribute(a, b->v.Name.id, Store, EXTRA) }
583582
| a=t_primary '[' b=slices ']' !t_lookahead { _Py_Subscript(a, b, Store, EXTRA) }
584583

Diff for: Lib/test/test_grammar.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -1279,7 +1279,7 @@ def __getitem__(self, i):
12791279
def test_try(self):
12801280
### try_stmt: 'try' ':' suite (except_clause ':' suite)+ ['else' ':' suite]
12811281
### | 'try' ':' suite 'finally' ':' suite
1282-
### except_clause: 'except' [expr ['as' expr]]
1282+
### except_clause: 'except' [expr ['as' NAME]]
12831283
try:
12841284
1/0
12851285
except ZeroDivisionError:
@@ -1297,6 +1297,9 @@ def test_try(self):
12971297
except (EOFError, TypeError, ZeroDivisionError) as msg: pass
12981298
try: pass
12991299
finally: pass
1300+
with self.assertRaises(SyntaxError):
1301+
compile("try:\n pass\nexcept Exception as a.b:\n pass", "?", "exec")
1302+
compile("try:\n pass\nexcept Exception as a[b]:\n pass", "?", "exec")
13001303

13011304
def test_suite(self):
13021305
# simple_stmt | NEWLINE INDENT NEWLINE* (stmt NEWLINE*)+ DEDENT

Diff for: Lib/test/test_peg_parser.py

+25
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@
3535
('attribute_simple', 'a.b'),
3636
('attributes_subscript', 'a.b[0]'),
3737
('augmented_assignment', 'x += 42'),
38+
('augmented_assignment_attribute', 'a.b.c += 42'),
39+
('augmented_assignment_paren', '(x) += 42'),
40+
('augmented_assignment_paren_subscript', '(x[0]) -= 42'),
3841
('binop_add', '1 + 1'),
3942
('binop_add_multiple', '1 + 1 + 1 + 1'),
4043
('binop_all', '1 + 2 * 5 + 3 ** 2 - -3'),
@@ -547,6 +550,11 @@ def f(*a, b):
547550
with a as (x, y):
548551
pass
549552
'''),
553+
('with_list_target',
554+
'''
555+
with a as [x, y]:
556+
pass
557+
'''),
550558
('yield', 'yield'),
551559
('yield_expr', 'yield a'),
552560
('yield_from', 'yield from a'),
@@ -560,6 +568,9 @@ def f(*a, b):
560568
("annotation_tuple", "(a,): int"),
561569
("annotation_tuple_without_paren", "a,: int"),
562570
("assignment_keyword", "a = if"),
571+
("augmented_assignment_list", "[a, b] += 1"),
572+
("augmented_assignment_tuple", "a, b += 1"),
573+
("augmented_assignment_tuple_paren", "(a, b) += (1, 2)"),
563574
("comprehension_lambda", "(a for a in lambda: b)"),
564575
("comprehension_else", "(a for a in b if c else d"),
565576
("del_call", "del a()"),
@@ -589,6 +600,20 @@ def f():
589600
a
590601
"""),
591602
("not_terminated_string", "a = 'example"),
603+
("try_except_attribute_target",
604+
"""
605+
try:
606+
pass
607+
except Exception as a.b:
608+
pass
609+
"""),
610+
("try_except_subscript_target",
611+
"""
612+
try:
613+
pass
614+
except Exception as a[0]:
615+
pass
616+
"""),
592617
]
593618

594619
FAIL_SPECIALIZED_MESSAGE_CASES = [

Diff for: Lib/test/test_syntax.py

+12
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,18 @@
129129
Traceback (most recent call last):
130130
SyntaxError: cannot assign to conditional expression
131131
132+
>>> a, b += 1, 2
133+
Traceback (most recent call last):
134+
SyntaxError: invalid syntax
135+
136+
>>> (a, b) += 1, 2
137+
Traceback (most recent call last):
138+
SyntaxError: cannot assign to tuple
139+
140+
>>> [a, b] += 1, 2
141+
Traceback (most recent call last):
142+
SyntaxError: cannot assign to list
143+
132144
From compiler_complex_args():
133145
134146
>>> def f(None=1):

Diff for: Parser/pegen/parse.c

+33-36
Original file line numberDiff line numberDiff line change
@@ -199,8 +199,8 @@ static KeywordToken *reserved_keywords[] = {
199199
#define star_targets_seq_type 1128
200200
#define star_target_type 1129
201201
#define star_atom_type 1130
202-
#define inside_paren_ann_assign_target_type 1131
203-
#define ann_assign_subscript_attribute_target_type 1132
202+
#define single_target_type 1131
203+
#define single_subscript_attribute_target_type 1132
204204
#define del_targets_type 1133
205205
#define del_target_type 1134
206206
#define del_t_atom_type 1135
@@ -501,8 +501,8 @@ static expr_ty star_targets_rule(Parser *p);
501501
static asdl_seq* star_targets_seq_rule(Parser *p);
502502
static expr_ty star_target_rule(Parser *p);
503503
static expr_ty star_atom_rule(Parser *p);
504-
static expr_ty inside_paren_ann_assign_target_rule(Parser *p);
505-
static expr_ty ann_assign_subscript_attribute_target_rule(Parser *p);
504+
static expr_ty single_target_rule(Parser *p);
505+
static expr_ty single_subscript_attribute_target_rule(Parser *p);
506506
static asdl_seq* del_targets_rule(Parser *p);
507507
static expr_ty del_target_rule(Parser *p);
508508
static expr_ty del_t_atom_rule(Parser *p);
@@ -1590,9 +1590,9 @@ compound_stmt_rule(Parser *p)
15901590

15911591
// assignment:
15921592
// | NAME ':' expression ['=' annotated_rhs]
1593-
// | ('(' inside_paren_ann_assign_target ')' | ann_assign_subscript_attribute_target) ':' expression ['=' annotated_rhs]
1593+
// | ('(' single_target ')' | single_subscript_attribute_target) ':' expression ['=' annotated_rhs]
15941594
// | ((star_targets '='))+ (yield_expr | star_expressions) TYPE_COMMENT?
1595-
// | target augassign (yield_expr | star_expressions)
1595+
// | single_target augassign (yield_expr | star_expressions)
15961596
// | invalid_assignment
15971597
static stmt_ty
15981598
assignment_rule(Parser *p)
@@ -1642,13 +1642,13 @@ assignment_rule(Parser *p)
16421642
}
16431643
p->mark = _mark;
16441644
}
1645-
{ // ('(' inside_paren_ann_assign_target ')' | ann_assign_subscript_attribute_target) ':' expression ['=' annotated_rhs]
1645+
{ // ('(' single_target ')' | single_subscript_attribute_target) ':' expression ['=' annotated_rhs]
16461646
Token * _literal;
16471647
void *a;
16481648
expr_ty b;
16491649
void *c;
16501650
if (
1651-
(a = _tmp_20_rule(p)) // '(' inside_paren_ann_assign_target ')' | ann_assign_subscript_attribute_target
1651+
(a = _tmp_20_rule(p)) // '(' single_target ')' | single_subscript_attribute_target
16521652
&&
16531653
(_literal = _PyPegen_expect_token(p, 11)) // token=':'
16541654
&&
@@ -1703,12 +1703,12 @@ assignment_rule(Parser *p)
17031703
}
17041704
p->mark = _mark;
17051705
}
1706-
{ // target augassign (yield_expr | star_expressions)
1706+
{ // single_target augassign (yield_expr | star_expressions)
17071707
expr_ty a;
17081708
AugOperator* b;
17091709
void *c;
17101710
if (
1711-
(a = target_rule(p)) // target
1711+
(a = single_target_rule(p)) // single_target
17121712
&&
17131713
(b = augassign_rule(p)) // augassign
17141714
&&
@@ -3350,7 +3350,7 @@ try_stmt_rule(Parser *p)
33503350
return _res;
33513351
}
33523352

3353-
// except_block: 'except' expression ['as' target] ':' block | 'except' ':' block
3353+
// except_block: 'except' expression ['as' NAME] ':' block | 'except' ':' block
33543354
static excepthandler_ty
33553355
except_block_rule(Parser *p)
33563356
{
@@ -3367,7 +3367,7 @@ except_block_rule(Parser *p)
33673367
UNUSED(_start_lineno); // Only used by EXTRA macro
33683368
int _start_col_offset = p->tokens[_mark]->col_offset;
33693369
UNUSED(_start_col_offset); // Only used by EXTRA macro
3370-
{ // 'except' expression ['as' target] ':' block
3370+
{ // 'except' expression ['as' NAME] ':' block
33713371
Token * _keyword;
33723372
Token * _literal;
33733373
asdl_seq* b;
@@ -3378,7 +3378,7 @@ except_block_rule(Parser *p)
33783378
&&
33793379
(e = expression_rule(p)) // expression
33803380
&&
3381-
(t = _tmp_48_rule(p), 1) // ['as' target]
3381+
(t = _tmp_48_rule(p), 1) // ['as' NAME]
33823382
&&
33833383
(_literal = _PyPegen_expect_token(p, 11)) // token=':'
33843384
&&
@@ -9605,25 +9605,22 @@ star_atom_rule(Parser *p)
96059605
return _res;
96069606
}
96079607

9608-
// inside_paren_ann_assign_target:
9609-
// | ann_assign_subscript_attribute_target
9610-
// | NAME
9611-
// | '(' inside_paren_ann_assign_target ')'
9608+
// single_target: single_subscript_attribute_target | NAME | '(' single_target ')'
96129609
static expr_ty
9613-
inside_paren_ann_assign_target_rule(Parser *p)
9610+
single_target_rule(Parser *p)
96149611
{
96159612
if (p->error_indicator) {
96169613
return NULL;
96179614
}
96189615
expr_ty _res = NULL;
96199616
int _mark = p->mark;
9620-
{ // ann_assign_subscript_attribute_target
9621-
expr_ty ann_assign_subscript_attribute_target_var;
9617+
{ // single_subscript_attribute_target
9618+
expr_ty single_subscript_attribute_target_var;
96229619
if (
9623-
(ann_assign_subscript_attribute_target_var = ann_assign_subscript_attribute_target_rule(p)) // ann_assign_subscript_attribute_target
9620+
(single_subscript_attribute_target_var = single_subscript_attribute_target_rule(p)) // single_subscript_attribute_target
96249621
)
96259622
{
9626-
_res = ann_assign_subscript_attribute_target_var;
9623+
_res = single_subscript_attribute_target_var;
96279624
goto done;
96289625
}
96299626
p->mark = _mark;
@@ -9643,14 +9640,14 @@ inside_paren_ann_assign_target_rule(Parser *p)
96439640
}
96449641
p->mark = _mark;
96459642
}
9646-
{ // '(' inside_paren_ann_assign_target ')'
9643+
{ // '(' single_target ')'
96479644
Token * _literal;
96489645
Token * _literal_1;
96499646
expr_ty a;
96509647
if (
96519648
(_literal = _PyPegen_expect_token(p, 7)) // token='('
96529649
&&
9653-
(a = inside_paren_ann_assign_target_rule(p)) // inside_paren_ann_assign_target
9650+
(a = single_target_rule(p)) // single_target
96549651
&&
96559652
(_literal_1 = _PyPegen_expect_token(p, 8)) // token=')'
96569653
)
@@ -9669,11 +9666,11 @@ inside_paren_ann_assign_target_rule(Parser *p)
96699666
return _res;
96709667
}
96719668

9672-
// ann_assign_subscript_attribute_target:
9669+
// single_subscript_attribute_target:
96739670
// | t_primary '.' NAME !t_lookahead
96749671
// | t_primary '[' slices ']' !t_lookahead
96759672
static expr_ty
9676-
ann_assign_subscript_attribute_target_rule(Parser *p)
9673+
single_subscript_attribute_target_rule(Parser *p)
96779674
{
96789675
if (p->error_indicator) {
96799676
return NULL;
@@ -11907,7 +11904,7 @@ _tmp_19_rule(Parser *p)
1190711904
return _res;
1190811905
}
1190911906

11910-
// _tmp_20: '(' inside_paren_ann_assign_target ')' | ann_assign_subscript_attribute_target
11907+
// _tmp_20: '(' single_target ')' | single_subscript_attribute_target
1191111908
static void *
1191211909
_tmp_20_rule(Parser *p)
1191311910
{
@@ -11916,14 +11913,14 @@ _tmp_20_rule(Parser *p)
1191611913
}
1191711914
void * _res = NULL;
1191811915
int _mark = p->mark;
11919-
{ // '(' inside_paren_ann_assign_target ')'
11916+
{ // '(' single_target ')'
1192011917
Token * _literal;
1192111918
Token * _literal_1;
1192211919
expr_ty b;
1192311920
if (
1192411921
(_literal = _PyPegen_expect_token(p, 7)) // token='('
1192511922
&&
11926-
(b = inside_paren_ann_assign_target_rule(p)) // inside_paren_ann_assign_target
11923+
(b = single_target_rule(p)) // single_target
1192711924
&&
1192811925
(_literal_1 = _PyPegen_expect_token(p, 8)) // token=')'
1192911926
)
@@ -11937,13 +11934,13 @@ _tmp_20_rule(Parser *p)
1193711934
}
1193811935
p->mark = _mark;
1193911936
}
11940-
{ // ann_assign_subscript_attribute_target
11941-
expr_ty ann_assign_subscript_attribute_target_var;
11937+
{ // single_subscript_attribute_target
11938+
expr_ty single_subscript_attribute_target_var;
1194211939
if (
11943-
(ann_assign_subscript_attribute_target_var = ann_assign_subscript_attribute_target_rule(p)) // ann_assign_subscript_attribute_target
11940+
(single_subscript_attribute_target_var = single_subscript_attribute_target_rule(p)) // single_subscript_attribute_target
1194411941
)
1194511942
{
11946-
_res = ann_assign_subscript_attribute_target_var;
11943+
_res = single_subscript_attribute_target_var;
1194711944
goto done;
1194811945
}
1194911946
p->mark = _mark;
@@ -13073,7 +13070,7 @@ _loop1_47_rule(Parser *p)
1307313070
return _seq;
1307413071
}
1307513072

13076-
// _tmp_48: 'as' target
13073+
// _tmp_48: 'as' NAME
1307713074
static void *
1307813075
_tmp_48_rule(Parser *p)
1307913076
{
@@ -13082,13 +13079,13 @@ _tmp_48_rule(Parser *p)
1308213079
}
1308313080
void * _res = NULL;
1308413081
int _mark = p->mark;
13085-
{ // 'as' target
13082+
{ // 'as' NAME
1308613083
Token * _keyword;
1308713084
expr_ty z;
1308813085
if (
1308913086
(_keyword = _PyPegen_expect_token(p, 531)) // token='as'
1309013087
&&
13091-
(z = target_rule(p)) // target
13088+
(z = _PyPegen_name_token(p)) // NAME
1309213089
)
1309313090
{
1309413091
_res = z;

0 commit comments

Comments
 (0)