我见过How can you find unused functions in Python code?,但那真的很旧,并没有真正回答我的问题。
我有一个包含多个库的大型python项目,这些库由多个入口点脚本共享。多年来,这个项目已经吸引了许多作者,所以有很多死代码。你知道该怎么做。
我知道找到所有死代码是不可判定的。我所需要的只是一个工具,可以找到所有未被调用的函数。我们没有根据函数名称的字符串调用函数做任何事情,所以我不担心任何病态......
我刚安装了pylint,但它似乎是基于文件的,并没有太多关注文件间依赖关系,甚至是函数依赖关系。
显然,我可以在所有文件中查找def,从中获取所有函数名称,并为每个函数名称执行grep。我只是希望有一些比那里更聪明的东西。
ETA:请注意,我不希望或想要完美的东西。我知道我的停止问题证明就像任何人一样(当我看到递归可枚举的东西时,我真的没有教过计算理论)。任何试图通过实际运行代码来逼近它的东西都会花费太长时间。我只是想要在语法上通过代码的东西并说“这个函数是肯定使用的。可以使用这个函数,这个函数绝对不使用,其他人甚至似乎都不知道它存在!”前两个类别并不重要。
答案 0 :(得分:35)
您可能想要试用vulture。由于Python的动态特性,它无法捕捉到所有内容,但它需要相当多,而不需要像coverage.py这样的完整测试套件,而其他人需要工作。
答案 1 :(得分:15)
尝试运行Ned Batchelder的coverage.py。
Coverage.py是一种用于测量Python程序代码覆盖率的工具。它监视您的程序,注意代码的哪些部分已被执行,然后分析源代码以识别可能已执行但未执行的代码。
答案 2 :(得分:7)
在不执行代码的情况下很难确定调用哪些函数和方法,即使代码没有做任何花哨的东西。普通函数调用很容易检测,但方法调用真的很难。只是一个简单的例子:
class A(object):
def f(self):
pass
class B(A):
def f(self):
pass
a = []
a.append(A())
a.append(B())
a[1].f()
这里没有任何花哨的东西,但是任何试图确定调用A.f()
或B.f()
的脚本都会在没有实际执行代码的情况下进行相当困难的时间。
虽然上面的代码没有做任何有用的事情,但它肯定使用出现在实际代码中的模式 - 即将实例放在容器中。真正的代码通常会做更复杂的事情 - 酸洗和去除,分层数据结构,条件。
如前所述,只需检测表单的简单函数调用
function(...)
或
module.function(...)
会很容易。您可以使用ast
模块来解析源文件。您将需要记录所有导入以及用于导入其他模块的名称。您还需要跟踪顶级函数定义和这些函数内的调用。这将为您提供依赖关系图,您可以使用NetworkX来检测此图的连接组件。
虽然这可能听起来相当复杂,但可能只需少于100行代码即可完成。不幸的是,几乎所有主要的Python项目都使用类和方法,所以它几乎没有帮助。
答案 3 :(得分:6)
这是我至少暂时使用的解决方案:
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 say)
它是丑陋的,它只给出了大致的答案,但我认为这对于一个开始是好的。你们有什么想法?答案 4 :(得分:4)
使用以下行,您可以列出显然不用作属性,函数调用,装饰器或返回值的所有函数定义。所以它几乎就是你要找的东西。这不是完美的,它很慢,但我从来没有得到任何误报。 (使用linux,您必须将ack
替换为ack-grep
)
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
答案 5 :(得分:1)
如果您的代码包含大量测试(完全非常有用),请使用代码覆盖率插件运行它们,然后您可以看到未使用的代码。)
答案 6 :(得分:1)
使用简单的pylint插件可以很快实现IMO:
然后你必须在所有代码库上调用pylint来获得有意义的东西。 当然,如上所述,这需要检查,因为可能存在推理失败或 这会引入假阳性。无论如何,这可能会大大减少要完成的grep数量。
我自己没有太多时间去做,但是有人会在python-projects@logilab.org邮件列表上找到帮助。