python如何在上下文管理器中安全地处理异常

python how to safely handle an exception inside a context manager

我想我已经读过with内的异常不允许__exit__正确调用。 如果我在这个笔记上错了,请原谅我的无知。

所以我在这里有一些伪代码,我的目标是使用一个锁定上下文,在__enter__记录一个开始日期时间并返回一个锁定id,并在__exit__记录结束日期时间并释放锁定:

1
2
3
4
5
6
def main():
    raise Exception

with cron.lock() as lockid:
    print('Got lock: %i' % lockid)
    main()

除了安全地存在上下文之外,我怎么还能引发错误?

注意:我故意在此伪代码中引发基本异常,因为我想在任何异常时安全退出,而不仅仅是预期的异常。

注意:替代/标准并发防止方法是无关紧要的,我想将这些知识应用于任何一般的上下文管理。 我不知道不同的背景是否有不同的怪癖。

PS。 finally块是否相关?


如果上下文管理器被异常中断,则__exit__方法被调用为正常。 实际上,传递给__exit__的参数都与处理这种情况有关! 来自文档:

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.

Note that __exit__() methods should not reraise the passed-in exception; this is the caller’s responsibility.

因此,您可以看到__exit__方法将被执行,然后,默认情况下,退出上下文管理器后将重新引发任何异常。 您可以通过创建一个简单的上下文管理器并使用异常来打破它来自行测试:

1
2
3
4
5
6
7
8
9
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往往会在追踪中间结束):

1
2
3
4
5
6
Entering...
Exiting...
Traceback (most recent call last):
  File"C:\foo.py", line 8, in <module>
    raise Exception()
Exception