【问题标题】:Using Python code coverage tool for understanding and pruning back source code of a large library使用 Python 代码覆盖工具来理解和修剪大型库的源代码
【发布时间】:2023-03-25 20:10:01
【问题描述】:

我的项目以低成本、低资源的嵌入式设备为目标。我依赖于一个相对庞大且庞大的 Python 代码库,其中我对其 API 的使用非常具体。

我热衷于通过在 Ned Batchelder 的 coveragefigleaf 等覆盖工具中执行我的测试套件,将这个库的代码精简到最低限度,然后在各种模块/文件中编写脚本删除未使用的代码。这不仅有助于理解库的内部结构,还有助于更轻松地编写任何补丁。 Ned 在他的一次在线演讲中实际上提到了使用覆盖工具对复杂代码进行“逆向工程”。

我向 SO 社区提出的问题是,人们是否有以这种方式使用覆盖工具的经验他们不介意分享?如果有什么陷阱? coverage 工具是一个不错的选择吗?还是我最好把时间花在 figleaf 上?

最终的目标是能够自动为库生成一个新的源代码树,基于原始树,但只包括我运行 nosetests /强>。

如果有人开发了一种工具,可以为他们的 Python 应用程序和库执行类似的工作,那么获得一个开始开发的基线将是非常棒的。

希望我的描述对读者有意义......

【问题讨论】:

  • 你需要有一套非常全面的测试,否则你可能会删除你的测试没有调用的一个函数,但在一些奇异但真实的情况下是需要的。我对此很难担心,因为很难达到 100% 的测试覆盖率。你有多确定你的测试是全面的?嵌入式系统:您是否在测试“内存不足”?
  • .... Ned 可能引用了测试覆盖率来“理解”,例如,查找与特定功能相关的代码,这很好,但它与查找 所有功能支持代码。

标签: python code-coverage reverse-engineering code-analysis


【解决方案1】:

您想要的不是“测试覆盖率”,而是“可以调用”从计算根开始的传递闭包。 (在线程应用程序中,您必须包含“can fork”)。

您想指定一些小的函数集(可能只有 1 个)构成应用程序的入口点,并希望跟踪该小集的所有可能的被调用者(有条件的或无条件的)。这是您必须拥有的一组功能。

由于动态调度,尤其是由于“eval”,Python 通常使这非常困难(IIRC,我不是 Python 专家)。对于应用于高度动态语言的静态分析器来说,推理可以调用​​什么函数可能非常棘手。

人们可能会使用测试覆盖率来将“可以调用”关系与特定的“确实调用”事实播种;这可能会捕获很多动态调度(取决于您的测试套件覆盖率)。那么你想要的结果是“can or did”调用的传递闭包。这仍然可能是错误的,但不太可能如此。

一旦您获得了一组“必要”功能,下一个问题将是从您拥有的源文件中删除不必要的功能。如果您开始使用的文件数量很大,那么手动删除死文件的工作量可能会非常高。更糟糕的是,您可能会修改您的应用程序,然后回答要保留更改的内容。因此,对于每次更改(发布),您都需要可靠地重新计算此答案。

我的公司构建了一个对 Java 包进行这种分析的工具(对动态加载和反射有适当的警告):输入是一组 Java 文件和(如上所述)一组指定的根函数。该工具计算调用图,并找到所有死成员变量并产生两个输出:a)据称死的方法和成员的列表,以及 b)删除了所有“死”内容的修改后的文件集。如果你相信 a),那么你就使用 b)。如果您认为 a) 是错误的,则将 a) 中列出的元素添加到根集并重复分析,直到您认为 a) 是正确的。为此,您需要一个静态分析工具来解析 Java、计算调用图,然后修改代码模块以删除无效条目。基本思想适用于任何语言。

我希望你需要一个类似的 Python 工具。

也许您可以坚持只删除完全未使用的文件,尽管这可能仍然需要大量工作。

【讨论】:

  • 理论上,对于我的应用程序,单元测试将非常接近您所指的传递闭包 - 尽管正如您正确指出的那样,如果没有更严格的分析,我永远无法绝对确定,还有额外的考虑退出可能性(异常,finally-blocks 等......)。能够从我正在使用的核心位中以正交目的修剪整个分支将是一个有用的过滤器。这显然存在风险,我需要更深入地考虑。
  • 即使我不部署修剪过的树,当涉及到在库中调试逻辑或开发补丁时,它肯定会引起我的注意——这是次要目标。
  • 在嵌入式上下文中使用 Python,我还可以做一些更激进的事情;添加一个顶级 try-catch 子句,该子句处理任何未在其他地方明确捕获的异常,以及由于不可靠的修剪而出现的异常。然后,即使修剪后的代码将在 99% 的时间内运行,但如果由于修剪不当而中断,它会优雅地失败并执行恢复启发式 - 例如重新启动。只是一个想法,不用说我不会急于部署此代码。
  • @landstatic:如果缺少的功能是需要的功能,就不能优雅的恢复。您没有描述您的嵌入式设备;我在工业界所经历的大多数都是即发即弃。特别是,您以后无法对其进行回补,因此您的回答将是不可接受的。
  • @Ira 我们的设备是具有可升级固件的互联网机顶盒,因此使用条件没有那么严格。感谢您的观察。很有趣。
【解决方案2】:

正如其他人所指出的,覆盖率可以告诉您执行了哪些代码。对您而言,诀窍是确保您的测试套件真正充分地练习代码。这里的失败案例是过度修剪,因为您的测试跳过了一些生产中真正需要的代码。

确保获取最新版本的coverage.py (v3.4):它添加了一个新功能来指示根本不会执行的文件。

BTW:: 对于第一次修剪,Python 提供了一个巧妙的技巧:删除源代码树中的所有 .pyc 文件,然后运行测试。仍然没有 .pyc 文件的文件显然没有执行!

【讨论】:

    【解决方案3】:

    我没有使用覆盖来修剪,但它似乎应该做得很好。我使用了nosetests + coverage的组合,它对我来说比figleaf更好。特别是,我发现来自 nosetests+coverage 的 html 报告很有帮助——这应该有助于您了解库中未使用的部分在哪里。

    【讨论】:

    • 感谢有关 figleaf 的反馈。我将同时试一试,但将我最初的注意力集中在报道上。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-10-14
    • 1970-01-01
    • 2011-07-07
    • 1970-01-01
    • 2016-10-23
    • 1970-01-01
    • 2015-04-02
    相关资源
    最近更新 更多