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
__enter__ and [__exit__] both are methods that are invoked on entry to and exit from the body of "the with statement" (PEP 343) and implementation of both is called context manager.
Context managers allow you to allocate and release resources precisely when you want to. The most widely used example of context managers is the with statement. Suppose you have two related operations which you'd like to execute as a pair, with a block of code in between.
The contextlib module of Python's standard library provides utilities for resource allocation to the with statement. The with statement in Python is used for resource management and exception handling. Therefore, it serves as a good Context Manager.
An ExitStack is (as the name suggests) a stack of clean-up functions. Adding a callback to the stack is the equivalent of calling Go's defer statement. However, clean-up functions are not executed when the function returns, but when execution leaves the with block - and until then, the stack can also be emptied again.
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
.
No. If there is the chance that an exception could occur in __enter__()
then you will need to catch it yourself and call a helper function that contains the cleanup code.
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