[Python-ideas] An improved `ContextManager`

cool-RR cool-rr at cool-rr.com
Tue Jan 4 03:28:25 CET 2011

Hello folks.

Ever since Michael Foord talked about `ContextDecorator` in python-ideas
I've been kicking around an idea for my own take on it. It's a
`ContextManager` class which provides the same thing that Foord's
`ContextDecorator` does, but also provides a few more goodies, chief of
which being the `manage_context` method.

I've been working on this for a few days and I think it's ready for review.
It's well-tested and extensively documented. I started using it wherever I
have context managers in GarlicSim.

I'll be happy to get your opinions on my approach and any critiques you may
have. If there are no problems with this approach, I'll probably release it
with GarlicSim 0.6.1 and blog about it.

Here is my `context_manager`
Here are its tests<https://github.com/cool-RR/GarlicSim/tree/first_context_manager_review/garlicsim/test_garlicsim/test_general_misc/test_context_manager>

Following is the module's docstring which explains the module in more


Defines the `ContextManager` and `ContextManagerType` classes.

These classes allow for greater freedom both when (a) defining context
and when (b) using them.

Inherit all your context managers from `ContextManager` (or decorate your
generator functions with `ContextManagerType`) to enjoy all the benefits
described below.

Defining context managers

There are 3 different ways in which context managers can be defined, and
has their own advantages and disadvantages over the others.

 1. The classic way to define a context manager is to define a class with
    `__enter__` and `__exit__` methods. This is allowed, and if you do this
    you should still inherit from `ContextManager`. Example:

        class MyContextManager(ContextManager):
            def __enter__(self):
                pass # preparation
            def __exit__(self, type_=None, value=None, traceback=None):
                pass # cleanup

 2. As a decorated generator, like so:

        def MyContextManager():
                pass # clean-up

    This usage is nothing new; It's also available when using the standard
    library's `contextlib.contextmanager` decorator. One thing that is
    here that `contextlib` doesn't allow is to yield the context manager
    by doing `yield SelfHook`.

 3. The third and novel way is by defining a class with a `manage_context`
    method which returns a decorator. Example:

        class MyContextManager(ContextManager):
            def manage_context(self):
                    with some_lock:
                        yield self

    This approach is sometimes cleaner than defining `__enter__` and
    `__exit__`; Especially when using another context manager inside
    `manage_context`. In our example we did `with some_lock` in our
    `manage_context`, which is shorter and more idiomatic than calling
    `some_lock.__enter__` in an `__enter__` method and `some_lock.__exit__`
    an `__exit__` method.

These were the different ways of *defining* a context manager. Now let's see
the different ways of *using* a context manager:

Using context managers

There are 2 different ways in which context managers can be used:

 1. The plain old honest-to-Guido `with` keyword:

       with MyContextManager() as my_context_manager:

 2. As a decorator to a function

        def do_stuff():
           pass # doing stuff

    When the `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
    with all the other goodies given by `ContextManager`.

That's it. Inherit all your context managers from `ContextManager` (or
your generator functions with `ContextManagerType`) to enjoy all these

Ram Rachum
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20110104/8167d08d/attachment.html>

More information about the Python-ideas mailing list