简体   繁体   English

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

[英]Re-raise Python exception and preserve stack trace

I'm trying to catch an exception in a thread and re-raise it in the main thread: 我试图在一个线程中捕获异常并在主线程中重新引发它:

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]

This basically works and yields the following output: 这基本上起作用并产生以下输出:

(<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]

However, the source of the exception points to line 16, where the re-raise occurred. 但是,异常的来源指向第16行,其中发生了重新加注。 The original exception comes from line 7. How do I have to modify the main thread so that the output reads: 原始异常来自第7行。如何修改线程以使输出显示:

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

In Python 2 you need to use all three arguments to raise: 在Python 2中,您需要使用所有三个参数来引发:

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

passing the traceback object in as the third argument preserves the stack. 传递traceback对象作为第三个参数保留堆栈。

From help('raise') : 来自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. 如果存在第三个对象而不是None ,则它必须是一个回溯对象(请参阅标准类型层次结构一节),并将其替换为当前位置作为发生异常的位置。 If the third object is present and not a traceback object or None , a TypeError exception is raised. 如果存在第三个对象而不是回溯对象或None ,则引发TypeError异常。 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. raise的三表达式形式对于在except子句中透明地重新引发异常很有用,但如果要重新引发的异常是当前作用域中最近活动的异常,则应该首选raise而不使用表达式。

In this particular case you cannot use the no expression version. 在这种特殊情况下,您不能使用no表达式版本。

For Python 3 (as per the comments): 对于Python 3(根据评论):

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

or you can simply chain the exceptions using raise ... from ... but that raises a chained exception with the original context attached in the cause attribute and that may or may not be what you want. 或者您可以使用raise ... from ...简单地链接异常,但是会在原因属性中附加原始上下文引发链接异常,这可能是也可能不是您想要的。

This code snippet works in both python 2 & 3: 这段代码在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

Could you write it somewhat like this: 你能写得这样吗:

try:
    raise ValueError('x')
except ValueError as ex:
    self.exc_info = ex

and then use the stacktrace from the exception? 然后使用异常中的stacktrace?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM