I happened upon a post whereby the author has:
a number of functions that all do very different things, but they all create and close a "context" [code example] I'd rather avoid the code duplication, and the danger of forgetting that try/finally block and the close().
The author's implementation was rather clever: using decorators to magically add the context, invoke the function passing the context instance as a keyword and then destroying the context after the function invocation. However, as the poster suggests, it's kinda kludgy and not explicit — thereby violating one of the tenets of The Zen of Python.
But, what's interesting, in some ways he basically invented the
with statement introduced in Python 2.5 without knowing it. I really like
with because it solves the often needed sequence of open-call-close rather abstractly and in quite a Pythonic way. My
with solution shell:
from __future__ import with_statement # needed in 2.5, not in 2.6+ import logging import traceback logging.basicConfig(level=logging.INFO) class Context(object): def __init__(self, i): self.i = i logging.info("(%d) initializing", self.i) def __enter__(self): logging.info("(%d) entering", self.i) def __exit__(self, exc_type, exc_val, exc_tb): logging.error("(%d) exiting", self.i) if exc_type: s = traceback.format_exception(exc_type, exc_val, exc_tb) logging.error("".join(s)) return True def g(): for i in range (4): with Context(i) as ctx: if i == 2: # for demonstration purposes let's raise raise ValueError(i) g()
The two magic methods required to participant in context management are:
__enter__: invoked when entering the context
__exit__: invoked when leaving the context; if the exception arguments are non-
Nonean exception was raised — return
Running the code yields:
$ python b.py INFO:root:(0) initializing INFO:root:(0) entering ERROR:root:(0) exiting INFO:root:(1) initializing INFO:root:(1) entering ERROR:root:(1) exiting INFO:root:(2) initializing INFO:root:(2) entering ERROR:root:(2) exiting ERROR:root:Traceback (most recent call last): File "b.py", line 26, in g raise ValueError(i) ValueError: 2 INFO:root:(3) initializing INFO:root:(3) entering ERROR:root:(3) exiting