Skip to content

Commit d79c9b3

Browse files
committed
Clarify target parsing contracts and AST expectations
1 parent aa3dc1e commit d79c9b3

3 files changed

Lines changed: 16 additions & 8 deletions

File tree

Grammar/python.gram

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -786,7 +786,8 @@ comparison[expr_ty]:
786786

787787
compare_op_bitwise_or_pair[CmpopExprPair*]:
788788
| '==' a=bitwise_or { _PyPegen_cmpop_expr_pair(p, Eq, a) }
789-
| (tok='!=' { _PyPegen_check_barry_as_flufl(p, tok) ? NULL : tok}) a=bitwise_or {_PyPegen_cmpop_expr_pair(p, NotEq, a) }
789+
| (tok='!=' { _PyPegen_check_barry_as_flufl(p, tok) ? NULL : tok }) a=bitwise_or {
790+
_PyPegen_cmpop_expr_pair(p, NotEq, a) }
790791
| '<=' a=bitwise_or { _PyPegen_cmpop_expr_pair(p, LtE, a) }
791792
| '<' a=bitwise_or { _PyPegen_cmpop_expr_pair(p, Lt, a) }
792793
| '>=' a=bitwise_or { _PyPegen_cmpop_expr_pair(p, GtE, a) }
@@ -1110,8 +1111,7 @@ star_targets[expr_ty]:
11101111
_PyAST_Tuple(CHECK(asdl_expr_seq*, _PyPegen_seq_insert_in_front(p, a, b)), Store, EXTRA) }
11111112

11121113
star_target[expr_ty] (memo):
1113-
| '*' a=(!'*' star_target) {
1114-
_PyAST_Starred(a, Store, EXTRA) }
1114+
| '*' a=(!'*' star_target) { _PyAST_Starred(a, Store, EXTRA) }
11151115
| target
11161116

11171117
target[expr_ty] (memo):

Lib/test/test_ast/test_ast.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -712,7 +712,14 @@ def test_primary_target_context(self):
712712
self.assertIsInstance(child.ctx, ast.Load)
713713

714714
def test_container_target_context(self):
715-
for expression in ('(a, [b, c])', '[a, (b, c)]', '((a))', '()', '[]'):
715+
cases = (
716+
('(a, [b, c])', '(a, [b, c])'),
717+
('[a, (b, c)]', '[a, (b, c)]'),
718+
('((a))', 'a'),
719+
('()', '()'),
720+
('[]', '[]'),
721+
)
722+
for expression, expected_segment in cases:
716723
for context in (ast.Load, ast.Store, ast.Del):
717724
with self.subTest(expression=expression, context=context):
718725
if context is ast.Load:
@@ -727,8 +734,7 @@ def test_container_target_context(self):
727734
if hasattr(child, 'ctx'):
728735
self.assertIsInstance(child.ctx, context)
729736
self.assertEqual(ast.get_source_segment(source, node),
730-
ast.get_source_segment(expression,
731-
ast.parse(expression, mode='eval').body))
737+
expected_segment)
732738

733739
node = ast.parse('[a, *[b, *c]] = value').body[0].targets[0]
734740
for child in ast.walk(node):

Parser/action_helpers.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,8 @@ _PyPegen_set_expr_context(Parser *p, expr_ty expr, expr_context_ty ctx)
346346
return new;
347347
}
348348

349-
/* Check the target form before copying it into Store or Del context. */
349+
/* Invalid targets return NULL without raising, allowing parsing to backtrack.
350+
Copy valid targets to preserve the memoized expression's context. */
350351
expr_ty
351352
_PyPegen_make_target(Parser *p, expr_ty expr, TARGETS_TYPE targets_type)
352353
{
@@ -1249,7 +1250,8 @@ _PyPegen_get_invalid_target(expr_ty e, TARGETS_TYPE targets_type)
12491250
return NULL;
12501251
}
12511252

1252-
if (targets_type == SINGLE_TARGETS || targets_type == ATTRIBUTE_OR_SUBSCRIPT_TARGETS) {
1253+
if (targets_type == SINGLE_TARGETS ||
1254+
targets_type == ATTRIBUTE_OR_SUBSCRIPT_TARGETS) {
12531255
if (targets_type == SINGLE_TARGETS && e->kind == Name_kind) {
12541256
return NULL;
12551257
}

0 commit comments

Comments
 (0)