vscode - Encontrar un código muerto en un proyecto python grande
python with vs code (7)
He visto ¿Cómo se pueden encontrar funciones no utilizadas en el código de Python? pero eso es realmente viejo, y realmente no responde mi pregunta.
Tengo un gran proyecto de python con múltiples bibliotecas que son compartidas por múltiples scripts de punto de entrada. Este proyecto se ha acumulado durante muchos años con muchos autores, por lo que hay un montón de código muerto. Ya sabes que hacer.
Sé que encontrar el código muerto no es decidible. Todo lo que necesito es una herramienta que encuentre todas las funciones que no se llaman en ninguna parte. No estamos haciendo nada elegante con funciones de llamada basadas en la cadena del nombre de la función, por lo que no estoy preocupado por nada patológico ...
Acabo de instalar pylint, pero parece estar basado en archivos y no presto demasiada atención a las dependencias entre archivos, ni siquiera a las dependencias de funciones.
Claramente, podría grep para def en todos los archivos, obtener todos los nombres de funciones de eso, y hacer un grep para cada uno de esos nombres de funciones. Solo espero que haya algo un poco más inteligente que eso ya.
ETA: tenga en cuenta que no espero o no quiero algo perfecto. Sé que mi prueba de detención de problemas es igual de buena para todos (en realidad no enseñé la teoría de la computación, lo sé cuando estoy mirando algo recursivamente enumerable). Cualquier cosa que intente aproximarse ejecutando realmente el código llevará demasiado tiempo. Solo quiero algo que atraviese sintácticamente el código y diga "Esta función definitivamente se usa. Esta función SE PODRÍA usar, y esta función definitivamente NO se usa, ¡nadie más parece saber que existe!" Y las dos primeras categorías no son importantes.
Aquí está la solución que estoy usando al menos tentativamente:
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
Luego miro las funciones individuales que tienen muy pocas referencias (<3 dicen)
es feo, y solo me da respuestas aproximadas, pero creo que es lo suficientemente bueno para empezar. ¿Cuáles son tus pensamientos?
Con la siguiente línea, puede enumerar todas las definiciones de funciones que obviamente no se utilizan como un atributo, una llamada a función, un decorador o un valor de retorno. Entonces es aproximadamente lo que estás buscando. No es perfecto, es lento, pero nunca recibí falsos positivos. (Con Linux debes reemplazar ack
con 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
Es muy difícil determinar a qué funciones y métodos se llama sin ejecutar el código, incluso si el código no hace ninguna cosa elegante. Las invocaciones a funciones simples son bastante fáciles de detectar, pero las llamadas a métodos son realmente difíciles. Solo un simple ejemplo:
class A(object):
def f(self):
pass
class B(A):
def f(self):
pass
a = []
a.append(A())
a.append(B())
a[1].f()
Aquí no hay nada sofisticado, pero cualquier script que intente determinar si se llama Af()
o Bf()
tendrá un tiempo bastante difícil para hacerlo sin ejecutar realmente el código.
Si bien el código anterior no hace nada útil, ciertamente utiliza patrones que aparecen en código real, es decir, poner instancias en contenedores. El código real normalmente hará cosas aún más complejas: decapado y deshecho, estructuras de datos jerárquicas, condicionales.
Como se dijo anteriormente, solo detectando invocaciones de función simple de la forma
function(...)
o
module.function(...)
será bastante fácil. Puede usar el módulo ast
para analizar sus archivos fuente. Deberá registrar todas las importaciones y los nombres utilizados para importar otros módulos. También necesitará realizar un seguimiento de las definiciones de funciones de nivel superior y las llamadas dentro de estas funciones. Esto le dará un gráfico de dependencia, y puede usar NetworkX para detectar los componentes conectados de este gráfico.
Si bien esto puede sonar bastante complejo, probablemente se pueda hacer con menos de 100 líneas de código. Desafortunadamente, casi todos los proyectos principales de Python utilizan clases y métodos, por lo que será de poca ayuda.
Es posible que desee probar vulture . No puede captar todo debido a la naturaleza dinámica de Python, pero atrapa bastante sin necesidad de un conjunto de pruebas completo como coverage.py y otros deben funcionar.
IMO que podría lograrse muy rápidamente con un simple plugin de pylint que:
- recordar cada función / método analizado (/ clase?) en un conjunto S1
- rastrear cada función / método llamado (/ clase?) en un conjunto S2
- mostrar S1 - S2 en un informe
Entonces tendrías que llamar a pylint en todo tu código base para obtener algo que tenga sentido. Por supuesto, como dije, esto debería verificarse, ya que puede haber fallas de inferencia o tales que introducirían falsos positivos. De todos modos, eso probablemente reduciría en gran medida el número de grep por hacer.
Todavía no tengo mucho tiempo para hacerlo, pero cualquiera podría encontrar ayuda en la lista de correo de [email protected].
Intenta ejecutar la coverage.py Ned Batchelder.py .
Coverage.py es una herramienta para medir la cobertura de código de los programas de Python. Supervisa su programa, observa qué partes del código se han ejecutado y luego analiza la fuente para identificar el código que podría haberse ejecutado pero no lo estaba.
Si tiene su código cubierto con muchas pruebas (es bastante útil), ejecútelas con un complemento de cobertura de código y luego podrá ver el código que no utilizó.