68

我有一段类似的代码:

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
4

5 回答 5

116

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

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

如果您使用raise somethingPython,则无法知道something是刚刚捕获的异常,还是带有新堆栈跟踪的新异常。这就是为什么有raise保留堆栈跟踪的空白。

参考这里

于 2011-01-28T05:51:40.420 回答
68

可以修改和重新抛出异常:

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

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

如果存在且不存在第三个对象None,则它必须是回溯对象(请参阅标准类型层次结构部分),并且将其替换为发生异常的位置,而不是当前位置。如果存在第三个对象而不是回溯对象或NoneTypeError则会引发异常。

的三表达式形式对于在子句中raise透明地重新引发异常很有用 ,但如果要重新引发的异常是当前范围内最近活动的异常,则不应首选不使用表达式。exceptraise

所以如果你想修改异常并重新抛出它,你可以这样做:

try:
    buggy_code_which_throws_exception()
except Exception as e:
    raise Exception, "The code is buggy: %s" % e, sys.exc_info()[2]
于 2013-01-04T18:03:42.477 回答
7

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
于 2011-01-28T05:48:49.213 回答
5

虽然@Jochen 的答案在简单的情况下效果很好,但它无法处理更复杂的情况,在这种情况下您不能直接捕获和重新抛出,而是由于某种原因将异常作为对象并希望完全重新抛出新的上下文(即,如果您需要在不同的过程中处理它)。

在这种情况下,我提出以下建议:

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

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

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)
于 2017-04-11T00:42:48.517 回答
2

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

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

这是处理(和重新引发)错误的标准方法。这是一个键盘演示。

于 2011-01-28T05:50:44.787 回答