Catching exception in context manager __enter__()

contextmanager, exception, python, python-2.7, with-statement

Solution

Like this:

import sys

class Context(object):
    def __enter__(self):
        try:
            raise Exception("Oops in __enter__")
        except:
            # Swallow exception if __exit__ returns a True value
            if self.__exit__(*sys.exc_info()):
                pass
            else:
                raise


    def __exit__(self, e_typ, e_val, trcbak):
        print "Now it's running"


with Context():
    pass

To let the program continue on its merry way without executing the context block you need to inspect the context object inside the context block and only do the important stuff if `__enter__` succeeded.

class Context(object):
    def __init__(self):
        self.enter_ok = True

    def __enter__(self):
        try:
            raise Exception("Oops in __enter__")
        except:
            if self.__exit__(*sys.exc_info()):
                self.enter_ok = False
            else:
                raise
        return self

    def __exit__(self, e_typ, e_val, trcbak):
        print "Now this runs twice"
        return True


with Context() as c:
    if c.enter_ok:
        print "Only runs if enter succeeded"

print "Execution continues"

As far as I can determine, you can't skip the with-block entirely. And note that this context now swallows all exceptions in it. If you wish not to swallow exceptions if `__enter__` succeeds, check `self.enter_ok` in `__exit__` and `return False` if it's `True`.

Problem

Is it possible to ensure the `__exit__()` method is called even if there is an exception in `__enter__()`? ``` >>> class TstContx(object): ... def __enter__(self): ... raise Exception('Oops in __enter__') ... ... def __exit__(self, e_typ, e_val, trcbak): ... print "This isn't running" ... >>> with TstContx(): ... pass ... Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 3, in __enter__ Exception: Oops in __enter__ >>> ``` Edit This is as close as I could get... ``` class TstContx(object): def __enter__(self): try: # __enter__ code except Exception as e self.init_exc = e return self def __exit__(self, e_typ, e_val, trcbak): if all((e_typ, e_val, trcbak)): raise e_typ, e_val, trcbak # __exit__ code with TstContx() as tc: if hasattr(tc, 'init_exc'): raise tc.init_exc # code in context ``` In hind sight, a context manager might have not been the best design decision

Original source

Related problems