Python context manager not cleaning up

1.5k views Asked by At

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.

1

There are 1 answers

0
Simeon Visser On BEST ANSWER

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.