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
ExceptionGroupis raised, it's checked against eachexcept*block. except* KeyErrorblock catchesExceptionGroupthat containsKeyError.- The matched
except*block receives not the wholeExceptionGroupbut 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/0in 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
ExceptionGroupwith them is raised. So,ExceptionGroup('', [OSError()])was raised (and beautifully formatted).