`raise None` should be a no-op

This is an example from PEP 654:

try:
    low_level_os_operation()
except* OSError as errors:
    exc = errors.subgroup(lambda e: e.errno != errno.EPIPE)
    if exc is not None:
        raise exc from None

I find that conditional raise at the end quite smelly. Since subgroup can return None (instead of an empty exception group, which is forbidden), I believe allowing raise None would simplify a lot of code. For instance, that example would become just:

try:
    low_level_os_operation()
except* OSError as errors:
    raise errors.subgroup(lambda e: e.errno != errno.EPIPE) from None

I don’t see much of a back-compatibility problem here, but this isn’t even mentioned in the PEP’s rejected ideas.

In the end, I see it as quite pythonic that, since we can now “raise any number of exceptions at once”, we should also be able to “raise no exception at all”.

Ok, reading it back out loud I can already spot a problem. In my proposal:

try:
    low_level_os_operation()
except* OSError as errors:
    raise errors.subgroup(lambda e: e.errno != errno.EPIPE) from None

the last line is a conditional raise, only now I am hiding the conditional nature of the raise. Explicit is better than implicit; I reckon it’s useful that whenever we see a raise statement, it’s definitely a raise…

or is it? Most raise statements would continue being raise SomeKindOfError("message"), and complex raises like raise result_of_some_function() would be buried inside code that deals specifically with hacking error handling, maybe? But then again, if the impact wouldn’t be wide, it wouldn’t be worth it changing the rule to allow raise None?

I don’t know what I think about this anymore, but I’m curious why it wasn’t even considered in the first place.

It would be errorprone. Forget a return at the end of your function that return an exception, or forget to check whether there is an exception, and your raise turns into pumpkin.

4 Likes