How to manipulate the exception in __exit__ of a context manager?

浪尽此生 提交于 2019-12-04 11:35:45

问题


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?


回答1:


The context manager doesn't go away just because the block exits. You can preserve it in two ways:

  1. Create the context manager first, assign it to a variable, then use with with that object:

    cm = ContextManager()
    with cm:
        # ....
    
    state = cm.attribute
    
  2. 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.



来源:https://stackoverflow.com/questions/26907175/how-to-manipulate-the-exception-in-exit-of-a-context-manager

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!