Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
4dbba6b
gh-129515: Clarify syntax error messages for conditional expressions
sergey-miryanov Feb 9, 2025
6686090
Update news
sergey-miryanov Feb 9, 2025
b204fdb
Replace simple_stmt_without_expressions with explicit invalid_ifexp_*
sergey-miryanov Feb 9, 2025
aa3f17d
Merge branch 'main' into gh-129515-ifexp-syntax-error
sergey-miryanov Feb 9, 2025
8acc305
elif after else
sergey-miryanov Feb 9, 2025
b43b3a2
Merge branch 'gh-129515-ifexp-syntax-error' of github.com:sergey-miry…
sergey-miryanov Feb 10, 2025
48e4796
Merge branch 'main' into gh-129515-ifexp-syntax-error
sergey-miryanov Feb 10, 2025
60c5066
Simplify test_ifexp_* tests
sergey-miryanov Feb 10, 2025
c33b08f
Remove an accidentally added test line
sergey-miryanov Feb 10, 2025
8afe66a
Merge branch 'main' into gh-129515-ifexp-syntax-error
pablogsal Feb 10, 2025
7e4ed1b
Merge branch 'main' into gh-129515-ifexp-syntax-error
sergey-miryanov Feb 11, 2025
db2784e
Simplify invalid_expression rule
sergey-miryanov Feb 11, 2025
c09bf42
Update whatsnew for 3.14
sergey-miryanov Feb 11, 2025
c66fbe3
Fix references in whatsnew
sergey-miryanov Feb 11, 2025
79a046c
Fix reference to if_expr
sergey-miryanov Feb 11, 2025
2eaa968
Merge branch 'main' into gh-129515-ifexp-syntax-error
sergey-miryanov Feb 12, 2025
c1ef4ca
Fix code-block in whatsnew
sergey-miryanov Feb 12, 2025
63b4400
Merge branch 'main' into gh-129515-ifexp-syntax-error
sergey-miryanov Feb 13, 2025
903d9b6
Fix invalid rule for ifexp if statement given after else
sergey-miryanov Feb 13, 2025
b1c0ab4
Update doc-string in test_syntax
sergey-miryanov Feb 13, 2025
a33a705
Update Doc/whatsnew/3.14.rst
sergey-miryanov Feb 16, 2025
9c711ae
Update Misc/NEWS.d/next/Core_and_Builtins/2025-02-09-11-30-38.gh-issu…
sergey-miryanov Feb 17, 2025
b4a1c55
Update tests and whatsnew entry
sergey-miryanov Feb 17, 2025
c468622
Merge branch 'gh-129515-ifexp-syntax-error' of github.com:sergey-miry…
sergey-miryanov Feb 17, 2025
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
Prev Previous commit
Next Next commit
Fix invalid rule for ifexp if statement given after else
- also reword error messages
- move tests to test_syntax
- update whatsnew and news
  • Loading branch information
sergey-miryanov committed Feb 13, 2025
commit 903d9b6515f7df3c1e0f62b38c8ac24f6690a1c0
18 changes: 11 additions & 7 deletions Doc/whatsnew/3.14.rst
Original file line number Diff line number Diff line change
Expand Up @@ -174,26 +174,30 @@ Improved error messages
^^^^^^^
ValueError: too many values to unpack (expected 3, got 4)

* If a statement (:keyword:`!pass`, :keyword:`!del`, :keyword:`!return`, :keyword:`!yield`,
:keyword:`!raise`, :keyword:`!break`, :keyword:`!continue`, :keyword:`!assert`)
is passed to the :ref:`if_expr`, then the error message highlights where
the :token:`~python-grammar:expression` is required.

.. code-block:: pycon
* If a statement (:keyword:`pass`, :keyword:`del`, :keyword:`return`,
:keyword:`yield`, :keyword:`raise`, :keyword:`break`, :keyword:`continue`,
:keyword:`assert`, :keyword:`import`, :keyword:`from`) is passed to the
:ref:`if_expr` after :keyword:`else`, or one of the :keyword:`pass`,
:keyword:`break`, :keyword:`continue` is passed before :keyword:`if`, then the
Comment thread
sergey-miryanov marked this conversation as resolved.
Outdated
error message highlights where the :token:`~python-grammar:expression` is
required. (Contributed by Sergey Miryanov in :gh:`129515`.)

.. code-block:: python
Comment thread
sergey-miryanov marked this conversation as resolved.
Outdated

>>> x = 1 if True else pass
Traceback (most recent call last):
File "<string>", line 1
x = 1 if True else pass
^^^^
SyntaxError: statement given where 'orelse' expression required
SyntaxError: expected expression after 'else', but statement is given

>>> x = continue if True else break
Traceback (most recent call last):
File "<string>", line 1
x = continue if True else break
^^^^^^^^
SyntaxError: statement given where 'body' expression required
SyntaxError: expected expression before 'if', but statement is given


