loading...
Cover image for Suppressing Exceptions in Python with contextlib.suppress, not try/except/pass

Suppressing Exceptions in Python with contextlib.suppress, not try/except/pass

bowmanjd profile image Jonathan Bowman ・2 min read

When programming, it is common to perform an operation that is expected to fail at times, but should not throw an error. In Python, try/except clauses are frequent. In human terms, try/except means, "try this operation, and if it fails for this reason, then do this other thing." For instance, you can try the following out in your console.

friends = {"Nguyen": "tacos", "Hannah": "borscht"}


def get_food(friend_name):
    try:
        food = friends[friend_name]
    except KeyError:
        food = "baklava"
        friends[friend_name] = food
    return food


get_food("Nguyen")
# "tacos"

get_food("Ahmed")
# "baklava"

friends
# {'Nguyen': 'tacos', 'Hannah': 'borscht', 'Ahmed': 'baklava'}
Enter fullscreen mode Exit fullscreen mode

The above in simple language: if the key isn't in the dictionary, don't fret, just add it with a default value.

What if you don't have an alternate course of action? What if you just want to silence the error? Let's say you want to make a directory, and if the directory is already there, don't whine about it, just ignore. Some developers use try/finally for this case as well:

import os

os.mkdir("frivolous_directory")

try:
    os.mkdir("frivolous_directory")
except FileExistsError:
    pass
Enter fullscreen mode Exit fullscreen mode

So, the above works, it is common practice, and one doesn't have to do any special imports. Awesome. No shame if you have good reason to use this pattern.

However, in human terms, the above try/except block reads like this: make the directory, but if that fails because it already exists, then perform the following action: um, nothing.

Crickets

So, from a utility standpoint, it works fine. From a semantic standpoint, it is not particularly meaningful.

A meaningful alternative: contextlib.suppress

If the desire is to simply suppress an error, rather than perform some sort of branching logic, the Python standard library has a paradigm for that: contextlib.suppress(). It works like this:

import contextlib
import os

os.mkdir("frivolous_directory")

with contextlib.suppress(FileExistsError):
    os.mkdir("frivolous_directory")
Enter fullscreen mode Exit fullscreen mode

The FileExistsError in the above was silenced.

This was two less lines of code than the previous try/except example (OK, we had to add import contextlib as well, so one less line in this example).

More importantly, it is very clear what is happening in the code. We don't mind if the operation fails for the specific reason given. There is no pass with unclear intent. We are not trying something nor are we seeking to intercept the error for further logic or processing.

Conclusion

This isn't so much about working code as it is about readable and reasonable code. Using contextlib.suppress is a worthwhile practice. Reading a bit about the treasures in contextlib may be worth your time as well!

Discussion

pic
Editor guide
Collapse
hanpari profile image
Pavel Morava

Oh my, that's a great discovery for me.
I agree with you that handling an exception with no follow-up makes more sense this way.

Collapse
bowmanjd profile image
Jonathan Bowman Author

I am so glad you find it interesting, too! I first encountered this while using the wemake python styleguide

Collapse
hanpari profile image
Pavel Morava

Interesting project.

  • Reformat code, since we believe that developers should do that

Actually, I am using Black formatter because I believe that I should not format any code myself.

Otherwise, the philosophy sounds about right. Thanks for the link.

Thread Thread
bowmanjd profile image
Jonathan Bowman Author

Agreed. I use black, too. I don't actually use the wemake python styleguide much, but I did use it on a project once and learned a lot.

I do use a variety of Flake8 plugins, though, along with Black and Mypy (and sometimes PyRight).

Thread Thread
hanpari profile image
Pavel Morava

Black + MyPy as well. I set Pylama for my project, but it keeps complaining, so I usually stick only with MyPy.