inspect.getframeinfo() 必须配合 sys.exc_info()[2] 的 traceback 对象使用才能准确定位异常位置,直接调用仅返回当前帧信息;正确做法是通过 inspect.getinnerframes(tb) 获取异常帧列表,取第0项提取文件、行号、函数名。

inspect.getframeinfo() 能拿到异常位置,但得配合 sys.exc_info()
直接调用 inspect.getframeinfo() 拿不到异常点——它只查当前帧。真要定位异常发生处,必须先从 sys.exc_info() 拿到 traceback 对象,再用 inspect.getframeinfo() 解析最内层帧。
常见错误是写成这样:
import inspect inspect.getframeinfo(inspect.currentframe()) # 这只是打印当前这行,不是异常位置
正确做法是:
- 在
except块里调用sys.exc_info(),获取(type, value, tb) - 用
traceback.extract_tb(tb)或inspect.getframeinfo(tb.tb_frame)向下遍历到最深的tb.tb_next,直到tb.tb_next is None - 对最终那个
tb.tb_frame调用inspect.getframeinfo(),才得到异常实际发生的文件、行号、函数名
用 inspect.trace() 更省事,但它默认不包含异常帧
inspect.trace() 返回的是「当前执行栈」,不是「异常栈」。想让它反映异常上下文,得手动把 traceback 帧塞进去:
- 不能直接
inspect.trace()—— 它只返回从当前except块往上的调用链 - 要用
inspect.getinnerframes(tb)(注意是getinnerframes,不是trace),它接受sys.exc_info()[2],返回从异常点开始的完整帧列表 -
getinnerframes()返回的元组里,第 0 项就是异常发生的那一帧,[0][1]是行号,[0][2]是函数名
示例:
仅使用 Python 将 Claw Code 虚拟化框架与 OpenClaw 集成,提供 184 种工具和 200 多条命令,用于安全分析和代码质量...
import sys, inspect
try:
1/0
except:
tb = sys.exc_info()[2]
frames = inspect.getinnerframes(tb)
filename, lineno, function, _, _ = frames[0]
print(f"Error in {filename}:{lineno} in {function}")
inspect.currentframe().f_back 不可靠,尤其在优化模式或装饰器里
有人想绕过 sys.exc_info(),直接用 inspect.currentframe().f_back 获取上一层帧来“猜”异常位置。这在大多数情况下会失败:
- CPython 启用
-O优化时,f_back可能为None - 被
@functools.wraps或其他装饰器包裹的函数,f_back指向的是装饰器内部,不是原始调用点 - 异步函数(
async def)中,f_back指向事件循环调度逻辑,完全失真
真正稳定的路径只有一条:从 sys.exc_info()[2] 出发,逐级 tb.tb_next 直到末端。
别漏掉 tb.tb_lineno 和 tb.tb_frame.f_lineno 的区别
同一个 traceback 帧对象里,tb.tb_lineno 和 tb.tb_frame.f_lineno 看似一样,但行为不同:
-
tb.tb_lineno是异常真正触发的行号(准确,推荐用) -
tb.tb_frame.f_lineno是帧当前“执行到”的行号,可能因断点、跳转或调试器干预而偏移 - 在生成器或协程中,
f_lineno可能停在yield或await行,而非raise行
所以定位异常点时,务必取 tb.tb_lineno,而不是依赖帧对象的 f_lineno。
异常上下文不是靠猜出来的,是靠从 sys.exc_info()[2] 一帧一帧扒到底才确定的。中间任何跳步,比如省略 tb_next 遍历、依赖 f_back 或混用 f_lineno,都会在边界场景翻车。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










