首页 > 后端开发 > Python教程 > 调试救星!利用 ObjWatch 在复杂的 Python 项目中进行高效的代码理解和调试

调试救星!利用 ObjWatch 在复杂的 Python 项目中进行高效的代码理解和调试

Mary-Kate Olsen
发布: 2025-01-06 02:43:41
原创
342 人浏览过

源代码链接

Debugging Savior! Leveraging ObjWatch for Efficient Code Comprehension and Debugging in Complex Python Projects 啊啊啊啊 / 对象观察

️ ObjWatch 是一个用于跟踪和监视对象属性和方法调用的 Python 库。

ObjWatch

Debugging Savior! Leveraging ObjWatch for Efficient Code Comprehension and Debugging in Complex Python Projects Debugging Savior! Leveraging ObjWatch for Efficient Code Comprehension and Debugging in Complex Python Projects Debugging Savior! Leveraging ObjWatch for Efficient Code Comprehension and Debugging in Complex Python Projects Debugging Savior! Leveraging ObjWatch for Efficient Code Comprehension and Debugging in Complex Python Projects Debugging Savior! Leveraging ObjWatch for Efficient Code Comprehension and Debugging in Complex Python Projects

[ 英语 | 中文]

概述

ObjWatch 是一个强大的 Python 库,旨在简化复杂项目的调试和监控。通过提供对象属性和方法调用的实时跟踪,ObjWatch 使开发人员能够更深入地了解其代码库,从而促进问题识​​别、性能优化和整体代码质量增强。

⚠️性能警告

ObjWatch 可能会影响您的应用程序的性能。建议仅在调试环境中使用它。

特点

  • 嵌套结构跟踪:通过清晰的分层日志记录可视化和监控嵌套函数调用和对象交互。

  • 增强的日志记录支持:利用 Python 的内置日志记录模块进行结构化、可定制的日志输出,包括对简单和详细格式的支持。此外,为了确保即使记录器被外部库禁用或删除也能捕获日志,您可以设置 level="force"。当 level 设置为“force”时,ObjWatch 会绕过标准日志处理程序并使用 print() 来…

在 GitHub 上查看

当前调试痛点

在读取和调试复杂项目时,经常会遇到多达十几层的嵌套调用,导致难以确定执行顺序。最令人沮丧的方面是在多进程环境中进行调试;调试单个进程常常会导致其他进程等待并超时,需要不断重新启动调试程序。频繁使用print语句会导致错过函数调用,费时费力。目前还没有一个兼具简单性和全面性的调试库,所以我花了一个周末开发了一个工具来解决这个痛点。

什么是 ObjWatch?

ObjWatch 专为简化复杂项目的调试和监控而设计。它提供对象属性和方法调用的实时跟踪,并允许自定义挂钩来帮助开发人员更深入地了解代码库。

快速使用示例

您可以直接使用 pip install objwatch 安装它。为了演示目的,您需要克隆源代码:

git clone https://github.com/aeeeeeep/objwatch
cd objwatch
pip install .
python3 examples/example_usage.py
登录后复制
登录后复制

执行上述代码会产生以下调用信息:

