Skip to content

Commit 1acf7ff

Browse files
iritkatrielmiss-islington
authored andcommitted
gh-135629: rewrite language reference section on except* to improve clarity (GH-136150)
(cherry picked from commit a651ec9) Co-authored-by: Irit Katriel <[email protected]>
1 parent 13761d1 commit 1acf7ff

File tree

1 file changed

+32
-28
lines changed

1 file changed

+32
-28
lines changed

Doc/reference/compound_stmts.rst

Lines changed: 32 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -332,15 +332,29 @@ stored in the :mod:`sys` module is reset to its previous value::
332332
:keyword:`!except*` clause
333333
--------------------------
334334

335-
The :keyword:`!except*` clause(s) are used for handling
336-
:exc:`ExceptionGroup`\s. The exception type for matching is interpreted as in
337-
the case of :keyword:`except`, but in the case of exception groups we can have
338-
partial matches when the type matches some of the exceptions in the group.
339-
This means that multiple :keyword:`!except*` clauses can execute,
340-
each handling part of the exception group.
341-
Each clause executes at most once and handles an exception group
342-
of all matching exceptions. Each exception in the group is handled by at most
343-
one :keyword:`!except*` clause, the first that matches it. ::
335+
The :keyword:`!except*` clause(s) specify one or more handlers for groups of
336+
exceptions (:exc:`BaseExceptionGroup` instances). A :keyword:`try` statement
337+
can have either :keyword:`except` or :keyword:`!except*` clauses, but not both.
338+
The exception type for matching is mandatory in the case of :keyword:`!except*`,
339+
so ``except*:`` is a syntax error. The type is interpreted as in the case of
340+
:keyword:`!except`, but matching is performed on the exceptions contained in the
341+
group that is being handled. An :exc:`TypeError` is raised if a matching
342+
type is a subclass of :exc:`!BaseExceptionGroup`, because that would have
343+
ambiguous semantics.
344+
345+
When an exception group is raised in the try block, each :keyword:`!except*`
346+
clause splits (see :meth:`~BaseExceptionGroup.split`) it into the subgroups
347+
of matching and non-matching exceptions. If the matching subgroup is not empty,
348+
it becomes the handled exception (the value returned from :func:`sys.exception`)
349+
and assigned to the target of the :keyword:`!except*` clause (if there is one).
350+
Then, the body of the :keyword:`!except*` clause executes. If the non-matching
351+
subgroup is not empty, it is processed by the next :keyword:`!except*` in the
352+
same manner. This continues until all exceptions in the group have been matched,
353+
or the last :keyword:`!except*` clause has run.
354+
355+
After all :keyword:`!except*` clauses execute, the group of unhandled exceptions
356+
is merged with any exceptions that were raised or re-raised from within
357+
:keyword:`!except*` clauses. This merged exception group propagates on.::
344358

345359
>>> try:
346360
... raise ExceptionGroup("eg",
@@ -353,22 +367,18 @@ one :keyword:`!except*` clause, the first that matches it. ::
353367
caught <class 'ExceptionGroup'> with nested (TypeError(2),)
354368
caught <class 'ExceptionGroup'> with nested (OSError(3), OSError(4))
355369
+ Exception Group Traceback (most recent call last):
356-
| File "<stdin>", line 2, in <module>
357-
| ExceptionGroup: eg
370+
| File "<doctest default[0]>", line 2, in <module>
371+
| raise ExceptionGroup("eg",
372+
| [ValueError(1), TypeError(2), OSError(3), OSError(4)])
373+
| ExceptionGroup: eg (1 sub-exception)
358374
+-+---------------- 1 ----------------
359375
| ValueError: 1
360376
+------------------------------------
361377

362-
363-
Any remaining exceptions that were not handled by any :keyword:`!except*`
364-
clause are re-raised at the end, along with all exceptions that were
365-
raised from within the :keyword:`!except*` clauses. If this list contains
366-
more than one exception to reraise, they are combined into an exception
367-
group.
368-
369-
If the raised exception is not an exception group and its type matches
370-
one of the :keyword:`!except*` clauses, it is caught and wrapped by an
371-
exception group with an empty message string. ::
378+
If the exception raised from the :keyword:`try` block is not an exception group
379+
and its type matches one of the :keyword:`!except*` clauses, it is caught and
380+
wrapped by an exception group with an empty message string. This ensures that the
381+
type of the target ``e`` is consistently :exc:`BaseExceptionGroup`::
372382

373383
>>> try:
374384
... raise BlockingIOError
@@ -377,13 +387,7 @@ exception group with an empty message string. ::
377387
...
378388
ExceptionGroup('', (BlockingIOError()))
379389

380-
An :keyword:`!except*` clause must have a matching expression; it cannot be ``except*:``.
381-
Furthermore, this expression cannot contain exception group types, because that would
382-
have ambiguous semantics.
383-
384-
It is not possible to mix :keyword:`except` and :keyword:`!except*`
385-
in the same :keyword:`try`.
386-
The :keyword:`break`, :keyword:`continue`, and :keyword:`return` statements
390+
:keyword:`break`, :keyword:`continue` and :keyword:`return`
387391
cannot appear in an :keyword:`!except*` clause.
388392

389393

0 commit comments

Comments
 (0)