[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`
module<https://github.com/cool-RR/GarlicSim/blob/first_context_manager_review/garlicsim/garlicsim/general_misc/context_manager.py>.
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
detail.
Ram.
Defines the `ContextManager` and `ContextManagerType` classes.
These classes allow for greater freedom both when (a) defining context
managers
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
each
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:
@ContextManagerType
def MyContextManager():
try:
yield
finally:
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
allowed
here that `contextlib` doesn't allow is to yield the context manager
itself
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):
do_some_preparation()
try:
with some_lock:
yield self
finally:
do_some_cleanup()
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__`
in
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:
do_stuff()
2. As a decorator to a function
@MyContextManager()
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
combined
with all the other goodies given by `ContextManager`.
That's it. Inherit all your context managers from `ContextManager` (or
decorate
your generator functions with `ContextManagerType`) to enjoy all these
benefits.
--
Sincerely,
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