在模块警告(https://docs.python.org/3.5/library/warnings.html)中,有能力发出似乎来自堆栈早期某处的警告:
warnings.warn('This is a test', stacklevel=2)
是否有相应的提出错误?我知道我可以使用备用回溯引发错误,但我无法在模块中创建该回溯,因为它需要来自之前的版本。我想象的是:
tb = magic_create_traceback_right_here()
raise ValueError('This is a test').with_traceback(tb.tb_next)
原因是我正在开发一个具有函数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')
只有这个:
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.
答案 0 :(得分:2)
如果我理解正确,你会想要这个最小例子的输出:
def check_raise(function):
try:
return function()
except Exception:
raise RuntimeError('An exception was raised.')
def function():
1/0
check_raise(function)
只有这个:
Traceback (most recent call last):
File "tb2.py", line 10, in <module>
check_raise(function)
RuntimeError: An exception was raised.
事实上,它的输出要多得多;存在异常链接,可以通过立即处理RuntimeError
,删除其__context__
并重新提升它来处理,并且RuntimeError
本身还有另一行追溯:
File "tb2.py", line 5, in check_raise
raise RuntimeError('An exception was raised.')
据我所知,纯Python代码不可能在引发异常后替换异常的回溯;解释器可以控制添加它,但只有在处理异常时它才会公开当前的回溯。没有API(甚至在使用跟踪函数时也没有)将自己的回溯传递给解释器,并且回溯对象是不可变的(这是由涉及C级别的Jinja hack所解决的)。
因此,进一步假设您对缩短的追溯感兴趣不是为了进一步的程序化使用,而是仅针对用户友好的输出,您最好的选择是excepthook
来控制如何将回溯打印到安慰。为了确定停止打印的位置,可以使用一个特殊的局部变量(这比将回溯限制为长度减去1或者这样更加强大)。此示例需要Python 3.5(适用于traceback.walk_tb
):
import sys
import traceback
def check_raise(function):
__exclude_from_traceback_from_here__ = True
try:
return function()
except Exception:
raise RuntimeError('An exception was raised.')
def print_traceback(exc_type, exc_value, tb):
for i, (frame, lineno) in enumerate(traceback.walk_tb(tb)):
if '__exclude_from_traceback_from_here__' in frame.f_code.co_varnames:
limit = i
break
else:
limit = None
traceback.print_exception(
exc_type, exc_value, tb, limit=limit, chain=False)
sys.excepthook = print_traceback
def function():
1/0
check_raise(function)
这是现在的输出:
Traceback (most recent call last):
File "tb2.py", line 26, in <module>
check_raise(function)
RuntimeError: An exception was raised.
答案 1 :(得分:2)
我不敢相信我发布了这个
通过这样做,您将反对the zen 。
特殊情况不足以打破规则。
但如果你坚持这里是你的神奇代码。
import sys
import traceback
def raise_if_string_is_true(string):
if string == 'true':
#the frame that called this one
f = sys._getframe().f_back
#the most USELESS error message ever
e = RuntimeError("An exception was raised.")
#the first line of an error message
print('Traceback (most recent call last):',file=sys.stderr)
#the stack information, from f and above
traceback.print_stack(f)
#the last line of the error
print(*traceback.format_exception_only(type(e),e),
file=sys.stderr, sep="",end="")
#exit the program
#if something catches this you will cause so much confusion
raise SystemExit(1)
# SystemExit is the only exception that doesn't trigger an error message by default.
这是纯python,不会干扰sys.excepthook
,即使在except Exception:
中,它也不会被except:
捕获1>
import check_raise
check_raise.raise_if_string_is_true("true")
print("this should never be printed")
将为您提供您想要的(可怕的无信息和极其伪造)追溯信息。
Tadhgs-MacBook-Pro:Documents Tadhg$ python3 test.py
Traceback (most recent call last):
File "test.py", line 3, in <module>
check_raise.raise_if_string_is_true("true")
RuntimeError: An exception was raised.
Tadhgs-MacBook-Pro:Documents Tadhg$
答案 2 :(得分: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>
据我所知,这正是你想要完成的事情;清楚地表明错误的原因不是你的模块,而是其他地方。如果你试图像你的编辑那样试图忽略追溯信息,那么这个答案的其余部分对你没有任何帮助。
关于语法的说明:
始终初始化异常对象上的
__cause__
属性 没有。它由'raise'语句的新形式设置:raise EXCEPTION from CAUSE
相当于:
exc = EXCEPTION exc.__cause__ = CAUSE raise exc
所以最基本的例子就是这样:
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)
,它会显示如下错误消息:
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.
然而,原因的第一行是try
check_raise
区块中的陈述:
File "/PATH/test.py", line 7, in check_raise
function()
所以在提出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
这样,回溯中看似来自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))
像这样引发异常的好处是,当引发新错误时,原始的Traceback消息不会丢失,这意味着可以引发一系列非常复杂的异常,并且python仍将正确显示所有相关信息:< / p>
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)
给我以下错误消息:
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.
是的,它很长但是显着更具信息性:
Traceback (most recent call last):
File "/Users/Tadhg/Documents/test.py", line 18, in <module>
check_raise(test_chain)
RuntimeError: An exception was raised.
更不用说即使程序没有结束,原始错误仍然可用:
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...")