When I raise any exceptions inside a context manager the cleanup code is not run. For example:
from contextlib import contextmanager
try:
raise BaseException()
except BaseException:
print "bye from except"
@contextmanager
def say_goodbye():
yield
print "bye from context manager"
with say_goodbye():
raise BaseException()
Will output:
bye from except
Traceback (most recent call last):
File "", line 15, in
BaseException
Notice that the try/except properly catches the exception while the with statement does not. Is there something I don't understand about how with statements are supposed to work?
You can see the code in a fiddle here: http://pythonfiddle.com/context-manager-failing
FYI I'm running python 2.7 on OSX mavericks. Although I've been able to reproduce in many environments so I doubt that has much to do with it.
You'll need to add exception handling yourself:
@contextmanager
def say_goodbye():
try:
yield
finally:
print "bye from context manager"
The above ensures the finally
block always runs regardless of an exception inside the with block. If you want exceptions to be handled you'll need to catch those and possible reraise them:
@contextmanager
def say_goodbye():
try:
yield
except Exception as exc:
print(exc)
finally:
print "bye from context manager"
As the documentation says:
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.
Also note that BaseException
is not an Exception
:
>>> isinstance(BaseException(), Exception)
False
Your own exceptions should inherit from Exception
, not BaseException
.