Подтвердить что ты не робот

Поиск мертвого кода в большом проекте python

Я видел Как вы можете найти неиспользуемые функции в коде Python?, но это действительно старое и на самом деле не отвечает на мой вопрос.

У меня есть большой проект python с несколькими библиотеками, которые разделяются несколькими сценариями точки входа. Этот проект в течение многих лет акридировался со многими авторами, поэтому там было много мертвого кода. Вы знаете, что делать.

Я знаю, что найти все мертвые коды невозможно. Все, что мне нужно - это инструмент, который найдет все функции, которые нигде не называются. Мы ничего не делаем с вызовами функций, основанных на строке имени функции, поэтому я не беспокоюсь о чем-то патологическом...

Я только что установил pylint, но он, похоже, основан на файлах, и не уделяет много внимания зависимостям между файлами или даже зависимостям между функциями.

Ясно, что я мог бы grep для def во всех файлах, получить из них все имена функций и сделать grep для каждого из этих имен функций. Я просто надеюсь, что там что-то немного умнее, чем это уже есть.

ETA: Пожалуйста, обратите внимание, что я не ожидаю и не хочу что-то совершенное. Я знаю, что моя проблема с остановкой - так же хорошо, как и все (я действительно не преподавал теорию вычислений, которую знаю, когда я смотрю на то, что рекурсивно перечислимо). Любая вещь, которая пытается аппроксимировать ее, фактически запустив код, займет слишком много времени. Я просто хочу что-то синтаксически проходит через код и говорит: "Эта функция определенно используется. Эта функция МОЖЕТ использоваться, и эта функция, безусловно, НЕ используется, никто другой даже не знает, что она существует!" И первые две категории не важны.

4b9b3361

Ответ 1

Возможно, вы захотите попробовать vulture. Он не может поймать все из-за динамической природы Python, но он ловит совсем немного, не требуя полного набора тестов, такого как cover.py, и другие должны работать.

Ответ 2

Попробуйте запустить Ned Batchelder coverage.py.

Coverage.py - инструмент для измерения охвата кода программ Python. Он контролирует вашу программу, отмечая, какие части кода были выполнены, затем анализирует источник, чтобы определить код, который мог быть выполнен, но не был.

Ответ 3

Очень сложно определить, какие функции и методы вызывать, не выполняя код, даже если код не делает каких-либо причудливых вещей. Обычные вызовы функций довольно легко обнаружить, но вызовы методов действительно сложны. Простой пример:

class A(object):
    def f(self):
        pass

class B(A):
    def f(self):
        pass

a = []
a.append(A())
a.append(B())
a[1].f()

Ничего особенного здесь не происходит, но любой script, который пытается определить, будет ли вызываться A.f() или B.f(), будет иметь довольно трудное время, не выполняя этот код.

В то время как вышеприведенный код не делает ничего полезного, он, безусловно, использует шаблоны, которые отображаются в реальном коде, а именно - размещение экземпляров в контейнерах. Реальный код обычно выполняет еще более сложные вещи - травление и рассыпание, иерархические структуры данных, условные обозначения.

Как указано выше, просто обнаружение простых функций-вызовов формы

function(...)

или

module.function(...)

будет довольно легко. Вы можете использовать модуль ast для анализа ваших исходных файлов. Вам нужно будет записать весь импорт и имена, используемые для импорта других модулей. Вам также необходимо будет отслеживать определения функций верхнего уровня и вызовы внутри этих функций. Это даст вам график зависимости, и вы можете использовать NetworkX для обнаружения подключенных компонентов этого графика.

Хотя это может показаться довольно сложным, возможно, это может быть сделано с менее чем 100 строками кода. К сожалению, почти все основные проекты Python используют классы и методы, поэтому это будет мало помогать.

Ответ 4

Здесь решение, которое я использую, по крайней мере, ориентировочно:

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)

это уродливо, и это дает мне приблизительные ответы, но я думаю, что это достаточно хорошо для начала. Каковы ваши мысли?

Ответ 5

В следующей строке вы можете перечислить все определения функций, которые явно не используются в качестве атрибута, вызова функции, декоратора или возвращаемого значения. Таким образом, это примерно то, что вы ищете. Он не идеален, он медленный, но у меня никогда не было никаких ложных срабатываний. (С 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

Ответ 6

Если у вас есть код с большим количеством тестов (это вообще очень полезно), запустите их с плагином для покрытия кода, и вы можете увидеть неиспользуемый код.)

Ответ 7

IMO, который может быть достигнут довольно быстро с помощью простого плагина pylint, который:

  • запомнить каждую анализируемую функцию/метод (/класс?) в наборе S1
  • отслеживать каждую вызванную функцию/метод (/класс?) в наборе S2
  • отображение S1 - S2 в отчете

Затем вам нужно будет вызвать pylint на всей вашей базе кода, чтобы получить что-то, что имеет смысл. Конечно, как сказано, это нужно проверить, так как, возможно, были ошибки вывода или такой, который вводит ложноположительный. В любом случае, это, вероятно, значительно уменьшит количество grep, которое нужно выполнить.

У меня не так много времени, чтобы сделать это сам, но каждый найдет помощь в списке рассылки [email protected]