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

时间:2011-01-28 05:42:45

标签: python exception exception-handling 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()

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

从这里,我不知道异常的来源。原始追溯丢失了。

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

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

5 个答案:

答案 0 :(得分:106)

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

# need to re-raise err so caller can do its own handling
if err:
    raise

如果您使用raise something,Python无法知道something是否是之前捕获的异常,或者是否有新堆栈跟踪的新异常。这就是保留堆栈跟踪的空白raise的原因。

Reference here

答案 1 :(得分:60)

可能modify and rethrow例外:

  

如果没有表达式,raise会重新引发最后一个异常   在当前范围内活跃。如果没有活动的例外   当前范围,引发TypeError异常,表明这是异常   错误(如果在IDLE下运行,则会引发Queue.Empty异常   代替)。

     

否则,raise使用表达式来计算表达式以获得三个对象   None作为省略表达式的值。前两个对象是   用于确定异常的类型和值。

     

如果存在第三个对象而不是None,则它必须是回溯   object(参见标准类型层次结构),它是   替换当前位置而不是当前位置   异常发生。如果第三个对象存在而不是回溯   对象或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]

答案 2 :(得分:5)

您可以通过sys.exc_info()以及traceback模块获取有关异常的大量信息

尝试对您的代码进行以下扩展。

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

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

*** 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

答案 3 :(得分:4)

虽然@Jochen的答案在简单的情况下效果很好,但它不能处理更复杂的情况,你不是直接捕捉和重新抛出,但出于某种原因,将异常作为一个对象和愿望重新投入一个全新的环境(即如果你需要在不同的过程中处理它)。

在这种情况下,我建议如下:

  1. 获取原始的exc_info
  2. 使用堆栈跟踪格式化原始错误消息
  3. 使用嵌入式
  4. 的完整错误消息(堆栈跟踪包含)抛出新异常

    在此之前,请定义一个新的异常类型,稍后您将重新抛出...

    class ChildTaskException(Exception):
        pass
    

    违规代码......

    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)
    

    ...重新抛出

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

答案 4 :(得分:2)

您的主要功能需要如下所示:

def main():
    try:
        func1()
    except Exception, err:
        # error processing
        raise

这是处理(和重新提出)错误的标准方法。 Here is a codepad demonstration.

相关问题