重新引发Python异常并保留堆栈跟踪

ros*_*ori 53 python exception stack-trace

我试图在一个线程中捕获异常并在主线程中重新引发它:

import threading
import sys

class FailingThread(threading.Thread):
    def run(self):
        try:
            raise ValueError('x')
        except ValueError:
            self.exc_info = sys.exc_info()

failingThread = FailingThread()
failingThread.start()
failingThread.join()

print failingThread.exc_info
raise failingThread.exc_info[1]
Run Code Online (Sandbox Code Playgroud)

这基本上起作用并产生以下输出:

(<type 'exceptions.ValueError'>, ValueError('x',), <traceback object at 0x1004cc320>)
Traceback (most recent call last):
  File "test.py", line 16, in <module>
    raise failingThread.exc_info[1]
Run Code Online (Sandbox Code Playgroud)

但是,异常的来源指向第16行,其中发生了重新加注.原始异常来自第7行.如何修改线程以使输出显示:

Traceback (most recent call last):
  File "test.py", line 7, in <module>
Run Code Online (Sandbox Code Playgroud)

Dun*_*can 52

在Python 2中,您需要使用所有三个参数来引发:

raise failingThread.exc_info[0], failingThread.exc_info[1], failingThread.exc_info[2]
Run Code Online (Sandbox Code Playgroud)

传递traceback对象作为第三个参数保留堆栈.

来自help('raise'):

如果存在第三个对象而不存在None,则它必须是一个回溯对象(请参阅标准类型层次结构一节),并将其替换为当前位置作为发生异常的位置.如果第三个对象存在而不是回溯对象None,TypeError则引发异常.三表达式形式raise对于在except子句中透明地重新引发异常很有用,但raise如果要重新引发的异常是当前作用域中最近活动的异常,则不应该首选表达式.

在这种特殊情况下,您不能使用no表达式版本.

对于Python 3(根据评论):

raise failingThread.exc_info[1].with_traceback(failingThread.exc_info[2])
Run Code Online (Sandbox Code Playgroud)

或者你可以简单地使用例外raise ... from ...链接异常,但是会在原因属性中附加原始上下文引发链接异常,这可能是也可能不是你想要的.

  • @AndyHayden确实,在Python 3 [raise](http://docs.python.org/3/reference/simple_stmts.html#the-raise-statement)中必须以某种方式调用`raise failingThread.exc_info [0] (failingThread.exc_info [1]).with_traceback(failingThread.exc_info [2])`.虽然在Python 3中,`raise AnotherError from stored_exception`可能会提供更好的输出 (5认同)
  • @CecilCurry 很好的建议,我更新了我的答案。当然,尽管您有足够的权限,您也可以直接编辑它吗? (2认同)