Dibujar un gráfico de llamadas

Estoy manteniendo una base de código antigua escrita en python. En particular, hay un fragmento de código complejo que desde un módulo llama a otras funciones de otros módulos que llaman a otras funciones y así sucesivamente. No es POO, solo funciones y módulos.
He intentado hacer un seguimiento de dónde comienza y termina el flujo cada vez que llamo a la función principal, pero siento que necesito dibujar esto porque me estoy perdiendo en las sub-llamadas.

Lo que me preocupa es que cada función llama a múltiples funciones externas dentro de su cuerpo para completar su tarea y devolver el valor a la persona que llama.

¿Cómo puedo dibujar esto? ¿Qué tipo de gráfico / gráfico sería apropiado para documentar este tipo de comportamiento / código?

Entonces, no creo que sea útil para dibujar un diagrama UML , ni un diagrama de flujo. ¿Un gráfico de llamadas, tal vez?

Comentarios

  • doxygen: generará gráficos de llamadas / personas que llaman, I ' No estoy seguro de cuánto soporte tiene para Python. Sé que puedes documentar el código Python para ello.
  • Yo ' he probado pycallgraph pero ' también complicado / demasiado profundo para usarlo. Esto se debe a la complejidad de mi código porque mezcla python simple con django y una llamada externa a la URL de la API. Por eso quise dibujarlo a mano solo teniendo en cuenta la parte relevante que necesito. El problema es que ' no sé qué tipo de gráfico usar para tener una comprensión completa del sistema
  • Si esto es solo para ayudarlo a comprenderlo , simplemente dibuja lo que venga naturalmente. Siempre puede ordenarlo más tarde si ' va a la documentación formal.

Respuesta

Creo que lo que «estás buscando aquí es un Diagrama de secuencia . Estos te permiten visualizar el orden en el que varios módulos llaman entre sí mediante el uso de flechas.

Construir una es simple:

  1. Dibuja tu clase inicial con una línea de puntos debajo.
  2. Dibuja la siguiente clase / método en el seguimiento de llamadas con una línea punteada debajo
  3. Conecte las líneas con una flecha, colocada verticalmente debajo de la última flecha que dibujó
  4. Repita los pasos 2-3 para todas las llamadas en su traza

Ejemplo

Supongamos que tenemos el siguiente código para el que queremos crear un diagrama de secuencia:

def long_division(quotient, divisor): solution = "" remainder = quotient working = "" while len(remainder) > 0: working += remainder[0] remainder = remainder[1:] multiplier = find_largest_fit(working, divisor) solution += multiplier working = calculate_remainder(working, multiplier, divisor) print solution def calculate_remainder(working, multiplier, divisor): cur_len = len(working) int_rem = int(working) - (int(multiplier) * int (divisor)) return "%*d" % (cur_len, int_rem) def find_largest_fit(quotient, divisor): if int(divisor) == 0: return "0" i = 0 while i <= 10: if (int(divisor) * i) > int(quotient): return str(i - 1) else: i += 1 if __name__ == "__main__": long_division("645", "5") 

Lo primero que dibujaremos es el punto de entrada (main) que se conecta al método long_division . Tenga en cuenta que esto crea un cuadro en long_ división, que significa el alcance de la llamada al método. Para este ejemplo simple, la caja tendrá la altura completa de nuestro diagrama de secuencia debido al hecho de que esto es lo único que se ejecuta.

ingrese la descripción de la imagen aquí

Ahora llamamos a find_largest_fit para encontrar el múltiplo más grande que se ajuste a nuestro número de trabajo y nos lo devuelve. Dibujamos una línea desde long_division a find_largest_fit con otro cuadro para indicar el alcance de la llamada a la función. Observe cómo termina el cuadro cuando se devuelve el multiplicador; ¡este es el final del alcance de las funciones!

ingrese la descripción de la imagen aquí

Repita varias veces para obtener un número mayor y su gráfico debería verse así:

enter descripción de la imagen aquí

Notas

Puede elegir si desea etiquetar las llamadas con los nombres de variable pasados, o sus valores si solo desea documentar un caso específico. También puede mostrar la recursividad con una función que se llama a sí misma.

Además, puede mostrar a los usuarios aquí y pedirles que muestren su entrada en el sistema con bastante facilidad. ¡Es un sistema bastante flexible que creo que encontrará bastante útil!

Comentarios

  • Gracias, conozco el diagrama de secuencia, pero me parece que es más adecuado para oop. En mi caso, las cosas son un poco más complicadas, lo que significa que, por ejemplo, tengo alrededor de 20 funciones / ayudantes repartidos en varios módulos. ¿Cómo especificaría el módulo al que pertenece la función? Teniendo en cuenta que algunas funciones también se renombran durante las importaciones ..
  • Yo diría que no importa cuántos módulos tenga, el ejemplo anterior tampoco es correcto. Simplemente nómbrelos para que pueda encontrarlos más tarde, ModuleA / function1, ModuleB / Function2, etc. Para 20 funciones, será más grande, pero definitivamente no es imposible de entender. Otra idea que puede hacer es terminar la línea de una función después de su último uso y poner otra línea de funciones debajo para ahorrar espacio horizontal en su diagrama.

Respuesta

Creo que un gráfico de llamadas sería la visualización más apropiada.Si decide no hacerlo a mano, hay una pequeña y agradable herramienta llamada pyan que realiza un análisis estático en un archivo de Python y puede generar un gráfico de llamadas visualizado por medio de un punto Graphviz. (que se puede renderizar en una imagen). Ha habido un par de bifurcaciones, pero la más completa parece ser https://github.com/davidfraser/pyan .

Solo necesita especificar todos los archivos que desea procesar cuando ejecute el comando:

python ~/bin/pyan.py --dot a.py b.py c.py -n > pyan.dot; dot -Tpng -opyan.png pyan.dot

o

python ~/bin/pyan.py --dot $(find . -name "*.py") -n > pyan.dot; dot -Tpng -opyan.png pyan.dot

Puede hacer que el gráfico sea más limpio con «-n» que elimina el líneas que muestran dónde se definió una función.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *