如果不立即重新引发异常回溯,则会隐藏异常回溯

par*_*ier 65 python exception-handling exception try-catch traceback

我有一段类似于此的代码:

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()
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

从这里我看不出异常的来源.原始追溯丢失了.

如何保留原始追溯并重新提升?我想看到类似的东西:

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
Run Code Online (Sandbox Code Playgroud)

Joc*_*zel 110

空白raise会引发最后一个异常.

# need to re-raise err so caller can do its own handling
if err:
    raise
Run Code Online (Sandbox Code Playgroud)

如果您使用raise somethingPython无法知道是否something是之前捕获的异常,或者是新堆栈跟踪的新异常.这就是为什么有空白raise来保留堆栈跟踪.

参考这里

  • 在yprez的评论中,"这个"意味着"在离开除了块之后空出加薪".裸"提升"在Python 3中有效(但只在except块内部.) (15认同)
  • 值得一提的是,这在Python 3中不起作用. (3认同)

qri*_*ris 66

可以修改和重新抛出异常:

如果不存在表达式,则raise重新引发当前作用域中处于活动状态的最后一个异常.如果当前作用域中没有活动TypeError异常,则会引发异常,指示这是一个错误(如果在IDLE下运行,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]
Run Code Online (Sandbox Code Playgroud)

  • @elias [six.reraise(exc_type, exc_value, exc_traceback=None)](https://pythonhosted.org/six/#six.reraise) (5认同)
  • 我用它来用不同的消息重新抛出相同的异常,包括有关导致异常的条件的更多细节,这些条件在外部作用域中可用,但在内部作用域中不可用. (4认同)

Sen*_*ran 6

您可以通过得到很多关于异常的信息sys.exc_info()连同追踪模块

尝试以下扩展您的代码.

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()
Run Code Online (Sandbox Code Playgroud)

这将打印,类似于您想要的.

*** 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
Run Code Online (Sandbox Code Playgroud)

  • 不,我不想在`main()`中打印它。我想用原始的回溯重新引发它,并让“main()”的调用者来处理它(例如忽略、打印到控制台、保存到数据库等)。乔亨的解决方案奏效了。 (2认同)
  • 如果将“print”更改为“raise exc_type.with_traceback(exc_value, exc_traceback)”之类的内容,这将是 Python3 的最佳答案 (2认同)

tvt*_*173 5

虽然@Jochen的答案在简单的情况下效果很好,但它无法处理更复杂的情况,在这种情况下,您不能直接捕获并重新抛出,但是由于某种原因被赋予例外,因此希望将其完全抛出新的上下文(即,如果您需要在其他过程中处理它)。

在这种情况下,我提出以下建议:

  1. 得到原始的exc_info
  2. 使用堆栈跟踪格式化原始错误消息
  3. 引发一个新的异常,内嵌完整的错误消息(包括堆栈跟踪信息)

在执行此操作之前,请定义新的异常类型,稍后将其重新抛出...

class ChildTaskException(Exception):
    pass
Run Code Online (Sandbox Code Playgroud)

在令人反感的代码中...

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)
Run Code Online (Sandbox Code Playgroud)

重新投掷...

# again, a multiprocessing example of receiving that message through a pipe
error_msg = pcon.recv()
raise ChildTaskException(error_msg)
Run Code Online (Sandbox Code Playgroud)