如何在上下文管理器中安全地处理异常 [英] How to safely handle an exception inside a context manager
问题描述
我想我已经读到with
内的异常不允许正确调用__exit__
.如果我在此注释上写错了,请原谅我的无知.
I think I've read that exceptions inside a with
do not allow __exit__
to be call correctly. If I am wrong on this note, pardon my ignorance.
所以我这里有一些伪代码,我的目标是使用一个锁上下文,该锁上下文在__enter__
上记录开始日期时间并返回锁id,在__exit__
上记录结束日期时间并释放锁:>
So I have some pseudo code here, my goal is to use a lock context that upon __enter__
logs a start datetime and returns a lock id, and upon __exit__
records an end datetime and releases the lock:
def main():
raise Exception
with cron.lock() as lockid:
print('Got lock: %i' % lockid)
main()
除了安全地存在上下文之外,我还如何引发错误?
How can I still raise errors in addition to existing the context safely?
注意:我故意在此伪代码中引发基本异常,因为我想在发生任何异常(不仅是预期的异常)时安全退出.
注意:替代/标准并发预防方法无关紧要,我想将此知识应用于任何常规上下文管理.我不知道不同的上下文是否有不同的怪癖.
Note: Alternative/standard concurrency prevention methods are irrelevant, I want to apply this knowledge to any general context management. I do not know if different contexts have different quirks.
PS. finally
块是否相关?
PS. Is the finally
block relevant?
推荐答案
如果上下文管理器被异常破坏,则按常规方式调用__exit__
方法 .实际上,传递给__exit__
的参数都与处理这种情况有关!来自文档:
The __exit__
method is called as normal if the context manager is broken by an exception. In fact, the parameters passed to __exit__
all have to do with handling this case! From the docs:
object.__exit__(self, exc_type, exc_value, traceback)
退出与此对象相关的运行时上下文.这些参数描述了导致退出上下文的异常.如果上下文无例外地退出,则所有三个参数都将为无".
Exit the runtime context related to this object. The parameters describe the exception that caused the context to be exited. If the context was exited without an exception, all three arguments will be None.
如果提供了异常,并且该方法希望抑制该异常(即防止其传播),则它应返回一个真值.否则,退出此方法后,异常将被正常处理.
If an exception is supplied, and the method wishes to suppress the exception (i.e., prevent it from being propagated), it should return a true value. Otherwise, the exception will be processed normally upon exit from this method.
请注意,__exit__()
方法不应引发传入的异常.这是呼叫者的责任.
Note that __exit__()
methods should not reraise the passed-in exception; this is the caller’s responsibility.
因此,您可以看到将执行__exit__
方法,然后默认情况下,退出上下文管理器后, 将重新引发任何异常.您可以通过创建一个简单的上下文管理器并使用异常将其断开来进行测试:
So you can see that the __exit__
method will be executed and then, by default, any exception will be re-raised after exiting the context manager. You can test this yourself by creating a simple context manager and breaking it with an exception:
DummyContextManager(object):
def __enter__(self):
print('Entering...')
def __exit__(self, exc_type, exc_value, traceback):
print('Exiting...')
# If we returned True here, any exception would be suppressed!
with DummyContextManager() as foo:
raise Exception()
运行此代码时,您应该看到所需的所有内容(由于print
往往会在回溯中间结束,因此可能会出现故障):
When you run this code, you should see everything you want (might be out of order since print
tends to end up in the middle of tracebacks):
Entering...
Exiting...
Traceback (most recent call last):
File "C:\foo.py", line 8, in <module>
raise Exception()
Exception
这篇关于如何在上下文管理器中安全地处理异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!