我正在研究第三方开发人员使用的Python库,为我们的核心应用程序编写扩展.
我想知道是否可以在引发异常时修改回溯,因此最后一个堆栈框架是在开发人员代码中调用库函数,而不是引发异常的库中的行.堆栈底部还有一些框架,其中包含对第一次加载我理想情况下要移除的代码时使用的函数的引用.
提前感谢任何建议!
use*_*826 12
您可以通过使用traceback的tb_next元素进行提升来轻松删除回溯的顶部:
except:
ei = sys.exc_info()
raise ei[0], ei[1], ei[2].tb_next
Run Code Online (Sandbox Code Playgroud)
tb_next是一个read_only属性,所以我不知道从底部删除东西的方法.您可能可以使用属性机制来允许访问属性,但我不知道如何执行此操作.
ber*_*ers 11
从Python 3.7开始,您可以实例化一个新traceback
对象并.with_traceback()
在抛出时使用该方法。sys._getframe(1)
下面是一些使用任一(或更强大的替代方案)的演示代码,这些代码会引发一段AssertionError
时间,使调试器相信错误发生在myassert(False)
:sys._getframe(1)
省略顶部堆栈帧。
我应该补充的是,虽然这在调试器中看起来很好,但控制台行为揭示了它真正在做什么:
Traceback (most recent call last):
File ".\test.py", line 35, in <module>
myassert_false()
File ".\test.py", line 31, in myassert_false
myassert(False)
File ".\test.py", line 26, in myassert
raise AssertionError().with_traceback(back_tb)
File ".\test.py", line 31, in myassert_false
myassert(False)
AssertionError
Run Code Online (Sandbox Code Playgroud)
我没有删除堆栈顶部,而是添加了倒数第二帧的副本。
不管怎样,我关注的是调试器的行为方式,看起来这个调试器工作正常:
"""Modify traceback on exception.
See also https://github.com/python/cpython/commit/e46a8a
"""
import sys
import types
def myassert(condition):
"""Throw AssertionError with modified traceback if condition is False."""
if condition:
return
# This function ... is not guaranteed to exist in all implementations of Python.
# https://docs.python.org/3/library/sys.html#sys._getframe
# back_frame = sys._getframe(1)
try:
raise AssertionError
except AssertionError:
traceback = sys.exc_info()[2]
back_frame = traceback.tb_frame.f_back
back_tb = types.TracebackType(tb_next=None,
tb_frame=back_frame,
tb_lasti=back_frame.f_lasti,
tb_lineno=back_frame.f_lineno)
raise AssertionError().with_traceback(back_tb)
def myassert_false():
"""Test myassert(). Debugger should point at the next line."""
myassert(False)
if __name__ == "__main__":
myassert_false()
Run Code Online (Sandbox Code Playgroud)
小智 8
看看jinja2在这里做了什么:
https://github.com/mitsuhiko/jinja2/blob/5b498453b5898257b2287f14ef6c363799f1405a/jinja2/debug.py
这很难看,但似乎做了你需要做的事情.我不会在这里复制粘贴示例,因为它很长.
不改变回溯怎么样?您要求的两件事都可以通过不同的方式更轻松地完成。
也就是说,如果你真的需要的话,应该很有可能修改回溯......但是你会在哪里做呢?如果在最顶层的某些包装器代码中,那么您可以简单地获取回溯,采取切片来删除您不需要的部分,然后使用“回溯”模块中的函数根据需要进行格式化/打印。