Skip to content

gh-131831: Implement PEP 758 – Allow except and except* expressions without parentheses #131833

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Apr 1, 2025
Merged
Show file tree
Hide file tree
Changes from all 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: 4 additions & 1 deletion Doc/reference/compound_stmts.rst
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,8 @@ Additional information on exceptions can be found in section :ref:`exceptions`,
and information on using the :keyword:`raise` statement to generate exceptions
may be found in section :ref:`raise`.

.. versionchanged:: next
Support for optionally dropping grouping parentheses when using multiple exception types. See :pep:`758`.

.. _except:

Expand All @@ -247,7 +249,8 @@ An expression-less :keyword:`!except` clause, if present, must be last;
it matches any exception.

For an :keyword:`!except` clause with an expression, the
expression must evaluate to an exception type or a tuple of exception types.
expression must evaluate to an exception type or a tuple of exception types. Parentheses
can be dropped if multiple exception types are provided and the ``as`` clause is not used.
The raised exception matches an :keyword:`!except` clause whose expression evaluates
to the class or a :term:`non-virtual base class <abstract base class>` of the exception object,
or to a tuple that contains such a class.
Expand Down
27 changes: 27 additions & 0 deletions Doc/whatsnew/3.14.rst
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,33 @@ If you encounter :exc:`NameError`\s or pickling errors coming out of
New features
============

.. _whatsnew314-pep758:

PEP 758 – Allow except and except* expressions without parentheses
------------------------------------------------------------------

The :keyword:`except` and :keyword:`except* <except_star>` expressions now allow
parentheses to be omitted when there are multiple exception types and the ``as`` clause is not used.
For example the following expressions are now valid:

.. code-block:: python

try:
release_new_sleep_token_album()
except AlbumNotFound, SongsTooGoodToBeReleased:
print("Sorry, no new album this year.")

# The same applies to except* (for exception groups):
try:
release_new_sleep_token_album()
except* AlbumNotFound, SongsTooGoodToBeReleased:
print("Sorry, no new album this year.")

Check :pep:`758` for more details.

(Contributed by Pablo Galindo and Brett Cannon in :gh:`131831`.)


.. _whatsnew314-pep649:

PEP 649: deferred evaluation of annotations
Expand Down
20 changes: 12 additions & 8 deletions Grammar/python.gram
Original file line number Diff line number Diff line change
Expand Up @@ -435,14 +435,18 @@ try_stmt[stmt_ty]:

