DEV Community

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

Posted on

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

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!

Top comments (3)

Collapse
 
bowmanjd profile image
Jonathan Bowman

I haven't heard that one. I am curious if you have any links to references saying this?

If it is slower, I can't imagine it would be much slower. Typically, exception handling isn't the primary source of slowness in a Python program.

 
bowmanjd profile image
Jonathan Bowman

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).

Collapse
 
bowmanjd profile image
Jonathan Bowman

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