[2025-01-04 19:15:13] [DEBUG] objwatch: Processed targets:
>>>>>>>>>>
examples/example_usage.py
<<<<<<<<<<
[2025-01-04 19:15:13] [WARNING] objwatch: wrapper 'BaseLogger' loaded
[2025-01-04 19:15:13] [INFO] objwatch: Starting ObjWatch tracing.
[2025-01-04 19:15:13] [INFO] objwatch: Starting tracing.
[2025-01-04 19:15:13] [DEBUG] objwatch: run main <-
[2025-01-04 19:15:13] [DEBUG] objwatch: | run SampleClass.__init__ <- '0':(type)SampleClass, '1':10
[2025-01-04 19:15:13] [DEBUG] objwatch: | end SampleClass.__init__ -> None
[2025-01-04 19:15:13] [DEBUG] objwatch: | run SampleClass.increment <- '0':(type)SampleClass
[2025-01-04 19:15:13] [DEBUG] objwatch: | | upd SampleClass.value None -> 10
[2025-01-04 19:15:13] [DEBUG] objwatch: | | upd SampleClass.value 10 -> 11
[2025-01-04 19:15:13] [DEBUG] objwatch: | end SampleClass.increment -> None
[2025-01-04 19:15:13] [DEBUG] objwatch: | run SampleClass.increment <- '0':(type)SampleClass
[2025-01-04 19:15:13] [DEBUG] objwatch: | | upd SampleClass.value 11 -> 12
[2025-01-04 19:15:13] [DEBUG] objwatch: | end SampleClass.increment -> None
[2025-01-04 19:15:13] [DEBUG] objwatch: | run SampleClass.increment <- '0':(type)SampleClass
[2025-01-04 19:15:13] [DEBUG] objwatch: | | upd SampleClass.value 12 -> 13
[2025-01-04 19:15:13] [DEBUG] objwatch: | end SampleClass.increment -> None
[2025-01-04 19:15:13] [DEBUG] objwatch: | run SampleClass.increment <- '0':(type)SampleClass
[2025-01-04 19:15:13] [DEBUG] objwatch: | | upd SampleClass.value 13 -> 14
[2025-01-04 19:15:13] [DEBUG] objwatch: | end SampleClass.increment -> None
[2025-01-04 19:15:13] [DEBUG] objwatch: | run SampleClass.increment <- '0':(type)SampleClass
[2025-01-04 19:15:13] [DEBUG] objwatch: | | upd SampleClass.value 14 -> 15
[2025-01-04 19:15:13] [DEBUG] objwatch: | end SampleClass.increment -> None
[2025-01-04 19:15:13] [DEBUG] objwatch: | run SampleClass.decrement <- '0':(type)SampleClass
[2025-01-04 19:15:13] [DEBUG] objwatch: | | upd SampleClass.value 15 -> 14
[2025-01-04 19:15:13] [DEBUG] objwatch: | end SampleClass.decrement -> None
[2025-01-04 19:15:13] [DEBUG] objwatch: | run SampleClass.decrement <- '0':(type)SampleClass
[2025-01-04 19:15:13] [DEBUG] objwatch: | | upd SampleClass.value 14 -> 13
[2025-01-04 19:15:13] [DEBUG] objwatch: | end SampleClass.decrement -> None
[2025-01-04 19:15:13] [DEBUG] objwatch: | run SampleClass.decrement <- '0':(type)SampleClass
[2025-01-04 19:15:13] [DEBUG] objwatch: | | upd SampleClass.value 13 -> 12
[2025-01-04 19:15:13] [DEBUG] objwatch: | end SampleClass.decrement -> None
[2025-01-04 19:15:13] [DEBUG] objwatch: end main -> None
[2025-01-04 19:15:13] [INFO] objwatch: Stopping ObjWatch tracing.
[2025-01-04 19:15:13] [INFO] objwatch: Stopping tracing.
登录后复制

代码中最关键的部分如下:

# Using as a Context Manager with Detailed Logging
with objwatch.ObjWatch(['examples/example_usage.py']):
    main()

# Using the API with Simple Logging
obj_watch = objwatch.watch(['examples/example_usage.py'])
main()
obj_watch.stop()
登录后复制

我们可以通过上下文管理器和 API 调用来使用该工具。在示例中,我们指定对examples/example_usage.py 文件进行跟踪,这意味着该工具将记录examples/example_usage.py 中的任何函数、方法或变量。这种清晰的分层日志记录有助于可视化和监视嵌套函数调用和对象交互。打印的日志包括以下几种执行类型:

  • run:表示函数或类方法执行的开始。
  • end:表示函数或类方法执行结束。
  • upd:代表创建一个新变量。
  • apd:表示向列表、集合或字典等数据结构添加元素。
  • pop:标记从数据结构(如列表、集合或字典)​​中删除元素。

示例相对简单,但此功能对于执行大型项目非常有用。