except_block[excepthandler_ty]:
| invalid_except_stmt_indent
| 'except' e=expression t=['as' z=NAME { z }] ':' b=block {
_PyAST_ExceptHandler(e, (t) ? ((expr_ty) t)->v.Name.id : NULL, b, EXTRA) }
| 'except' e=expressions ':' b=block {
_PyAST_ExceptHandler(e, NULL, b, EXTRA) }
| 'except' e=expression 'as' t=NAME ':' b=block {
_PyAST_ExceptHandler(e, ((expr_ty) t)->v.Name.id, b, EXTRA) }
| 'except' ':' b=block { _PyAST_ExceptHandler(NULL, NULL, b, EXTRA) }
| invalid_except_stmt
except_star_block[excepthandler_ty]:
| invalid_except_star_stmt_indent
| 'except' '*' e=expression t=['as' z=NAME { z }] ':' b=block {
_PyAST_ExceptHandler(e, (t) ? ((expr_ty) t)->v.Name.id : NULL, b, EXTRA) }
| 'except' '*' e=expressions ':' b=block {
_PyAST_ExceptHandler(e, NULL, b, EXTRA) }
| 'except' '*' e=expression 'as' t=NAME ':' b=block {
_PyAST_ExceptHandler(e, ((expr_ty) t)->v.Name.id, b, EXTRA) }
| invalid_except_star_stmt
finally_block[asdl_stmt_seq*]:
| invalid_finally_stmt
Expand Down Expand Up @@ -1356,16 +1360,16 @@ invalid_try_stmt:
| 'try' ':' block* except_star_block+ a='except' [expression ['as' NAME]] ':' {
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "cannot have both 'except' and 'except*' on the same 'try'") }
invalid_except_stmt:
| 'except' a=expression ',' expressions ['as' NAME ] ':' {
RAISE_SYNTAX_ERROR_STARTING_FROM(a, "multiple exception types must be parenthesized") }
| 'except' a=expression ',' expressions 'as' NAME ':' {
RAISE_SYNTAX_ERROR_STARTING_FROM(a, "multiple exception types must be parenthesized when using 'as'") }
| a='except' expression ['as' NAME ] NEWLINE { RAISE_SYNTAX_ERROR("expected ':'") }
| a='except' NEWLINE { RAISE_SYNTAX_ERROR("expected ':'") }
| 'except' expression 'as' a=expression {
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(
a, "cannot use except statement with %s", _PyPegen_get_expr_name(a)) }
invalid_except_star_stmt:
| 'except' '*' a=expression ',' expressions ['as' NAME ] ':' {
RAISE_SYNTAX_ERROR_STARTING_FROM(a, "multiple exception types must be parenthesized") }
| 'except' '*' a=expression ',' expressions 'as' NAME ':' {
RAISE_SYNTAX_ERROR_STARTING_FROM(a, "multiple exception types must be parenthesized when using 'as'") }
| a='except' '*' expression ['as' NAME ] NEWLINE { RAISE_SYNTAX_ERROR("expected ':'") }
| a='except' '*' (NEWLINE | ':') { RAISE_SYNTAX_ERROR("expected one or more exception types") }
| 'except' '*' expression 'as' a=expression {
Expand Down
4 changes: 4 additions & 0 deletions Lib/test/test_grammar.py
Original file line number Diff line number Diff line change
Expand Up @@ -1453,6 +1453,8 @@ def test_try(self):
try: 1/0
except (EOFError, TypeError, ZeroDivisionError): pass
try: 1/0
except EOFError, TypeError, ZeroDivisionError: pass
try: 1/0
except (EOFError, TypeError, ZeroDivisionError) as msg: pass
try: pass
finally: pass
Expand All @@ -1476,6 +1478,8 @@ def test_try_star(self):
try: 1/0
except* (EOFError, TypeError, ZeroDivisionError): pass
try: 1/0
except* EOFError, TypeError, ZeroDivisionError: pass
try: 1/0
except* (EOFError, TypeError, ZeroDivisionError) as msg: pass
try: pass
finally: pass
Expand Down
38 changes: 5 additions & 33 deletions Lib/test/test_syntax.py
Original file line number Diff line number Diff line change
Expand Up @@ -1667,28 +1667,14 @@
SyntaxError: invalid syntax

Check that an multiple exception types with missing parentheses
raise a custom exception

>>> try:
... pass
... except A, B:
... pass
Traceback (most recent call last):
SyntaxError: multiple exception types must be parenthesized

>>> try:
... pass
... except A, B, C:
... pass
Traceback (most recent call last):
SyntaxError: multiple exception types must be parenthesized
raise a custom exception only when using 'as'

>>> try:
... pass
... except A, B, C as blech:
... pass
Traceback (most recent call last):
SyntaxError: multiple exception types must be parenthesized
SyntaxError: multiple exception types must be parenthesized when using 'as'

>>> try:
... pass
Expand All @@ -1697,29 +1683,15 @@
... finally:
... pass
Traceback (most recent call last):
SyntaxError: multiple exception types must be parenthesized
SyntaxError: multiple exception types must be parenthesized when using 'as'


>>> try:
... pass
... except* A, B:
... pass
Traceback (most recent call last):
SyntaxError: multiple exception types must be parenthesized

>>> try:
... pass
... except* A, B, C:
... pass
Traceback (most recent call last):
SyntaxError: multiple exception types must be parenthesized

>>> try:
... pass
... except* A, B, C as blech:
... pass
Traceback (most recent call last):
SyntaxError: multiple exception types must be parenthesized
SyntaxError: multiple exception types must be parenthesized when using 'as'

>>> try:
... pass
Expand All @@ -1728,7 +1700,7 @@
... finally:
... pass
Traceback (most recent call last):
SyntaxError: multiple exception types must be parenthesized
SyntaxError: multiple exception types must be parenthesized when using 'as'

Custom exception for 'except*' without an exception type

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Add support for optionally dropping grouping parentheses when using multiple
exception types as per :pep:`758`. Patch by Pablo Galindo
Loading
Loading