You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何Python自定义追踪脚本无法打印被追踪代码的所有行?

自定义Python代码执行追踪的问题与优化方案

问题背景

修改了一段5年前的代码实现自定义代码执行追踪,遇到三个核心问题:

  • 无法理解原代码的工作逻辑
  • 追踪输出遗漏了目标代码的第5行和第8行
  • 需要替代原粗糙hack实现的更优方案,同时支持自定义输出格式

原代码通过子进程动态生成包含追踪逻辑的代码,执行后输出的追踪记录中,目标代码里单独的闭合括号行(第5行、第8行)没有被捕获。

原代码逻辑分析

原代码的工作流程:

  1. 定义tracerCode模板,包含sys.settrace追踪逻辑和嵌入目标代码的占位符
  2. 将目标代码codeToRun处理后(按行分割、去除空白后用|拼接)填充到模板中
  3. 通过subprocess.Popen启动新Python进程,将生成的追踪代码传入执行
  4. 追踪逻辑里的monitor函数通过硬编码的行号偏移(frame.f_lineno-30)对应目标代码的行,打印追踪信息

遗漏行原因

输出遗漏第5行和第8行的核心原因是Python解释器的字节码编译特性:
目标代码中第5行是print(的闭合),第8行是time.sleep(的闭合),这些行属于多行表达式的语法换行,Python编译时不会为这些单独的括号行生成可执行字节码。sys.settrace的line事件仅会触发在有实际字节码对应的源代码行上,因此这些纯括号的行不会被追踪到。

另外原代码的硬编码行号偏移(固定减30)是非常脆弱的hack:一旦tracerCode模板的行数变化,行号对应关系就会完全错误,这也是原实现的重大缺陷。

优化后的代码实现

下面是更可靠的自定义追踪实现,避免硬编码偏移,直接通过inspect模块获取源代码行,同时用上下文管理器简化追踪逻辑:

import sys
import inspect
from contextlib import contextmanager

@contextmanager
def custom_trace(output_func=None):
    """自定义代码行追踪上下文管理器,支持自定义输出函数"""
    # 默认输出函数,可替换为自定义格式
    def default_output(line_num, line_content):
        print(f"line {line_num}: {line_content.strip()}")
        sys.stdout.flush()

    output = output_func or default_output

    def monitor(frame, event, arg):
        if event == "line":
            try:
                # 获取当前执行的源代码行信息
                frame_info = inspect.getframeinfo(frame)
                # 跳过追踪上下文管理器自身的代码
                if frame_info.filename == __file__ and frame_info.function == "custom_trace":
                    return monitor
                # 获取当前行内容
                line_content = frame_info.code_context[0]
                output(frame_info.lineno, line_content)
            except (IndexError, AttributeError):
                # 处理无法获取代码行的边界情况
                pass
        return monitor

    original_trace = sys.gettrace()
    sys.settrace(monitor)
    try:
        yield
    finally:
        # 恢复原追踪函数
        sys.settrace(original_trace)

# 示例:要执行的目标代码
def target_code():
    import time
    for _ in range(1): 
        print( 
            'hello' 
        )
        time.sleep(
            5
        )
    print('goodbye')

# 自定义输出格式示例(可选)
def my_custom_output(line_num, content):
    print(f"[追踪记录] 行号:{line_num} 内容:{content.strip()}")

# 使用默认输出执行追踪
print("=== 默认格式追踪 ===")
with custom_trace():
    target_code()

# 使用自定义输出格式执行追踪
print("\n=== 自定义格式追踪 ===")
with custom_trace(output_func=my_custom_output):
    target_code()

优化点说明

  • 避免硬编码偏移:通过inspect.getframeinfo直接获取当前执行的行号和代码内容,无需依赖模板行数计算偏移
  • 灵活的输出自定义:支持传入自定义输出函数,轻松修改追踪记录的格式
  • 更简洁的资源管理:用contextmanager自动管理sys.settrace的开启和恢复,避免资源泄漏
  • 边界情况处理:捕获无法获取代码行的异常,提升稳定性

关于多行表达式的说明

需要注意:即使使用优化后的代码,那些仅包含闭合括号的语法换行行仍然不会被追踪到——这是Python解释器的固有特性,因为这些行没有对应的可执行字节码,不属于实际执行的代码行。如果需要完整展示源代码的每一行,可以在追踪前先打印目标代码的完整内容,再结合执行追踪记录。

内容的提问来源于stack exchange,提问作者user7711283

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 10:05:28