使用Python代码覆盖工具理解并修剪大型库的源码

8 投票
3 回答
2313 浏览
提问于 2025-04-16 05:08

我的项目是针对一种低成本、低资源的嵌入式设备。我依赖于一个相对庞大且复杂的Python代码库,而我使用这个库的API的方式比较具体。

我想把这个库的代码精简到最基本的部分,方法是使用像Ned Batchelder的coveragefigleaf这样的覆盖率工具来运行我的测试套件,然后编写脚本来删除未使用的代码,这些代码分布在不同的模块和文件中。这不仅有助于我理解库的内部结构,还能让编写补丁变得更简单。Ned在他的一次在线演讲中提到过,使用覆盖率工具可以“逆向工程”复杂的代码。

我想问问大家,是否有人有过以这种方式使用覆盖率工具的经验,愿意分享一下?有没有什么需要注意的地方?coverage工具是否是一个好的选择?还是说我应该花时间去研究figleaf

我的最终目标是能够自动生成一个新的库源代码树,基于原始的代码树,但只包含在我运行nosetests实际使用过的代码。

如果有人开发过类似的工具来处理他们的Python应用和库,能够提供一个起点就太好了。

希望我的描述能让读者明白……

3 个回答

0

我没有用过coverage来进行剪枝,但感觉它应该效果不错。我用过nosetests和coverage的组合,这对我来说比figleaf更好。特别是,我发现nosetests和coverage生成的html报告很有帮助——这应该能帮助你了解库中哪些部分没有被使用。

2

正如其他人提到的,代码覆盖率可以告诉你哪些代码被执行过。关键是要确保你的测试套件能够全面测试代码。这里的一个常见问题是过度修剪,因为你的测试可能跳过了一些在实际运行中确实需要的代码。

记得下载最新版本的coverage.py(版本3.4):这个版本增加了一个新功能,可以标记那些根本没有被执行过的文件。

顺便说一下,对于初步的修剪,Python提供了一个很简单的方法:删除你源代码目录下的所有.pyc文件,然后运行你的测试。那些仍然没有.pyc文件的文件,显然是没有被执行过的!

9

你想要的不是“测试覆盖率”,而是从计算的起点出发,找出“可以调用”的所有可能性。 (在多线程应用中,还需要考虑“可以分叉”的情况)。

你需要指定一小部分(可能只有一个)函数,这些函数是你应用程序的入口点,然后追踪所有可能被调用的函数(无论是有条件的还是无条件的)。这就是你必须拥有的函数集合。

在Python中,这个过程通常很困难(我记得没错,我不是Python的深度专家),因为Python有动态调度,特别是“eval”这个功能。对于高度动态的语言,静态分析工具要判断哪些函数可能被调用是相当棘手的。

你可以利用测试覆盖率来为“可以调用”的关系提供一些具体的“确实调用”的事实;这可以捕捉到很多动态调用(这取决于你的测试覆盖情况)。然后你想要的结果就是“可以或确实”调用的关系的传递闭包。虽然这仍然可能出错,但出错的可能性会小一些。

一旦你得到了一个“必要”的函数集合,下一个问题就是去除你源文件中不必要的函数。如果你开始时的文件数量很大,手动去除这些无用的东西可能会非常繁琐。更糟的是,你可能会对应用进行修改,这样你需要保留的内容也会随之改变。因此,对于每一次修改(发布),你都需要可靠地重新计算这个答案。

我的公司开发了一款工具,可以对Java包进行这种分析(当然要考虑动态加载和反射的问题):输入是一组Java文件和(如上所述)指定的一组根函数。这个工具会计算调用图,并找到所有无用的成员变量,输出两个结果:a) 声称无用的方法和成员列表,b) 一组修订后的文件,所有“无用”的东西都被移除。如果你相信a),那么就使用b)。如果你认为a)是错误的,那么就把a)中列出的元素添加到根集合中,重复分析,直到你认为a)是正确的。为了做到这一点,你需要一个静态分析工具来解析Java,计算调用图,然后修订代码模块以去除无用的条目。这个基本思路适用于任何语言。

我想你也需要一个类似的工具来处理Python。

也许你可以先删除那些完全没有使用的文件,尽管这可能仍然需要很多工作。

撰写回答