In my code, I need to be able to open and close a device properly, and therefore see the need to use a context manager. While a context manager is usually defined as a class with __enter__
and __exit__
methods, there also seem to be the possibility to decorate a function for use with the context manager (see a recent post and another nice example here).
In the following (working) code snippet, I have implemented the two possibilities; one just need to swap the commented line with the other one:
import time
import contextlib
def device():
return 42
@contextlib.contextmanager
def wrap():
print("open")
yield device
print("close")
return
class Wrap(object):
def __enter__(self):
print("open")
return device
def __exit__(self, type, value, traceback):
print("close")
#with wrap() as mydevice:
with Wrap() as mydevice:
while True:
time.sleep(1)
print mydevice()
What I try is to run the code and stop it with CTRL-C
. When I use the Wrap
class in the context manager, the __exit__
method is called as expeced (the text 'close' is printed in the terminal), but when I try the same thing with the wrap
function, the text 'close' is not printed to the terminal.
My question: Is there a problem with the code snippet, am I missing something, or why is the line print("close")
not called with the decorated function?
The example in the documentation for contextmanager
is somewhat misleading. The portion of the function after yield
does not really correspond to the __exit__
of the context manager protocol. The key point in the documentation is this:
If an unhandled exception occurs in the block, it is reraised inside the generator at the point where the yield occurred. Thus, you can use a
try...except...finally
statement to trap the error (if any), or ensure that some cleanup takes place.
So if you want to handle an exception in your contextmanager-decorated function, you need to write your own try
that wraps the yield
and handle the exceptions yourself, executing cleanup code in a finally
(or just block the exception in except
and execute your cleanup after the try/except
). For example:
@contextlib.contextmanager
def cm():
print "before"
exc = None
try:
yield
except Exception, exc:
print "Exception was caught"
print "after"
if exc is not None:
raise exc
>>> with cm():
... print "Hi!"
before
Hi!
after
>>> with cm():
... print "Hi!"
... 1/0
before
Hi!
Exception was caught
after
This page also shows an instructive example.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With