Python 3.2 has this lovely new contextlib.ContextDecorator mixin [1] for context manager classes that allows you to apply the context manager as a decorator. The docs for this feature include the note:
ContextDecorator is used by contextmanager(), so you get this functionality automatically. Sweet! So I tried this out: >>> @contextmanager ... def print_stuff(): ... print('Starting') ... try: ... yield 'Yielded' ... finally: ... print('Exiting') ... >>> @print_stuff() ... def test(): ... print('The bit in the middle') ... >>> test() Starting The bit in the middle Exiting So far so good. There seems to be no straight-forward way for the function to get access to the 'Yielded' value, but I suppose I can live with that. But then I tried calling the function again: >>> test() Traceback (most recent call last): File "c:\python32\lib\contextlib.py", line 28, in __enter__ return next(self.gen) StopIteration During handling of the above exception, another exception occurred: Traceback (most recent call last): File "<stdin>", line 1, in <module> File "c:\python32\lib\contextlib.py", line 15, in inner with self: File "c:\python32\lib\contextlib.py", line 30, in __enter__ raise RuntimeError("generator didn't yield") RuntimeError: generator didn't yield Whoops! The problem is that the same generator instance is used for both function calls, and since the first call has already exhausted the generator, the second call just fizzles. Now, you might think that this can be salvaged by looping inside the generator. But that doesn't work either: >>> @contextmanager ... def print_stuff(): ... while True: ... print('Starting') ... try: ... yield ... finally: ... print('Exiting') ... >>> @print_stuff() ... def test(): ... print('The bit in the middle') ... >>> test() Starting The bit in the middle Exiting Starting Traceback (most recent call last): File "<stdin>", line 1, in <module> File "c:\python32\lib\contextlib.py", line 16, in inner return func(*args, **kwds) File "c:\python32\lib\contextlib.py", line 39, in __exit__ raise RuntimeError("generator didn't stop") RuntimeError: generator didn't stop So as far as I can tell, generator-based context managers simply can't be used as ContextDecorators. Furthermore, the documentation's claim that they can is actually harmful, since they *appear* to work at first. Or am I simply missing something here? Cheers, Ian [1] http://docs.python.org/py3k/library/contextlib.html#contextlib.ContextDecorator -- http://mail.python.org/mailman/listinfo/python-list