异常回溯在未立即重新抛出时被隐藏

80 投票
7 回答
42263 浏览
提问于 2025-04-16 10:46

我有一段代码,差不多是这样的:

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

7 个回答

8

你可以通过 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
72

可以对异常进行修改并重新抛出:

如果没有提供任何表达式,raise 会重新抛出当前作用域内最后一个活跃的异常。如果当前作用域没有活跃的异常,就会抛出一个 TypeError 异常,表示这是一个错误(如果在 IDLE 环境下运行,则会抛出 Queue.Empty 异常)。

如果有其他表达式,raise 会计算这些表达式,得到三个对象,省略的表达式会用 None 作为值。前两个对象用来确定异常的类型和具体值。

如果第三个对象存在且不为 None,它必须是一个追踪对象(可以参考标准类型层次结构),并且会替代当前的位置,作为异常发生的地方。如果第三个对象存在但不是追踪对象或 None,则会抛出一个 TypeError 异常。

使用三表达式形式的 raiseexcept 语句中透明地重新抛出异常是很有用的,但如果要重新抛出的异常是当前作用域内最近活跃的异常,建议使用不带表达式的 raise

所以,如果你想修改异常并重新抛出,可以这样做:

try:
    buggy_code_which_throws_exception()
except Exception as e:
    raise Exception, "The code is buggy: %s" % e, sys.exc_info()[2]
129

一个空的 raise 会重新抛出最后一个异常。

# need to re-raise err so caller can do its own handling
if err:
    raise

如果你使用 raise something,Python 无法判断 something 是之前捕获到的异常,还是一个新的异常并且有新的调用记录。这就是为什么会有空的 raise,它可以保留调用记录。

参考链接在这里

撰写回答