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

Re-raise Python exception and preserve stack trace

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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]

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

1
2
3
4
(<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]

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

1
2
Traceback (most recent call last):
  File"test.py", line 7, in <module>


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

1
raise failingThread.exc_info[0], failingThread.exc_info[1], failingThread.exc_info[2]

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

来自help('raise')

If a third object is present and not None, it must be a traceback
object (see section The standard type hierarchy), and it is
substituted instead of the current location as the place where the
exception occurred. If the third object is present and not a
traceback object or None, a TypeError exception is raised. The
three-expression form of raise is useful to re-raise an exception
transparently in an except clause, but raise with no expressions
should be preferred if the exception to be re-raised was the most
recently active exception in the current scope.

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

对于Python 3(根据评论):

1
raise failingThread.exc_info[1].with_traceback(failingThread.exc_info[2])

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


这段代码片段适用于python 2&amp;3:

1
2
3
4
5
      1 try:
----> 2     raise KeyError('Default key error message')
      3 except KeyError as e:
      4     e.args = ('Custom message when get re-raised',) #The comma is not a typo, it's there to indicate that we're replacing the tuple that e.args pointing to with another tuple that contain the custom message.
      5     raise


你能写得这样吗:

1
2
3
4
try:
    raise ValueError('x')
except ValueError as ex:
    self.exc_info = ex

然后使用异常中的stacktrace?