I love context managers, and I love the
with keyword. If you’ve
never dealt with context managers or
with, here’s a practical guide
which explains how to use them. You may also read the more official PEP 343 which introduced these features to the language.
with and context managers in your code contributes a lot to making your code more beautiful and maintainable. Every time you replace a
finally clause with a
with clause, an angel gets a pair of wings.
Now, you don’t need any official
ContextManager class in order to
use context managers or define them; you just need to define
__exit__() methods in your class, and then you
can use your class as a context manager. But, if you use the
ContextManager class as a base class to your context manager class,
you could enjoy a few more features that might make your code a bit more
concise and elegant.
ContextManager class allows using context managers as decorators
(in addition to their normal use) and supports writing context managers in a
new form called
manage_context(). (As well as the original forms).
First let’s import:
>>> from python_toolbox import context_management
Now let’s go over the features one by one.
ContextManager class allows you to define context managers in
new ways and to use context managers in new ways. I’ll explain both of
these; let’s start with defining context managers.
There are 3 different ways in which context managers can be defined, and each has their own advantages and disadvantages over the others.
The classic way to define a context manager is to define a class with
__exit__() methods. This is allowed, and if you
do this you should still inherit from
>>> class MyContextManager(context_management.ContextManager): ... def __enter__(self): ... pass # preparation ... def __exit__(self, type_=None, value=None, traceback=None): ... pass # cleanup
As a decorated generator, like so:
>>> @context_management.ContextManagerType ... def MyContextManager(): ... # preparation ... try: ... yield ... finally: ... pass # cleanup
The advantage of this approach is its brevity, and it may be a good fit for
relatively simple context managers that don’t require defining an actual class.
This usage is nothing new; it’s also available when using the standard
contextlib.contextmanager() decorator. One thing that is
allowed here that
contextlib doesn’t allow is to yield the context
manager itself by doing
The third and novel way is by defining a class with a
method which returns a decorator. Example:
>>> class MyContextManager(ContextManager): ... def manage_context(self): ... do_some_preparation() ... with other_context_manager: ... yield self
This approach is sometimes cleaner than defining
__exit__(); especially when using another context manager inside
manage_context(). In our example we did
manage_context(), which is shorter, more idiomatic and less
double-underscore-y than the equivalent classic definition:
>>> class MyContextManager(object): ... def __enter__(self): ... do_some_preparation() ... other_context_manager.__enter__() ... return self ... def __exit__(self, *exc): ... return other_context_manager.__exit__(*exc)
Another advantage of the
manage_context() approach over
__exit__() is that it’s better at handling
exceptions, since any exceptions would be raised inside
manage_context() where we could
except them, which is much
more idiomatic than the way
__exit__() handles exceptions, which is by
receiving their type and returning whether to swallow them or not.
These were the different ways of defining a context manager. Now let’s see the different ways of using a context manager:
There are 2 different ways in which context managers can be used:
The plain old honest-to-Guido
>>> with MyContextManager() as my_context_manager: ... do_stuff()
As a decorator to a function:
>>> @MyContextManager() ... def do_stuff(): ... pass # doing stuff
do_stuff function will be called, the context manager will be
used. This functionality is also available in the standard library of Python
3.2+ by using
contextlib.ContextDecorator, but here it is
combined with all the other goodies given by
Another advantage that
ContextManager has over
contextlib.ContextDecorator is that
it uses Michele Simionato’s excellent decorator module to preserve the
decorated function’s signature.
That’s it. Inherit all your context managers from
decorate your generator functions with
ContextManagerType) to enjoy
all of these benefits.