except*
PEP 654 introduced not only ExceptionGroup
itself but also a new syntax to handle it. Let's start right with an example:
try:
raise ExceptionGroup('', [
ValueError(),
KeyError('hello'),
KeyError('world'),
OSError(),
])
except* KeyError as e:
print('caught1:', repr(e))
except* ValueError as e:
print('caught2:', repr(e))
except* KeyError as e:
1/0
The output:
caught1: ExceptionGroup('', [KeyError('hello'), KeyError('world')])
caught2: ExceptionGroup('', [ValueError()])
+ Exception Group Traceback (most recent call last):
| File "<stdin>", line 2, in <module>
| ExceptionGroup: (1 sub-exception)
+-+---------------- 1 ----------------
| OSError
+------------------------------------
This is what happened:
- When
ExceptionGroup
is raised, it's checked against eachexcept*
block. except* KeyError
block catchesExceptionGroup
that containsKeyError
.- The matched
except*
block receives not the wholeExceptionGroup
but its copy containing only matched sub-exceptions. In case ofexcept* KeyError
, it includes bothKeyError('hello')
andKeyError('world')
- For each sub-exception, only the first match is executed (
1/0
in the example wasn't reached). - While there are unmatched sub-exceptions, they will be tried to match to remaining
except*
blocks. - If there are still sub-exceptions left after all of that, the
ExceptionGroup
with them is raised. So,ExceptionGroup('', [OSError()])
was raised (and beautifully formatted).