context managers the easy way

About Me

Python instructor for Marakana


Alternative title


Everybody knows what with is about, right?

with open("foo.txt") as fp:
     for line in fp:
         if line.startswith("ERROR"):
              print line # doesn't work, file handle is automatically closed by contextmanager


This is a cool idea. It’s like decorators for blocks of code instead of functions!

Any time you have a before/after or do/undo kind of API. Especially if the after/undo part should happen no matter what!

Programmers always follow instructions, so you can just tell people to make sure to put the .after() call in a finally block, right?

For example:

but a touch verbose

  1. define a class with an __enter__ and __exit__ method.

  2. __enter__ is the before method and can return the value you catch with the as clause.

  3. __exit__ is the after method and accepts three arguments (exception_type, exception_instance, traceback). It can return True to supress exceptions that occur in the with block.

  4. Use the __init__ to catch arguments to your contextmanager (filename to open, etc)

We can do better shorter

But it takes several concepts packed into a tight space.

Phenomenal cosmic power!



For the purposes of this lightning talk, yield is a return that pauses the function instead of ending it.

Functions with yield in them aren’t really functions.

Think of them as constructors for generator objects.

>>> def do_two_things():
...     print "first thing"
...     yield
...     print "second thing"
>>> gen = do_two_things()
>>> gen
<generator object do_two_things at 0x11078db90>
first thing
second thing
Traceback (most recent call last):


I don’t really have to explain this, do I?

    print 5/0 # usually doing something stupid crashes my program
    print "But this line always runs no matter what!"

Are my 5 minutes up yet?


If you don’t understand them… magic applied to functions.

>>> class Foo(object):
...     @staticmethod
...     def normal_function(): # notice no self! Magic!
...         print "Usually leaving off self is a mistake!"
>>> Foo.normal_function()
Usually leaving off self is a mistake!


A decorator that can build the contextmanager for you!

Et. Voila!

>>> from contextlib import contextmanager
>>> import os, glob
>>> @contextmanager
... def cd(path):
...     try:
...         cur = os.getcwd()
...         os.chdir(os.path.expanduser(path))
...         yield # end the "before" stuff. Could yield a value
...     finally: # use try/finally in case the block crashes
...         os.chdir(cur) # this is the "after" stuff
>>> with cd("~/work/talks"):
...     print glob.glob("*txt")


questions? (I bet my 5 minutes is up!)