我有一段类似这样的代码:
import sys
def func1():
func2()
def func2():
raise Exception('test error')
def main():
err = None
try:
func1()
except:
err = sys.exc_info()[1]
pass
# some extra processing, involving checking err details (if err is not None)
# need to re-raise err so caller can do its own handling
if err:
raise err
if __name__ == '__main__':
main()
当
func2
引发异常时,我收到以下回溯:
Traceback (most recent call last):
File "err_test.py", line 25, in <module>
main()
File "err_test.py", line 22, in main
raise err
Exception: test error
从这里我看不出异常来自哪里。原始回溯丢失了。
如何保留原始回溯并重新引发它?我想看到类似的东西:
Traceback (most recent call last):
File "err_test.py", line 26, in <module>
main()
File "err_test.py", line 13, in main
func1()
File "err_test.py", line 4, in func1
func2()
File "err_test.py", line 7, in func2
raise Exception('test error')
Exception: test error
空白
raise
引发最后一个异常。
# need to re-raise err so caller can do its own handling
if err:
raise
如果您使用
raise something
,Python 无法知道 something
是之前刚刚捕获的异常,还是具有新堆栈跟踪的新异常。这就是为什么有空白 raise
来保留堆栈跟踪。
可以修改并重新抛出异常:
如果不存在表达式,
重新引发最后一个异常 在当前范围内处于活跃状态。如果没有异常处于活动状态 当前范围,会引发raise
异常,表明这是 错误(如果在 IDLE 下运行,则会引发TypeError
异常 相反)。Queue.Empty
否则,
使用以下方法计算表达式以获取三个对象raise
作为省略表达式的值。前两个对象是 用于确定异常的类型和值。None
如果存在第三个对象但不存在
,则它必须是回溯 对象(请参阅标准类型层次结构部分),它是 代替当前位置作为该位置 发生异常。如果第三个对象存在并且不是回溯 对象或None
,会引发None
异常。TypeError
三表达
的形式对于以透明方式重新引发异常很有用raise
子句,但不带表达式的except
应该是首选,如果 要重新引发的异常是最近活跃的异常 在当前范围内。raise
所以如果你想修改异常并重新抛出它,你可以这样做:
try:
buggy_code_which_throws_exception()
except Exception as e:
raise Exception, "The code is buggy: %s" % e, sys.exc_info()[2]
您可以通过
sys.exc_info()
以及 traceback 模块 获取有关异常的大量信息
尝试对您的代码进行以下扩展。
import sys
import traceback
def func1():
func2()
def func2():
raise Exception('test error')
def main():
try:
func1()
except:
exc_type, exc_value, exc_traceback = sys.exc_info()
# Do your verification using exc_value and exc_traceback
print "*** print_exception:"
traceback.print_exception(exc_type, exc_value, exc_traceback,
limit=3, file=sys.stdout)
if __name__ == '__main__':
main()
这将打印出来,与您想要的类似。
*** print_exception:
Traceback (most recent call last):
File "err_test.py", line 14, in main
func1()
File "err_test.py", line 5, in func1
func2()
File "err_test.py", line 8, in func2
raise Exception('test error')
Exception: test error
虽然@Jochen的答案在简单的情况下效果很好,但它无法处理更复杂的情况,在这些情况下,您不能直接捕获和重新抛出,而是由于某种原因将异常作为对象给出并希望重新抛出全新的上下文(即,如果您需要在不同的进程中处理它)。
针对这种情况,我提出以下建议:
在执行此操作之前,定义一个新的异常类型,稍后您将重新抛出该异常类型...
class ChildTaskException(Exception):
pass
在有问题的代码中...
import sys
import traceback
try:
# do something dangerous
except:
error_type, error, tb = sys.exc_info()
error_lines = traceback.format_exception(error_type, error, tb)
error_msg = ''.join(error_lines)
# for example, if you are doing multiprocessing, you might want to send this to another process via a pipe
connection.send(error_msg)
重新抛出...
# again, a multiprocessing example of receiving that message through a pipe
error_msg = pcon.recv()
raise ChildTaskException(error_msg)
您的主要功能需要如下所示:
def main():
try:
func1()
except Exception, err:
# error processing
raise
这是处理(和重新引发)错误的标准方法。 这是键盘演示。
在 Python 3 中:
import sys
class CustomError(Exception):
pass
try:
code_throwing_an_exception()
except Exception as e:
_, value, traceback = sys.exc_info()
raise CustomError("A new Exception was raised: %s" % value).with_traceback(traceback)
正如 Jochen 提到的,Python 无法知道
err
是新异常还是捕获的正在重新引发的异常。 from
子句在这里可以提供帮助。
我有一个 test.py
,其中包含以下代码:
try:
raise RuntimeError("Original error")
except RuntimeError as e:
err = e
raise RuntimeError("Previous operation failed") from err
结果是合理的:
$ python test.py
Traceback (most recent call last):
File "/home/valajeyhani/development/repositories/geotab/image_builder/test.py", line 2, in <module>
raise RuntimeError("Original error")
RuntimeError: Original error
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/home/valajeyhani/development/repositories/geotab/image_builder/test.py", line 6, in <module>
raise RuntimeError("Previous operation failed") from err
RuntimeError: Previous operation failed
请参阅this了解Python2中
from
的支持。
如果异常的链式表示在您的特定应用程序中并不理想,我可以想到两种替代解决方案。
try:
raise RuntimeError("Original error")
except RuntimeError as e:
err = e
raise err from None
结果:
$ python test.py
Traceback (most recent call last):
File "/home/valajeyhani/development/repositories/geotab/image_builder/test.py", line 6, in <module>
raise err from None
File "/home/valajeyhani/development/repositories/geotab/image_builder/test.py", line 2, in <module>
raise RuntimeError("Original error")
RuntimeError: Original error
这不是原始的回溯,但它至少显示了“原始”异常的正确来源,即第 2 行。
最后是第二种不链接异常的方法:
try:
raise RuntimeError("Original error")
except RuntimeError as e:
err = e
raise err.with_traceback(err.__traceback__)
输出:
$ python test.py
Traceback (most recent call last):
File "/home/valajeyhani/development/repositories/geotab/image_builder/test.py", line 6, in <module>
raise err.with_traceback(err.__traceback__)
File "/home/valajeyhani/development/repositories/geotab/image_builder/test.py", line 2, in <module>
raise RuntimeError("Original error")
RuntimeError: Original error
结果与之前的方法非常相似。