【发布时间】:2018-10-18 10:11:16
【问题描述】:
我希望上下文管理器捕获异常,打印堆栈跟踪,然后允许继续执行。
我想知道我是否可以使用 contextlib contextmanager 装饰器来做到这一点。如果没有,我该怎么办?
文档建议如下:
在生成器产生的地方,嵌套在 with 语句中的块被执行。然后在退出块后恢复生成器。如果块中发生未处理的异常,它会在生成器中在产生产生的点重新引发。因此,您可以使用 try...except...finally 语句来捕获错误(如果有),或确保进行一些清理。如果捕获异常只是为了记录它或执行某些操作(而不是完全抑制它),则生成器必须重新引发该异常。
所以我尝试了文档引导我采用的明显方法:
import contextlib
import logging
@contextlib.contextmanager
def log_error():
try:
yield
except Exception as e:
logging.exception('hit exception')
finally:
print 'done with contextmanager'
def something_inside_django_app():
with log_error():
raise Exception('alan!')
something_inside_django_app()
print 'next block of code'
这会产生输出
ERROR:root:hit exception
Traceback (most recent call last):
File "exception_test.py", line 8, in log_error
yield
File "exception_test.py", line 17, in something_inside_django_app
raise Exception('alan!')
Exception: alan!
done with contextmanager
next block of code
这会丢失有关异常从何处引发的关键信息。考虑将上下文管理器调整为不抑制异常时得到的结果:
Traceback (most recent call last):
File "exception_test.py", line 20, in <module>
something_inside_django_app()
File "exception_test.py", line 17, in something_inside_django_app
raise Exception('alan!')
Exception: alan!
是的,它能够告诉我异常是从第 17 行引发的,非常感谢,但是第 20 行的先前调用丢失了信息。如何让上下文管理器给我 actual full 调用堆栈,而不是它的截断版本?回顾一下,我想满足两个要求:
- 有一个 python 上下文管理器抑制它包装的代码中引发的异常
- 如果我没有使用上下文管理器,打印该代码将生成的堆栈跟踪
如果使用装饰器无法做到这一点,那么我将使用其他样式的上下文管理器来代替。如果这不能通过上下文管理器完成,我想知道一个好的 Python 替代方案是什么。
【问题讨论】:
标签: python exception-handling contextmanager