January 1, 2015

Please use python's contextlib!

If you ever used python’s magical with statement, you’ve used the facilities of contextlib module. We all know that with is the idiomatic way of opening python files. with saves us the pain of closing an opened file resource manually. Handling files without using with looks like this:

f = open("myfile.txt")
do_something_with_content(f.read())
f.close()

With(!) the help of with statement, the code is simplified to this:

with open("myfile.txt") as f:
    do_something_with_content(f.read())

Although both snippets look similar, with has an implicit advantage. In case the do_something_with_content function raises an exception, the first snippet will leave the file open, while the second snippet will close it regardless.

We can make our own contexts/functions to work with with

This is what contextlib module is made for. For example, say we have a function prepare_dish. Every time we call prepare_dish, it serves us a prepared food. We need to wash the dishes after consuming the food. Sometimes we may get interrupted while eating, still we absolutely want to make sure that the dishes are clean no matter what. Here’s how to implement a context manager to serve this functionality -

from contextlib import contextmanager

def clean_dishes():
    print("Dishes cleaned.")

@contextmanager
def prepare_dish(food):
    prepared_food = "cooked {0}".format(food)

    try:
        # Serve the food to the `with` statement.
        yield prepared_food
    finally:
        # Clean the dishes.
        clean_dishes()


# Let's eat some rice!
with prepare_dish("rice") as food:
    print("Enjoying '{0}'!".format(food))

Running the above code snippet will output this -

Enjoying 'cooked rice'!
Dishes cleaned.

We didn’t ask for it, yet the dishes are cleaned afterwards because of using a context manager. Because we used try …​ finally construct inside the context manager, our code is also resilient to unexpected errors. Let’s test this.

with prepare_dish("daal") as food:
    raise RuntimeError("Meteor strike imminent!")

The output is:

Dishes cleaned.
Traceback (most recent call last):
  File "context_manager_example.py", line 19, in <module>
    raise RuntimeError("Meteor strike imminent!")
RuntimeError: Meteor strike imminent!

As expected, dishes are cleaned regardless of the RuntimeError exception.

Seriously, use them!

Context managers can make your python code more beautiful and safe. You can learn more about contextlib module from the official documentation at [python.org](https://docs.python.org/3/library/contextlib.html).

Tags: Python , Programming