Brett Cannon: Unravelling `finally` and `else` from `try` statements

image

In the last post of my syntactic sugar series, I showed how you can get away with not having elif and else clauses on an if statement. It turns out you can use the same trick to help get rid of else clauses on try statements. And then there’s another trick we can use to get rid of finally clauses, making it so you only need try and except.

else

In the post on else clauses in if statements, we used a variable to track whether any other clauses of the overall if statement were executed. Since else clauses for try statements only execute if no exception is raised, we can record whether execution reaches the end of the try clause, signalling that no exception was raised. Take a simple example:

try:
    A
except:
    B
else:
    C
Example try statement with an else clause

We can mark whether A executes fully or not to control whether the else clause should execute.

_A_finished = False
try:
    A
    _A_finished = True
except:
    B
if _A_finished:
    C

finally

In contrast to else, we want finally to always execute. That makes our biggest concern being not whether some other code executed but making sure we execute the finally clause once, every time. The challenge then is how to run the code from the finally clause no matter what exception is raised as well as if no exception is raised?

Tackling the case of when an exception is raised, we should be able to wrap the entire try statement in an outer try statment with a catch-all except BaseException clause that contains the finally clause’s code. We can then use a bare raise to let the exception we initially caught to continue to propagate.

For the “no exception raised” case, we can just copy the code after our added try statement. That works because we insert that raise statement in our except clause to make sure that exceptions keeping going.

That’s a lot of words, so let’s move on to some code to try and help make sense of it all. With the following example:

try:
    A
except Exception:
    B
finally:
    C
Example try statement with a finally clause

we can transform it into:

try:
    try:
        A
    except Exception:
        B
except BaseException:
    C
    raise
C
Unravelling of the finally example

As you can see we leave the initial try statement alone except for removing the finally clause. We then duplicate the code in the finally clause so it will be run both in the exception-raised case and the no-exception case.

And with that, we can simply try statements down to just that and except clauses!

Author: Shantun Parmar

Leave a Reply

Your email address will not be published.