context managers the easy way

About Me

Python instructor for Marakana

simeon@marakana.com

@simeonfranklin

http://simeonfranklin.com/blog/

Alternative title

with/contextmanagers

Everybody knows what with is about, right?

with open("foo.txt") as fp:
     for line in fp:
         if line.startswith("ERROR"):
              print line

fp.seek() # doesn't work, file handle is automatically closed by contextmanager

useful

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!

itty-bitty-living.gif

yield

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>
>>> gen.next()
first thing
>>> gen.next()
second thing
Traceback (most recent call last):
   ...
StopIteration

try/finally

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

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

Are my 5 minutes up yet?

decorators

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!

contextlib.contextmanager

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")
['contextlib.txt']

cool?

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

/

#