在大型python项目中查找死代码

我见过如何在Python代码中find未使用的函数? 但是这真的很老,并没有真正回答我的问题。

我有一个大型的Python项目与多个入口点脚本共享多个库。 这个项目已经和很多作者一起吸引了很多年,所以有很多死代码。 你知道该怎么做。

我知道find所有的死代码是不可判定的。 我所需要的只是一个能find所有不被任何地方调用的函数的工具。 我们根本不用调用基于函数名string的函数做任何事情,所以我不担心任何病态的…

我只是安装了pylint,但它似乎是基于文件的,而不是很注意文件间依赖关系,甚至是函数依赖关系。

显然,我可以在所有的文件中使用grep来获取def,从中获得所有的函数名,并为每个函数名做一个grep。 我只是希望有一些比那里更聪明的东西。

ETA:请注意,我不期望或想要完美的东西。 我知道我的停止问题certificate和其他人一样(当我看到recursion可枚举的东西时,我没有真正教过计算理论)。 任何试图通过实际运行代码来逼近它的事情都会花费太长时间。 我只是想在语法上通过代码的东西,并说:“这个function是肯定使用的,这个function可能会被使用,而且这个function是绝对不使用,其他人甚至没有人知道它存在! 前两个类别并不重要。

你可能想试试秃鹫 。 由于Python的dynamic特性,它无法捕捉所有的东西,但是它却捕获了很多,而不需要完整的testing套件,比如coverage.py和其他的需要工作。

尝试运行Ned Batchelder的coverage.py 。

Coverage.py是测量Python程序代码覆盖率的工具。 它监视你的程序,注意代码的哪个部分已经被执行,然后分析源代码来识别可能已经被执行但没有被执行的代码。

即使代码没有做任何花哨的东西,也很难确定哪些函数和方法被调用而不执行代码。 平原函数调用相当容易检测,但方法调用真的很难。 只是一个简单的例子:

 class A(object): def f(self): pass class B(A): def f(self): pass a = [] a.append(A()) a.append(B()) a[1].f() 

这里没有什么奇特的东西,但是任何试图确定是否调用Af()Bf()脚本在实际执行代码时都会遇到相当困难的时间。

虽然上面的代码没有做任何有用的事情,但它确实使用了以实际代码出现的模式 – 即将实例放入容器中。 真正的代码通常会做更复杂的事情 – 酸洗和取消,分层数据结构,条件。

如前所述,只是检测表单的纯函数调用

 function(...) 

要么

 module.function(...) 

将是相当容易的。 您可以使用ast模块来parsing您的源文件。 您将需要logging所有导入以及用于导入其他模块的名称。 您还需要跟踪顶层函数定义以及这些函数中的调用。 这会给你一个依赖关系图,你可以使用NetworkX来检测这个图的连接组件。

虽然这听起来可能相当复杂,但可能只需less于100行的代码。 不幸的是,几乎所有主要的Python项目都使用类和方法,所以它没有什么帮助。

以下是我至less暂时使用的解决scheme:

 grep 'def ' *.py > defs # ... # edit defs so that it just contains the function names # ... for f in `cat defs` do cat $f >> defCounts cat *.py | grep -c $f >> defCounts echo >> defCounts done 

然后我看看几乎没有参考的个别函数(<3说)

这是丑陋的,它只给了我近似的答案,但我觉得这足够一开始。 你是什​​么 – 所有的想法?

通过以下代码行,您可以列出明显不用作属性,函数调用,装饰器或返回值的所有函数定义。 所以这大概是你在找什么。 这不是完美的,它是缓慢的,但我从来没有任何误报。 (用linux你必须用ack-grep来replaceack

 for f in $(ack --python --ignore-dir tests -h --noheading "def ([^_][^(]*).*\):\s*$" --output '$1' | sort| uniq); do c=$(ack --python -ch "^\s*(|[^#].*)(@|return\s+|\S*\.|.*=\s*|)"'(?<!def\s)'"$f\b"); [ $c == 0 ] && (echo -n "$f: "; ack --python --noheading "$f\b"); done 

如果你的代码覆盖了很多testing(这是非常有用的),运行它们的代码覆盖插件,你可以看到未使用的代码。)

国际海事组织,可以很快实现一个简单的pylint插件:

  • 记住S1集中每个分析的函数/方法(/ class?)
  • 跟踪S2集中每个被调用的函数/方法(/ class?)
  • 在报告中显示S1 – S2

那么你将不得不在所有的代码库上调用pylint来获得有意义的东西。 当然,正如所说的那样,这需要检查,因为可能有推理失败或者会导致误报。 无论如何,这可能会大大减less要完成的grep的数量。

我自己没有太多时间去做,但是任何人都可以在python-projects@logilab.org邮件列表上find帮助。