如何在`try`外获得带有`__traceback__`属性的异常,包含调用栈
在Python(3.10)中,如果在一个try
块里发生了异常,这个异常的追踪信息(traceback)并不会显示到调用这个try
块的地方。这让我有点意外,更重要的是,这并不是我想要的结果。
下面是一个简单的程序,展示了这个问题:
# short_tb.py
import traceback
def caller():
somefunction()
def somefunction():
try:
raise ValueError("This is a value error")
except ValueError as e:
# in my actual code, e is passed to a function, and __traceback__ is pulled from it for logging at this other
# location; the code below simply demonstrates the lack of frames in the traceback object
print("".join(traceback.format_tb(e.__traceback__)))
print("This will produce a traceback with only one frame, the one in somefunction()")
caller()
def caller2():
somefunction2()
def somefunction2():
raise ValueError("This is a value error")
print("This will produce a traceback with all relevant frames (the default behavior of the python interpreter)")
caller2()
输出结果:
This will produce a traceback with only one frame, the one in somefunction()
File ".../short_tb.py", line 10, in somefunction
raise ValueError("This is a value error")
This will produce a traceback with all relevant frames (the default behavior of the python interpreter)
Traceback (most recent call last):
File ".../short_tb.py", line 30, in <module>
caller2()
File ".../short_tb.py", line 22, in caller2
somefunction2()
File ".../short_tb.py", line 26, in somefunction2
raise ValueError("This is a value error")
ValueError: This is a value error
我希望__traceback__
能包含第二个例子中的所有信息。我愿意覆盖这个变量,并且我可以在处理异常的地方这样做……但我该如何获取一个对象来实现这个目的呢?
在这个问题中,有很多关于追踪信息的回答,但似乎没有一个是关于traceback objects
的:捕获并打印完整的Python异常追踪信息,而不停止/退出程序
2 个回答
1
正如@chepner在评论中提到的,“追踪记录是异常在被抛出和被捕获之间经过的所有步骤的记录,而不是异常被抛出时堆栈的状态。”不过,traceback
模块也可以在代码的当前点生成堆栈帧,所以下面的代码会在一个辅助函数中打印出整个堆栈帧:
import traceback
import sys
def print_full_exception_stack():
etype, e, tb = sys.exc_info()
s = traceback.extract_stack()[:2] # Frames up to where this helper function was called.
t = traceback.extract_tb(tb) # Frames to the exception.
L = traceback.format_list(s + t) # Join and format the frames.
print('Traceback (most recent call last):')
print(''.join(L).rstrip())
print(f'{etype.__name__}: {e}')
def caller():
somefunction()
def somefunction():
try:
raise ValueError('This is a value error')
except ValueError:
print_full_exception_stack()
caller()
输出:
Traceback (most recent call last):
File "C:\Users\xxx\test.py", line 17, in <module>
caller()
File "C:\Users\xxx\test.py", line 4, in caller
somefunction()
File "C:\Users\xxx\test.py", line 8, in somefunction
raise ValueError('This is a value error')
ValueError: This is a value error
1
更正确的处理方法已经由 @Mark Tolonen 提出,但如果真的需要 __traceback__
包含完整的调用栈:
- 可以使用
tb_frame.f_back
来访问之前的调用帧。 - 使用
types.TracebackType
来重建 traceback 对象。 - 通过
tb_next
这个构造参数将它们连接起来。
import traceback
import types
def caller(full = False):
somefunction(full)
def somefunction(full):
try:
raise ValueError("This is a value error")
except ValueError as e:
if full:
complete_traceback(e)
print("".join(traceback.format_tb(e.__traceback__)))
def complete_traceback(exception):
t = exception.__traceback__
fb = t.tb_frame.f_back
while fb:
t = types.TracebackType(tb_next=t,
tb_frame=fb,
tb_lasti=fb.f_lasti,
tb_lineno=fb.f_lineno)
fb = t.tb_frame.f_back
exception.__traceback__ = t
print("This will produce a traceback with only one frame, the one in somefunction()")
caller()
print("This will produce a traceback with all relevant frames")
caller(True)
输出:
This will produce a traceback with only one frame, the one in somefunction()
File ".../short_tb.py", line 10, in somefunction
raise ValueError("This is a value error")
This will produce a traceback with all relevant frames
File ".../short_tb.py", line 32, in <module>
caller(True)
File ".../short_tb.py", line 6, in caller
somefunction(full)
File ".../short_tb.py", line 10, in somefunction
raise ValueError("This is a value error")