web-dev-qa-db-ja.com

大きなpythonプロジェクトでデッドコードを見つける

Python code? で未使用の関数を見つけるにはどうすればよいですか?)==しかし、それは本当に古く、私の質問に実際には答えていません。

私は大きなpython複数のエントリポイントスクリプトによって共有される複数のライブラリを持つプロジェクトを持っています。このプロジェクトは多くの著者と共に長年にわたって蓄積されてきました。ドリル。

すべてのデッドコードを見つけることは決定できないことを知っています。必要なのは、どこでも呼び出されないすべての関数を見つけるツールです。関数名の文字列に基づいて関数を呼び出すことについては何も凝っていませんので、病理学的なことについては心配していません...

私はちょうどpylintをインストールしましたが、ファイルベースであるようで、ファイル間の依存関係、さらには関数の依存関係にあまり注意を払っていません。

明らかに、すべてのファイルでdefをgrepし、そこからすべての関数名を取得し、それらの関数名ごとにgrepを実行できます。すでにそれよりも少しスマートなものがあることを望んでいます。

ETA:完璧なものを期待したり、求めたりしないことに注意してください。私は誰でも同様に私の停止問題の証拠を知っています(再帰的に列挙可能なものを見ているとき、私は実際に計算理論を教えていません)。実際にコードを実行して概算しようとすると、時間がかかりすぎます。構文的にコードを調べて、「この関数は間違いなく使用されます。この関数は使用される可能性があり、この関数は間違いなく使用されます。そして、最初の2つのカテゴリは重要ではありません。

67
Brian Postow

vulture を試してみてください。 Pythonの動的な性質のためにすべてをキャッチすることはできませんが、coverage.pyなどの完全なテストスイートを必要とせずにかなりキャッチします。

36
Keith Gaughan

Ned Batcheldercoverage.py を実行してみてください。

Coverage.pyは、Pythonプログラムのコードカバレッジを測定するためのツールです。プログラムの監視を行い、コードのどの部分が実行されたかを記録し、ソースを分析して実行可能なコードを特定しますしかし、そうではなかった。

15
Peter Wood

コードが派手なことをしていなくても、コードを実行せずにどの関数とメソッドが呼び出されるかを判断することは非常に困難です。プレーンな関数の呼び出しはかなり簡単に検出できますが、メソッドの呼び出しは非常に困難です。簡単な例:

_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プロジェクトはクラスとメソッドを使用するため、ほとんど役に立ちません。

7
Sven Marnach

少なくとも暫定的に使用しているソリューションは次のとおりです。

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)

ugいし、おおよその答えしか得られませんが、最初は十分だと思います。あなたの考えは何ですか?

6
Brian Postow

次の行を使用すると、属性、関数呼び出し、デコレータ、または戻り値として明らかに使用されていないすべての関数定義をリストできます。だから、おおよそあなたが探しているものです。完璧ではなく、遅いですが、誤検知はありませんでした。 (Linuxでは、ackack-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
4
diefans

コードが多くのテストでカバーされている場合(まったく役に立ちます)、コードカバレッジプラグインで実行すると、未使用のコードが表示されます。

1
yedpodtrzitko

IMOは、次の単純なpylintプラグインで非常に迅速に達成できます。

  • s1セットの分析された各関数/メソッド(/クラス?)
  • s2セットで呼び出された各関数/メソッド(/クラス?)を追跡する
  • レポートにS1-S2を表示する

次に、すべてのコードベースでpylintを呼び出して、意味のあるものを取得する必要があります。もちろん、前述のように、推論の失敗や誤検知が発生する可能性があるため、これを確認する必要があります。とにかく、おそらくgrepの数が大幅に削減されます。

まだ自分でやる時間はあまりありませんが、python-projects @ logilab.orgメーリングリストで誰でも助けを見つけるでしょう。

1
sthenault