A better contextlib.contextmanager
Michele Simionato
michele.simionato at gmail.com
Wed May 23 02:21:35 EDT 2012
Python 3.2 enhanced contextlib.contextmanager so that it is possible to
use a context manager as a decorator. For instance, given the
contextmanager factory below
@contextmanager
def before_after():
print(before)
yield
print(after)
it is possibile to use it to generate decorators:
@before_after()
def hello(user):
print('hello', user)
This is equivalent to the more traditional
def hello(user):
with before_after():
print('hello', user)
but it has the advantage of saving a level of indentation and we all
know that flat is better than nested. Cool, but there are three issues:
1. I am using Python 2.7, not Python 3.2, so contextmanager has not such feature
2. The contextmanager decorator is losing the signature of the before_after factory:
>>> help(before_after)
Help on function before_after in module __main__:
before_after(*args, **kwds)
3. before_after() decorators do not preserve the signature of the decorated function either.
Since I am the author of the decorator module I have easily found out a recipe to solve both issues. Here it is:
from decorator import decorator, FunctionMaker
from contextlib import GeneratorContextManager
class GeneratorCM(GeneratorContextManager):
def __call__(self, func):
return FunctionMaker.create(
func, "with _cm_: return _func_(%(shortsignature)s)",
dict(_cm_=self, _func_=func), __wrapped__=func)
@decorator
def contextmanager(func, *args, **kwds):
return GeneratorCM(func(*args, **kwds))
before_after() objects obtained by using this version of
contextmanager become signature-preserving decorators. I am not going
to explain how FunctionMaker performs its magic (hint: it uses eval),
but I am asking a question instead: should I add this feature to the
next release of the decorator module? Do people use the
context-manager-as-decorator functionality? It is quite handy in unit
tests and actually I think it came from the unittest2 module. But I am
reluctant to complicate the API of the module, which currently is
really really small and such has been for many years.
More information about the Python-list
mailing list