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 — returnTrueto suppress,Falseto re-raise
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