【问题标题】:Re-raise Python exception and preserve stack trace重新引发 Python 异常并保留堆栈跟踪
【发布时间】:2012-01-06 15:34:30
【问题描述】:

我正在尝试在线程中捕获异常并在主线程中重新引发它:

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]

这基本上有效并产生以下输出:

(<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 行。我如何修改 main 线程以便输出显示为:

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

【问题讨论】:

标签: python exception stack-trace


【解决方案1】:

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

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

将回溯对象作为第三个参数传入会保留堆栈。

来自help('raise')

如果存在第三个对象而不是None,则它必须是回溯 对象(参见标准类型层次结构部分),它是 代替当前位置作为 发生异常。如果第三个对象存在而不是 回溯对象或None,会引发TypeError 异常。这 raise 的三表达式形式可用于重新引发异常 在 except 子句中透明,但 raise 没有表达式 如果要重新提出的异常是最 当前范围内最近活跃的异常。

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

对于 Python 3(根据 cmets):

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

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

【讨论】:

  • @AndyHayden 实际上,在 Python 3 中,raise 必须以某种方式被称为 raise failingThread.exc_info[0](failingThread.exc_info[1]).with_traceback(failingThread.exc_info[2])。尽管在 Python 3 中 raise AnotherError from stored_exception 可能会提供更好的输出
  • 更正,第一个应该是raise failingThread.exc_info[1].with_traceback(failingThread.exc_info[2]) 按照this comment
  • @TobiasKienzler 您介意将您高度支持的 cmets 推断为新答案将它们编辑为当前答案吗?在后 Python 2 时代,仅 Python 2 的解决方案已经过时了。
  • @CecilCurry 好建议,我更新了我的答案。当然,尽管您有足够的权限直接编辑它?
【解决方案2】:

此代码 sn-p 在 python 2 和 3 中都有效:

      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

【讨论】:

  • 我可以猜到为什么会投反对票。此解决方案仅在没有任何异常处理时才有效。想象一下,在捕获异常时,您尝试更新数据库中的状态,但也失败了。在这种情况下,将引发的异常是最后一个(数据库事务失败),而不是我们最初捕获的那个。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-07-12
  • 1970-01-01
  • 2012-02-18
  • 2018-06-19
  • 2013-01-17
  • 1970-01-01
  • 2012-02-18
相关资源
最近更新 更多