I know it's bad style to re-raise an exception from within a context manager's __exit__()
method. So, I'd like to tack an attribute on the instance which can carry contextual information that isn't available if I let the exception trickle through or if I catch it. This will avoid re-raising it.
The alternative to tacking the attribute on the exception would be to swallow the exception, set some state on the instance that doubles as the context manager in question and later check that state. Problem is that this would lead to a catch 22, wouldn't it? Since the exception means that execution inside the with
block is being exited. There is no way to repeat the operation other than entering the with
block again, right? So the instance in which I am trying to store the contextual information would go away once the __exit__()
method returns.
So in short: how can I manipulate the actual exception that is pending (if it is, which I'll assume as given for this question) while in the __exit__()
method?
The __exit__ method takes care of releasing the resources occupied with the current code snippet. This method must be executed no matter what after we are done with the resources.
__exit__() is an abstract method which by default returns None . See also the definition of Context Manager Types. New in version 3.6. An abstract base class for classes that implement object.
Summary: Python calls the __enter__() magic method when starting a with block whereas the __exit__() method is called at the end. An object that implements both __enter__() and __exit__() methods is called a context manager. By defining those methods, you can create your own context manager.
The block of code which uses the acquired resource is placed inside the block of the with statement. As soon as the code inside the with block is executed, the __exit__() method is called. All the acquired resources are released in the __exit__() method. This is how we use the with statement with user defined objects.
The context manager doesn't go away just because the block exits. You can preserve it in two ways:
Create the context manager first, assign it to a variable, then use with
with that object:
cm = ContextManager()
with cm:
# ....
state = cm.attribute
Return the context manager itself from the __enter__
method, use with ... as ...
to bind that to a local name. That name is not unbound when with
exits:
with ContextManager() as cm:
# ....
state = cm.attribute
where ContextManager.__enter__
uses return self
.
You can also set extra attributes on the exception itself; no need to re-raise the exception:
>>> class ContextManager(object):
... def __enter__(self):
... return self
... def __exit__(self, tp, v, tb):
... if tp is None: return
... v.extra_attribute = 'foobar'
... self.other_extra_attribute = 'spam-n-ham'
...
>>> try:
... with ContextManager() as cm:
... raise ValueError('barfoo')
... except ValueError as ex:
... print vars(ex)
...
{'extra_attribute': 'foobar'}
>>> vars(cm)
{'other_extra_attribute': 'spam-n-ham'}
Here the exception was given an extra attribute that persisted all the way to the exception handler. In the above I also show that cm
is still bound to the context manager.
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