Allow `break` and `return` inside `except*` clause

PEP 654 (Exception Groups) forbids usage of break, return and continue inside the new except* block. The reason for this given in the PEP is that

exceptions in an ExceptionGroup are assumed to be independent, and the presence or absence of one of them should not impact handling of the others

I think the first assumption i.e. that exceptions are independent is sensible. Whether that should also directly lead to the conclusion drawn in the PEP that presence or absence of one of them should not impact handling of the others I am less sure about.

Take for example:

try:
    ...
except* CriticalError as eg:
    ...
    return  # currently not allowed, raises SyntaxError
except* NonCriticalError as eg:
    ...

With the current limition one would have to write the above as something like this:

critical_error_encountered = False
try:
    ...
except* CriticalError as eg:
    ...
    critical_error_encountered = True
except* NonCriticalError as eg:
    if critical_error_encountered:
        pass
    else:
        ...

Similarly for breaking out of a loop that catches exception groups:

for i in ... :
    try:
        ...
    except* SomeError:
        break  # currently not allowed

which currently has to be written like this:

should_break = False
for i in ... :
    try:
        ...
    except* SomeError:
        should_break = True
    if should_break:
        break
1 Like

Do you actually have a real use example where this cramps your style? Or are you just arguing from a point of consistency?

IIRC the PEP authors thought that it was difficult to implement and couldn’t think of a real-world use cases where it would matter. It’s always easier to allow it later than to have to withdraw it once it’s been supported.

3 Likes

To me, the meaning of return/break/continue (go somewhere and continue normal execution) is inconsistent with the meaning of ‘except * x’ (handle the subgroup x within the exception group) combined with the idea that an exception group (of unrelated exceptions) is not handled unless all the subgroups are handled. For return/break/continue to have effect, they would have to mean that all other subgroups are considered to be handled. Currently, an exception group is reraised if any subgroups are left unhandled after the last except* executed. In other words, the CriticalError example looks to me like the return should fail if there are any NonCriticalErrors, causing the exception group to be reraised as the try statement is finished.

Anyone is free to directly catch the exception group with a normal except ExceptionGroup as e:, just as Trio people had to do with Trio’s prior version of an exception group. The ExceptionGroup is then caught as a whole and there no question of implicit re-raising. The suite could then implement ‘if CriticalError: whatever; return else: ’.

1 Like