lemiant
lemiant

Reputation: 4355

Python context manager not cleaning up

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.

Upvotes: 3

Views: 1811

Answers (1)

Simeon Visser
Simeon Visser

Reputation: 122336

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.

Upvotes: 12

Related Questions