[Python-checkins] bpo-40618: Disallow invalid targets in augassign and except clauses (GH-20083)

Lysandros Nikolaou webhook-mailer at python.org
Thu May 14 16:13:55 EDT 2020


https://github.com/python/cpython/commit/ce21cfca7bb2d18921bc4ac27cb064726996c519
commit: ce21cfca7bb2d18921bc4ac27cb064726996c519
branch: master
author: Lysandros Nikolaou <lisandrosnik at gmail.com>
committer: GitHub <noreply at github.com>
date: 2020-05-14T21:13:50+01:00
summary:

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 at gmail.com>

files:
M Grammar/python.gram
M Lib/test/test_grammar.py
M Lib/test/test_peg_parser.py
M Lib/test/test_syntax.py
M Parser/pegen/parse.c

diff --git a/Grammar/python.gram b/Grammar/python.gram
index 84c89330e3ee9..9087c7aa718b1 100644
--- a/Grammar/python.gram
+++ b/Grammar/python.gram
@@ -89,12 +89,12 @@ assignment[stmt_ty]:
             "Variable annotation syntax is",
             _Py_AnnAssign(CHECK(_PyPegen_set_expr_context(p, a, Store)), b, c, 1, EXTRA)
         ) }
-    | a=('(' b=inside_paren_ann_assign_target ')' { b }
-         | ann_assign_subscript_attribute_target) ':' b=expression c=['=' d=annotated_rhs { d }] {
+    | a=('(' b=single_target ')' { b }
+         | single_subscript_attribute_target) ':' b=expression c=['=' d=annotated_rhs { d }] {
         CHECK_VERSION(6, "Variable annotations syntax is", _Py_AnnAssign(a, b, c, 0, EXTRA)) }
     | a=(z=star_targets '=' { z })+ b=(yield_expr | star_expressions) tc=[TYPE_COMMENT] {
          _Py_Assign(a, b, NEW_TYPE_COMMENT(p, tc), EXTRA) }
-    | a=target b=augassign c=(yield_expr | star_expressions) {
+    | a=single_target b=augassign c=(yield_expr | star_expressions) {
          _Py_AugAssign(a, b->kind, c, EXTRA) }
     | invalid_assignment
 
@@ -185,7 +185,7 @@ try_stmt[stmt_ty]:
     | 'try' ':' b=block f=finally_block { _Py_Try(b, NULL, NULL, f, EXTRA) }
     | 'try' ':' b=block ex=except_block+ el=[else_block] f=[finally_block] { _Py_Try(b, ex, el, f, EXTRA) }
 except_block[excepthandler_ty]:
-    | 'except' e=expression t=['as' z=target { z }] ':' b=block {
+    | 'except' e=expression t=['as' z=NAME { z }] ':' b=block {
         _Py_ExceptHandler(e, (t) ? ((expr_ty) t)->v.Name.id : NULL, b, EXTRA) }
     | 'except' ':' b=block { _Py_ExceptHandler(NULL, NULL, b, EXTRA) }
 finally_block[asdl_seq*]: 'finally' ':' a=block { a }
@@ -573,12 +573,11 @@ star_atom[expr_ty]:
     | '(' a=[star_targets_seq] ')' { _Py_Tuple(a, Store, EXTRA) }
     | '[' a=[star_targets_seq] ']' { _Py_List(a, Store, EXTRA) }
 
-inside_paren_ann_assign_target[expr_ty]:
-    | ann_assign_subscript_attribute_target
+single_target[expr_ty]:
+    | single_subscript_attribute_target
     | a=NAME { _PyPegen_set_expr_context(p, a, Store) }
-    | '(' a=inside_paren_ann_assign_target ')' { a }
-
-ann_assign_subscript_attribute_target[expr_ty]:
+    | '(' a=single_target ')' { a }
+single_subscript_attribute_target[expr_ty]:
     | a=t_primary '.' b=NAME !t_lookahead { _Py_Attribute(a, b->v.Name.id, Store, EXTRA) }
     | a=t_primary '[' b=slices ']' !t_lookahead { _Py_Subscript(a, b, Store, EXTRA) }
 
diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py
index 02ba8a8b1579a..e1a402e2b463b 100644
--- a/Lib/test/test_grammar.py
+++ b/Lib/test/test_grammar.py
@@ -1279,7 +1279,7 @@ def __getitem__(self, i):
     def test_try(self):
         ### try_stmt: 'try' ':' suite (except_clause ':' suite)+ ['else' ':' suite]
         ###         | 'try' ':' suite 'finally' ':' suite
-        ### except_clause: 'except' [expr ['as' expr]]
+        ### except_clause: 'except' [expr ['as' NAME]]
         try:
             1/0
         except ZeroDivisionError:
@@ -1297,6 +1297,9 @@ def test_try(self):
         except (EOFError, TypeError, ZeroDivisionError) as msg: pass
         try: pass
         finally: pass
+        with self.assertRaises(SyntaxError):
+            compile("try:\n    pass\nexcept Exception as a.b:\n    pass", "?", "exec")
+            compile("try:\n    pass\nexcept Exception as a[b]:\n    pass", "?", "exec")
 
     def test_suite(self):
         # simple_stmt | NEWLINE INDENT NEWLINE* (stmt NEWLINE*)+ DEDENT
diff --git a/Lib/test/test_peg_parser.py b/Lib/test/test_peg_parser.py
index df2d46d8827f0..71e071940de2f 100644
--- a/Lib/test/test_peg_parser.py
+++ b/Lib/test/test_peg_parser.py
@@ -35,6 +35,9 @@
     ('attribute_simple', 'a.b'),
     ('attributes_subscript', 'a.b[0]'),
     ('augmented_assignment', 'x += 42'),
+    ('augmented_assignment_attribute', 'a.b.c += 42'),
+    ('augmented_assignment_paren', '(x) += 42'),
+    ('augmented_assignment_paren_subscript', '(x[0]) -= 42'),
     ('binop_add', '1 + 1'),
     ('binop_add_multiple', '1 + 1 + 1 + 1'),
     ('binop_all', '1 + 2 * 5 + 3 ** 2 - -3'),
@@ -547,6 +550,11 @@ def f(*a, b):
         with a as (x, y):
             pass
      '''),
+    ('with_list_target',
+     '''
+        with a as [x, y]:
+            pass
+     '''),
     ('yield', 'yield'),
     ('yield_expr', 'yield a'),
     ('yield_from', 'yield from a'),
@@ -560,6 +568,9 @@ def f(*a, b):
     ("annotation_tuple", "(a,): int"),
     ("annotation_tuple_without_paren", "a,: int"),
     ("assignment_keyword", "a = if"),
+    ("augmented_assignment_list", "[a, b] += 1"),
+    ("augmented_assignment_tuple", "a, b += 1"),
+    ("augmented_assignment_tuple_paren", "(a, b) += (1, 2)"),
     ("comprehension_lambda", "(a for a in lambda: b)"),
     ("comprehension_else", "(a for a in b if c else d"),
     ("del_call", "del a()"),
@@ -589,6 +600,20 @@ def f():
              a
      """),
     ("not_terminated_string", "a = 'example"),
+    ("try_except_attribute_target",
+     """
+     try:
+         pass
+     except Exception as a.b:
+         pass
+     """),
+    ("try_except_subscript_target",
+     """
+     try:
+         pass
+     except Exception as a[0]:
+         pass
+     """),
 ]
 
 FAIL_SPECIALIZED_MESSAGE_CASES = [
diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py
index 06636ae8a149a..a3a101534628a 100644
--- a/Lib/test/test_syntax.py
+++ b/Lib/test/test_syntax.py
@@ -129,6 +129,18 @@
 Traceback (most recent call last):
 SyntaxError: cannot assign to conditional expression
 
+>>> a, b += 1, 2
+Traceback (most recent call last):
+SyntaxError: invalid syntax
+
+>>> (a, b) += 1, 2
+Traceback (most recent call last):
+SyntaxError: cannot assign to tuple
+
+>>> [a, b] += 1, 2
+Traceback (most recent call last):
+SyntaxError: cannot assign to list
+
 From compiler_complex_args():
 
 >>> def f(None=1):
diff --git a/Parser/pegen/parse.c b/Parser/pegen/parse.c
index b1b248187ea3e..851d17226d162 100644
--- a/Parser/pegen/parse.c
+++ b/Parser/pegen/parse.c
@@ -199,8 +199,8 @@ static KeywordToken *reserved_keywords[] = {
 #define star_targets_seq_type 1128
 #define star_target_type 1129
 #define star_atom_type 1130
-#define inside_paren_ann_assign_target_type 1131
-#define ann_assign_subscript_attribute_target_type 1132
+#define single_target_type 1131
+#define single_subscript_attribute_target_type 1132
 #define del_targets_type 1133
 #define del_target_type 1134
 #define del_t_atom_type 1135
@@ -501,8 +501,8 @@ static expr_ty star_targets_rule(Parser *p);
 static asdl_seq* star_targets_seq_rule(Parser *p);
 static expr_ty star_target_rule(Parser *p);
 static expr_ty star_atom_rule(Parser *p);
-static expr_ty inside_paren_ann_assign_target_rule(Parser *p);
-static expr_ty ann_assign_subscript_attribute_target_rule(Parser *p);
+static expr_ty single_target_rule(Parser *p);
+static expr_ty single_subscript_attribute_target_rule(Parser *p);
 static asdl_seq* del_targets_rule(Parser *p);
 static expr_ty del_target_rule(Parser *p);
 static expr_ty del_t_atom_rule(Parser *p);
@@ -1590,9 +1590,9 @@ compound_stmt_rule(Parser *p)
 
 // assignment:
 //     | NAME ':' expression ['=' annotated_rhs]
-//     | ('(' inside_paren_ann_assign_target ')' | ann_assign_subscript_attribute_target) ':' expression ['=' annotated_rhs]
+//     | ('(' single_target ')' | single_subscript_attribute_target) ':' expression ['=' annotated_rhs]
 //     | ((star_targets '='))+ (yield_expr | star_expressions) TYPE_COMMENT?
-//     | target augassign (yield_expr | star_expressions)
+//     | single_target augassign (yield_expr | star_expressions)
 //     | invalid_assignment
 static stmt_ty
 assignment_rule(Parser *p)
@@ -1642,13 +1642,13 @@ assignment_rule(Parser *p)
         }
         p->mark = _mark;
     }
-    { // ('(' inside_paren_ann_assign_target ')' | ann_assign_subscript_attribute_target) ':' expression ['=' annotated_rhs]
+    { // ('(' single_target ')' | single_subscript_attribute_target) ':' expression ['=' annotated_rhs]
         Token * _literal;
         void *a;
         expr_ty b;
         void *c;
         if (
-            (a = _tmp_20_rule(p))  // '(' inside_paren_ann_assign_target ')' | ann_assign_subscript_attribute_target
+            (a = _tmp_20_rule(p))  // '(' single_target ')' | single_subscript_attribute_target
             &&
             (_literal = _PyPegen_expect_token(p, 11))  // token=':'
             &&
@@ -1703,12 +1703,12 @@ assignment_rule(Parser *p)
         }
         p->mark = _mark;
     }
-    { // target augassign (yield_expr | star_expressions)
+    { // single_target augassign (yield_expr | star_expressions)
         expr_ty a;
         AugOperator* b;
         void *c;
         if (
-            (a = target_rule(p))  // target
+            (a = single_target_rule(p))  // single_target
             &&
             (b = augassign_rule(p))  // augassign
             &&
@@ -3350,7 +3350,7 @@ try_stmt_rule(Parser *p)
     return _res;
 }
 
-// except_block: 'except' expression ['as' target] ':' block | 'except' ':' block
+// except_block: 'except' expression ['as' NAME] ':' block | 'except' ':' block
 static excepthandler_ty
 except_block_rule(Parser *p)
 {
@@ -3367,7 +3367,7 @@ except_block_rule(Parser *p)
     UNUSED(_start_lineno); // Only used by EXTRA macro
     int _start_col_offset = p->tokens[_mark]->col_offset;
     UNUSED(_start_col_offset); // Only used by EXTRA macro
-    { // 'except' expression ['as' target] ':' block
+    { // 'except' expression ['as' NAME] ':' block
         Token * _keyword;
         Token * _literal;
         asdl_seq* b;
@@ -3378,7 +3378,7 @@ except_block_rule(Parser *p)
             &&
             (e = expression_rule(p))  // expression
             &&
-            (t = _tmp_48_rule(p), 1)  // ['as' target]
+            (t = _tmp_48_rule(p), 1)  // ['as' NAME]
             &&
             (_literal = _PyPegen_expect_token(p, 11))  // token=':'
             &&
@@ -9605,25 +9605,22 @@ star_atom_rule(Parser *p)
     return _res;
 }
 
-// inside_paren_ann_assign_target:
-//     | ann_assign_subscript_attribute_target
-//     | NAME
-//     | '(' inside_paren_ann_assign_target ')'
+// single_target: single_subscript_attribute_target | NAME | '(' single_target ')'
 static expr_ty
-inside_paren_ann_assign_target_rule(Parser *p)
+single_target_rule(Parser *p)
 {
     if (p->error_indicator) {
         return NULL;
     }
     expr_ty _res = NULL;
     int _mark = p->mark;
-    { // ann_assign_subscript_attribute_target
-        expr_ty ann_assign_subscript_attribute_target_var;
+    { // single_subscript_attribute_target
+        expr_ty single_subscript_attribute_target_var;
         if (
-            (ann_assign_subscript_attribute_target_var = ann_assign_subscript_attribute_target_rule(p))  // ann_assign_subscript_attribute_target
+            (single_subscript_attribute_target_var = single_subscript_attribute_target_rule(p))  // single_subscript_attribute_target
         )
         {
-            _res = ann_assign_subscript_attribute_target_var;
+            _res = single_subscript_attribute_target_var;
             goto done;
         }
         p->mark = _mark;
@@ -9643,14 +9640,14 @@ inside_paren_ann_assign_target_rule(Parser *p)
         }
         p->mark = _mark;
     }
-    { // '(' inside_paren_ann_assign_target ')'
+    { // '(' single_target ')'
         Token * _literal;
         Token * _literal_1;
         expr_ty a;
         if (
             (_literal = _PyPegen_expect_token(p, 7))  // token='('
             &&
-            (a = inside_paren_ann_assign_target_rule(p))  // inside_paren_ann_assign_target
+            (a = single_target_rule(p))  // single_target
             &&
             (_literal_1 = _PyPegen_expect_token(p, 8))  // token=')'
         )
@@ -9669,11 +9666,11 @@ inside_paren_ann_assign_target_rule(Parser *p)
     return _res;
 }
 
-// ann_assign_subscript_attribute_target:
+// single_subscript_attribute_target:
 //     | t_primary '.' NAME !t_lookahead
 //     | t_primary '[' slices ']' !t_lookahead
 static expr_ty
-ann_assign_subscript_attribute_target_rule(Parser *p)
+single_subscript_attribute_target_rule(Parser *p)
 {
     if (p->error_indicator) {
         return NULL;
@@ -11907,7 +11904,7 @@ _tmp_19_rule(Parser *p)
     return _res;
 }
 
-// _tmp_20: '(' inside_paren_ann_assign_target ')' | ann_assign_subscript_attribute_target
+// _tmp_20: '(' single_target ')' | single_subscript_attribute_target
 static void *
 _tmp_20_rule(Parser *p)
 {
@@ -11916,14 +11913,14 @@ _tmp_20_rule(Parser *p)
     }
     void * _res = NULL;
     int _mark = p->mark;
-    { // '(' inside_paren_ann_assign_target ')'
+    { // '(' single_target ')'
         Token * _literal;
         Token * _literal_1;
         expr_ty b;
         if (
             (_literal = _PyPegen_expect_token(p, 7))  // token='('
             &&
-            (b = inside_paren_ann_assign_target_rule(p))  // inside_paren_ann_assign_target
+            (b = single_target_rule(p))  // single_target
             &&
             (_literal_1 = _PyPegen_expect_token(p, 8))  // token=')'
         )
@@ -11937,13 +11934,13 @@ _tmp_20_rule(Parser *p)
         }
         p->mark = _mark;
     }
-    { // ann_assign_subscript_attribute_target
-        expr_ty ann_assign_subscript_attribute_target_var;
+    { // single_subscript_attribute_target
+        expr_ty single_subscript_attribute_target_var;
         if (
-            (ann_assign_subscript_attribute_target_var = ann_assign_subscript_attribute_target_rule(p))  // ann_assign_subscript_attribute_target
+            (single_subscript_attribute_target_var = single_subscript_attribute_target_rule(p))  // single_subscript_attribute_target
         )
         {
-            _res = ann_assign_subscript_attribute_target_var;
+            _res = single_subscript_attribute_target_var;
             goto done;
         }
         p->mark = _mark;
@@ -13073,7 +13070,7 @@ _loop1_47_rule(Parser *p)
     return _seq;
 }
 
-// _tmp_48: 'as' target
+// _tmp_48: 'as' NAME
 static void *
 _tmp_48_rule(Parser *p)
 {
@@ -13082,13 +13079,13 @@ _tmp_48_rule(Parser *p)
     }
     void * _res = NULL;
     int _mark = p->mark;
-    { // 'as' target
+    { // 'as' NAME
         Token * _keyword;
         expr_ty z;
         if (
             (_keyword = _PyPegen_expect_token(p, 531))  // token='as'
             &&
-            (z = target_rule(p))  // target
+            (z = _PyPegen_name_token(p))  // NAME
         )
         {
             _res = z;



More information about the Python-checkins mailing list