整体特点

ObjWatch 提供以下接口:

  • 目标(列表):要监视的文件或模块。
  • except_targets(列表,可选):要从监视中排除的文件或模块。
  • ranks(列表,可选):使用 torch.distributed 时要跟踪的 GPU 排名。
  • 输出(str,可选):用于写入日志的文件的路径。
  • output_xml(str,可选):用于写入结构化日志的 XML 文件的路径。如果指定,跟踪信息将以嵌套 XML 格式保存,以便于浏览和分析。
  • level (str, 可选): 日志记录级别(例如logging.DEBUG、logging.INFO、force等)。
  • simple (bool, 可选): 启用简单日志记录模式,格式为“DEBUG: {msg}”。
  • 包装器(FunctionWrapper,可选):自定义包装器以扩展跟踪和日志记录功能。
  • with_locals(bool,可选):在函数执行期间启用函数内局部变量的跟踪和记录。
  • with_module_path (bool, 可选): 控制是否在日志中的函数名称前面添加模块路径。

主要功能:自定义包装扩展

ObjWatch 提供了 FunctionWrapper 抽象基类,允许用户创建自定义包装器来扩展和自定义库的跟踪和日志记录功能。通过继承FunctionWrapper,开发人员可以实现针对特定项目需求的定制行为。这些行为会在函数调用和返回时执行,提供更专业的监控。

函数包装类

FunctionWrapper 类定义了两个必须实现的核心方法:

  • wrap_call(self,func_name:str,frame:FrameType)-> str:

该方法在函数调用开始时被调用。它接收函数名称和当前帧对象,其中包含执行上下文,包括局部变量和调用堆栈。实现此方法以在函数执行之前提取、记录或修改信息。

  • wrap_return(self, func_name: str, result: Any) -> str:

此方法在函数返回时调用。它接收函数名称和函数返回的结果。使用此方法在函数完成执行后记录、分析或更改信息。

  • wrap_upd(self, old_value: Any, current_value: Any) ->元组[str, str]:

当变量更新时会触发该方法,接收旧值和当前值。它可用于记录变量的更改,从而允许跟踪和调试变量状态转换。

有关框架对象的更多详细信息,请参阅Python官方文档。

张量形状记录器

这是我根据我的使用场景实现的自定义包装器的示例。代码位于 objwatch/wrappers.py 文件中。该包装器自动记录指定模块内所有函数方法调用中输入和输出的张量形状以及变量的状态。这对于理解复杂分布式框架的执行逻辑非常有用。

git clone https://github.com/aeeeeeep/objwatch
cd objwatch
pip install .
python3 examples/example_usage.py
登录后复制
登录后复制

在深度学习项目中,张量的形状和维度至关重要。小尺寸误差可能会导致整个模型无法正确训练或预测。手动检查每个张量的形状既乏味又容易出错。 TensorShapeLogger 自动记录张量形状,帮助开发人员:

  • 快速识别尺寸不匹配问题:自动记录形状信息,以便及时检测并修复尺寸错误。
  • 优化模型架构:通过跟踪张量形状的变化,优化网络结构以提高模型性能。
  • 提高调试效率:减少手动检查张量形状所花费的时间,从而专注于核心模型开发。

使用自定义包装器的示例

建议参考tests/test_torch_train.py文件。该文件包含 PyTorch 训练过程的完整示例,演示如何集成 ObjWatch 进行监控和日志记录。

笔记

⚠️ 性能警告
在调试环境中使用 ObjWatch 会影响程序的性能。因此,建议仅在调试和开发阶段使用它。

这只是初步的写作;我计划随着时间的推移添加更多。如果您觉得有用,请随意给它一个star。

该库仍在积极更新中。如果您有任何问题或建议,请发表评论或在存储库中打开问题。

以上是调试救星!利用 ObjWatch 在复杂的 Python 项目中进行高效的代码理解和调试的详细内容。更多信息请关注PHP中文网其他相关文章!

来源:dev.to
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
作者最新文章
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板