* When incorrectly closed strings are detected, the error message suggests
Expand Down
7 changes: 3 additions & 4 deletions Grammar/python.gram
Original file line number Diff line number Diff line change
Expand Up @@ -1196,11 +1196,10 @@ invalid_expression:
_PyPegen_check_legacy_stmt(p, a) ? NULL : p->tokens[p->mark-1]->level == 0 ? NULL :
RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "invalid syntax. Perhaps you forgot a comma?") }
| a=disjunction 'if' b=disjunction !('else'|':') { RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "expected 'else' after 'if' expression") }
| a=disjunction 'if' b=disjunction 'else' c[stmt_ty]=(
return_stmt | raise_stmt | pass_stmt | del_stmt | yield_stmt | assert_stmt | break_stmt | continue_stmt) {
RAISE_SYNTAX_ERROR_KNOWN_LOCATION (c, "statement given where 'orelse' expression required") }
| a=disjunction 'if' b=disjunction 'else' !expression {
RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("expected expression after 'else', but statement is given") }
| a[stmt_ty]=(pass_stmt|break_stmt|continue_stmt) 'if' b=disjunction 'else' c=simple_stmt {
Comment thread
sergey-miryanov marked this conversation as resolved.
RAISE_SYNTAX_ERROR_KNOWN_LOCATION (a, "statement given where 'body' expression required") }
RAISE_SYNTAX_ERROR_KNOWN_LOCATION (a, "expected expression before 'if', but statement is given") }
| a='lambda' [lambda_params] b=':' &FSTRING_MIDDLE {
RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "f-string: lambda expressions are not allowed without parentheses") }

Expand Down
36 changes: 0 additions & 36 deletions Lib/test/test_ast/test_ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -1966,42 +1966,6 @@ def test_ifexp(self):
for args in (s, l, l), (l, s, l), (l, l, s):
self.expr(ast.IfExp(*args), "must have Load context")

def test_ifexp_orelse_stmt(self):
msg = "statement given where 'orelse' expression required"
for stmt in [
"x = 1 if 1 else pass",
"x = 1 if 1 else return",
"x = 1 if 1 else raise Exception('a')",
"x = 1 if 1 else del a",
"x = 1 if 1 else yield 2",
"x = 1 if 1 else assert False",
"x = 1 if 1 else break",
"x = 1 if 1 else continue"
]:
with self.subTest(stmt), self.assertRaisesRegex(SyntaxError, msg):
ast.parse(stmt)

def test_ifexp_body_stmt_orelse_expression(self):
msg = "statement given where 'body' expression required"
for stmt in [
"x = pass if 1 else 1",
"x = break if 1 else 1",
"x = continue if 1 else 1"
]:
with self.subTest(stmt), self.assertRaisesRegex(SyntaxError, msg):
ast.parse(stmt)

def test_ifexp_body_stmt_orelse_stmt(self):
msg = "statement given where 'body' expression required"
for stmt in [
"x = pass if 1 else pass",
"x = break if 1 else pass",
"x = continue if 1 else pass",
"x = continue if 1 else import ast"
]:
with self.subTest(stmt), self.assertRaisesRegex(SyntaxError, msg):
ast.parse(stmt)

def test_dict(self):
d = ast.Dict([], [ast.Name("x", ast.Load())])
self.expr(d, "same number of keys as values")
Expand Down
28 changes: 28 additions & 0 deletions Lib/test/test_syntax.py
Original file line number Diff line number Diff line change
Expand Up @@ -2863,6 +2863,34 @@ def test_match_stmt_invalid_as_expr(self):
end_offset=15 + len("obj.attr"),
)

def test_ifexp_else_stmt(self):
msg = "expected expression after 'else', but statement is given"
Comment thread
sergey-miryanov marked this conversation as resolved.
self._check_error("x = 1 if 1 else pass", msg)
self._check_error("x = 1 if 1 else return", msg)
self._check_error("x = 1 if 1 else return 2", msg)
self._check_error("x = 1 if 1 else raise Exception('a')", msg)
self._check_error("x = 1 if 1 else del a", msg)
self._check_error("x = 1 if 1 else yield 2", msg)
self._check_error("x = 1 if 1 else assert False", msg)
self._check_error("x = 1 if 1 else break", msg)
self._check_error("x = 1 if 1 else continue", msg)
self._check_error("x = 1 if 1 else import", msg)
self._check_error("x = 1 if 1 else import ast", msg)
self._check_error("x = 1 if 1 else from", msg)
self._check_error("x = 1 if 1 else from ast import *", msg)

def test_ifexp_body_stmt_else_expression(self):
msg = "expected expression before 'if', but statement is given"
self._check_error("x = pass if 1 else 1", msg)
self._check_error("x = break if 1 else 1", msg)
self._check_error("x = continue if 1 else 1", msg)

def test_ifexp_body_stmt_else_stmt(self):
msg = "expected expression before 'if', but statement is given"
self._check_error("x = pass if 1 else pass", msg)
self._check_error("x = break if 1 else pass", msg)
self._check_error("x = continue if 1 else pass", msg)
self._check_error("x = continue if 1 else import ast", msg)

def load_tests(loader, tests, pattern):
tests.addTest(doctest.DocTestSuite())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
Clarify syntax error messages for conditional expressions if statement
Comment thread
sergey-miryanov marked this conversation as resolved.
Outdated
specified in body and orelse parts.
specified before ``if`` or after ``else`` keywords.
Loading