Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Grammar/python.gram
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,7 @@ case_block[match_case_ty]:
| invalid_case_block
| "case" pattern=patterns guard=guard? ':' body=block {
_PyAST_match_case(pattern, guard, body, p->arena) }
| invalid_case_pattern

guard[expr_ty]: 'if' guard=named_expression { guard }

Expand Down Expand Up @@ -1480,6 +1481,10 @@ invalid_case_block:
| "case" patterns guard? NEWLINE { RAISE_SYNTAX_ERROR("expected ':'") }
| a="case" patterns guard? ':' NEWLINE !INDENT {
RAISE_INDENTATION_ERROR("expected an indented block after 'case' statement on line %d", a->lineno) }
invalid_case_pattern:
| "case" a=expression guard? ':' block {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it would be better to use this variant?

"case" a=expression 'as'? expression? guard? ':' block

For cases like this:

match ...:
  case T[1] as E:
    print(1)

Copy link
Member

@pablogsal pablogsal Oct 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be slower and a bit more unreadable I think and also allows to match "case" a=expression 'as' guard ':' block which makes no sense.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can run for ('as' expression)?. But "slower" sounds ugly, maybe such a little change doesn't worth it.

RAISE_SYNTAX_ERROR_KNOWN_LOCATION(
a, "cannot use case statement with %s", _PyPegen_get_expr_name(a)) }
invalid_as_pattern:
| or_pattern 'as' a="_" { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "cannot use '_' as a target") }
| or_pattern 'as' a=expression {
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ def testSyntaxErrorOffset(self):
check('[\nfile\nfor str(file)\nin\n[]\n]', 3, 5)
check('[file for\n str(file) in []]', 2, 2)
check("ages = {'Alice'=22, 'Bob'=23}", 1, 9)
check('match ...:\n case {**rest, "key": value}:\n ...', 2, 19)
check('match ...:\n case {**rest, "key": value}:\n ...', 2, 10)
check("[a b c d e f]", 1, 2)
check("for x yfff:", 1, 7)
check("f(a for a in b, c)", 1, 3, 1, 15)
Expand Down
29 changes: 27 additions & 2 deletions Lib/test/test_syntax.py
Original file line number Diff line number Diff line change
Expand Up @@ -374,13 +374,38 @@
... case {**rest, "key": value}:
... ...
Traceback (most recent call last):
SyntaxError: invalid syntax
SyntaxError: cannot use case statement with dict literal

>>> match ...:
... case {**_}:
... ...
Traceback (most recent call last):
SyntaxError: invalid syntax
SyntaxError: cannot use case statement with dict literal

>>> match ...:
... case ...: ...
Traceback (most recent call last):
SyntaxError: cannot use case statement with ellipsis

>>> match ...:
... case 1 // 2: ...
Traceback (most recent call last):
SyntaxError: cannot use case statement with expression

>>> match ...:
... case {1, 2, 3}: ...
Traceback (most recent call last):
SyntaxError: cannot use case statement with set display

>>> match ...:
... case a[0]: ...
Traceback (most recent call last):
SyntaxError: cannot use case statement with subscript

>>> match ...:
... case a[0].method(): ...
Traceback (most recent call last):
SyntaxError: cannot use case statement with function call

# But prefixes of soft keywords should
# still raise specialized errors
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Improve :exc:`SyntaxError` message for using ``case`` with regular
expressions.
Loading
Loading