从更高级别提出异常,la警告

Joe*_*oel 9 python python-3.x python-3.5

在模块警告(https://docs.python.org/3.5/library/warnings.html)中,可以发出似乎来自堆栈中某个位置的警告:

warnings.warn('This is a test', stacklevel=2)
Run Code Online (Sandbox Code Playgroud)

是否存在引发错误的等价物?我知道我可以使用备用回溯引发错误,但我无法在模块中创建该回溯,因为它需要来自之前的版本.我想象的是:

tb = magic_create_traceback_right_here()
raise ValueError('This is a test').with_traceback(tb.tb_next)
Run Code Online (Sandbox Code Playgroud)

原因是我正在开发一个具有一个函数的模块,module.check_raise我想引发一个看起来源于函数调用位置的错误.如果我在module.check_raise函数内引发错误,它似乎来自内部module.check_raise,这是不希望的.

此外,我尝试了一些技巧,比如提出一个虚拟异常,捕获它,然后传递回溯,但不知怎的,tb_next变成了None.我没有想法.

编辑:

我想要这个最小例子的输出(称为tb2.py):

import check_raise

check_raise.raise_if_string_is_true('True')
Run Code Online (Sandbox Code Playgroud)

只有这个:

Traceback (most recent call last):
  File "tb2.py", line 10, in <module>
    check_raise.raise_if_string_is_true(string)
RuntimeError: An exception was raised.
Run Code Online (Sandbox Code Playgroud)

Tad*_*sen 0

编辑:以前的版本没有提供引用或解释。

我建议参考PEP 3134,其中在动机中指出:

有时,异常处理程序有意重新引发异常可能很有用,可以提供额外的信息,也可以将异常转换为另一种类型。该__cause__属性提供了一种显式的方式来记录异常的直接原因。

Exception使用属性引发时__cause__,回溯消息采用以下形式:

Traceback (most recent call last):
 <CAUSE TRACEBACK>

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  <MAIN TRACEBACK>
Run Code Online (Sandbox Code Playgroud)

据我了解,这正是您想要实现的目标;清楚地表明错误的原因不是您的模块而是其他地方。如果您尝试像编辑建议的那样省略回溯信息,那么这个答案的其余部分不会对您有任何好处。


只是语法上的注释:

异常对象的属性__cause__始终初始化为 None。它由新形式的“raise”语句设置:

   raise EXCEPTION from CAUSE
Run Code Online (Sandbox Code Playgroud)

这相当于:

    exc = EXCEPTION
    exc.__cause__ = CAUSE
    raise exc
Run Code Online (Sandbox Code Playgroud)

所以最简单的例子是这样的:

def function():
    int("fail")

def check_raise(function):
    try:
        function()
    except Exception as original_error:
        err = RuntimeError("An exception was raised.")
        raise err from original_error

check_raise(function)
Run Code Online (Sandbox Code Playgroud)

它给出了这样的错误消息:

Traceback (most recent call last):
  File "/PATH/test.py", line 7, in check_raise
    function()
  File "/PATH/test.py", line 3, in function
    int("fail")
ValueError: invalid literal for int() with base 10: 'fail'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/PATH/test.py", line 12, in <module>
    check_raise(function)
  File "/PATH/test.py", line 10, in check_raise
    raise err from original_error
RuntimeError: An exception was raised.
Run Code Online (Sandbox Code Playgroud)

try然而,原因的第一行是块中的语句check_raise

  File "/PATH/test.py", line 7, in check_raise
    function()
Run Code Online (Sandbox Code Playgroud)

因此,在提出之前,err可能(或可能不)需要从以下位置删除最外层的回溯框架original_error

except Exception as original_error:
    err = RuntimeError("An exception was raised.")
    original_error.__traceback__ = original_error.__traceback__.tb_next
    raise err from original_error
Run Code Online (Sandbox Code Playgroud)

这样,回溯中唯一出现的行check_raise是最后一条raise语句,纯 python 代码不能省略该语句,尽管根据消息的信息量,您可以非常清楚地表明您的模块不是问题的原因:

err = RuntimeError("""{0.__qualname__} encountered an error during call to {1.__module__}.{1.__name__}
the traceback for the error is shown above.""".format(function,check_raise))
Run Code Online (Sandbox Code Playgroud)

像这样引发异常的优点是,当引发新错误时,原始 Traceback 消息不会丢失,这意味着可以引发一系列非常复杂的异常,并且 python 仍将正确显示所有相关信息:

def check_raise(function):
    try:
        function()
    except Exception as original_error:
        err = RuntimeError("""{0.__qualname__} encountered an error during call to {1.__module__}.{1.__name__}
the traceback for the error is shown above.""".format(function,check_raise))
        original_error.__traceback__ = original_error.__traceback__.tb_next
        raise err from original_error

def test_chain():
    check_raise(test)

def test():
    raise ValueError

check_raise(test_chain)
Run Code Online (Sandbox Code Playgroud)

给我以下错误消息:

Traceback (most recent call last):
  File "/Users/Tadhg/Documents/test.py", line 16, in test
    raise ValueError
ValueError

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/Users/Tadhg/Documents/test.py", line 13, in test_chain
    check_raise(test)
  File "/Users/Tadhg/Documents/test.py", line 10, in check_raise
    raise err from original_error
RuntimeError: test encountered an error during call to __main__.check_raise
the traceback for the error is shown above.

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/Users/Tadhg/Documents/test.py", line 18, in <module>
    check_raise(test_chain)
  File "/Users/Tadhg/Documents/test.py", line 10, in check_raise
    raise err from original_error
RuntimeError: test_chain encountered an error during call to __main__.check_raise
the traceback for the error is shown above.
Run Code Online (Sandbox Code Playgroud)

是的,它很长,但它的信息量要大得多

Traceback (most recent call last):
  File "/Users/Tadhg/Documents/test.py", line 18, in <module>
    check_raise(test_chain)
RuntimeError: An exception was raised.
Run Code Online (Sandbox Code Playgroud)

更不用说即使程序没有结束,原始错误仍然可用:

import traceback

def check_raise(function):
    ...

def fail():
    raise ValueError

try:
    check_raise(fail)
except RuntimeError as e:
    cause = e.__cause__
    print("check_raise failed because of this error:")
    traceback.print_exception(type(cause), cause, cause.__traceback__)

print("and the program continues...")
Run Code Online (Sandbox Code Playgroud)