Zeichnen Sie ein Anrufdiagramm

11

Ich pflege eine alte Codebasis, die in Python geschrieben ist. Insbesondere gibt es einen komplexen Code, der von einem Modul andere Funktionen von anderen Modulen aufruft, die andere Funktionen aufrufen, und so weiter. Es ist kein OOP, nur Funktionen und Module.
Ich habe versucht zu verfolgen, wo der Fluss beginnt und endet, wenn ich die Hauptfunktion aufrufe, aber ich habe das Gefühl, dass ich dies zeichnen muss, weil ich mich in den Unteraufrufen verliere.

Was mich betrifft, ist, dass jede Funktion mehrere externe Funktionen in ihrem Körper aufruft, um ihre Aufgabe abzuschließen und den Wert an den Aufrufer zurückzugeben.

Wie kann ich das zeichnen? Was bedeutet, welche Art von Diagramm / Grafik wäre geeignet, um diese Art von Verhalten / Code zu dokumentieren?

Ich denke also nicht, dass es nützlich wäre, ein UML-Diagramm oder ein Flussdiagramm zu zeichnen. Vielleicht ein Anrufdiagramm?

Leonardo
quelle
doxygen - generiert Anruf- / Anrufer-Diagramme. Ich bin mir nicht sicher, wie viel Unterstützung Python bietet. Ich weiß, dass Sie Python-Code dafür dokumentieren können.
Gbjbaanb
Ich habe Pycallgraph ausprobiert, aber es ist einfach zu kompliziert / zu tief, um es zu verwenden. Dies liegt an der Komplexität meines Codes, da er einfaches Python mit Django und externem Aufruf der API-URL mischt. Deshalb wollte ich es von Hand zeichnen, nur unter Berücksichtigung des relevanten Teils, den ich brauche. Das Problem ist, dass ich nicht weiß, welche Art von Grafik ich verwenden soll, um das System vollständig zu verstehen
Leonardo
5
Wenn dies nur dazu dient, Ihnen das Verständnis zu erleichtern, zeichnen Sie einfach, was natürlich kommt. Sie können es später jederzeit aufräumen, wenn es in die formale Dokumentation geht.
Jonrsharpe

Antworten:

9

Ich denke, was Sie hier suchen, ist ein Sequenzdiagramm . Mit diesen können Sie mithilfe von Pfeilen die Reihenfolge visualisieren, in der sich verschiedene Module gegenseitig aufrufen.

Eine zu konstruieren ist einfach:

  1. Zeichnen Sie Ihre Startklasse mit einer gepunkteten Linie darunter.
  2. Zeichnen Sie die nächste Klasse / Methode in der Aufrufverfolgung mit einer gepunkteten Linie darunter
  3. Verbinden Sie die Linien mit einem Pfeil, der vertikal unter dem zuletzt gezeichneten Pfeil positioniert ist
  4. Wiederholen Sie die Schritte 2-3 für alle Anrufe in Ihrer Ablaufverfolgung

Beispiel

Nehmen wir an, wir haben den folgenden Code, für den wir ein Sequenzdiagramm erstellen möchten:

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")

Das erste, was wir zeichnen, ist der Einstiegspunkt ( main), der eine Verbindung zur Methode herstellt long_division. Beachten Sie, dass dadurch in long_division ein Feld erstellt wird, das den Umfang des Methodenaufrufs angibt. In diesem einfachen Beispiel entspricht die Box der gesamten Höhe unseres Sequenzdiagramms, da dies die einzige Ausführung ist.

Geben Sie hier die Bildbeschreibung ein

Jetzt rufen wir find_largest_fitan, um das größte Vielfache zu finden, das in unsere Arbeitsnummer passt, und senden es uns zurück. Wir ziehen eine Linie von long_divisionbis find_largest_fitmit einem anderen Feld, um den Umfang des Funktionsaufrufs zu kennzeichnen. Beachten Sie, wie das Feld endet, wenn der Multiplikator zurückgegeben wird. Dies ist das Ende dieses Funktionsumfangs!

Geben Sie hier die Bildbeschreibung ein

Wiederholen Sie dies einige Male für eine größere Zahl, und Ihr Diagramm sollte ungefähr so ​​aussehen:

Geben Sie hier die Bildbeschreibung ein

Anmerkungen

Sie können wählen, ob Sie die Aufrufe mit den übergebenen Variablennamen oder deren Werten kennzeichnen möchten, wenn Sie nur einen bestimmten Fall dokumentieren möchten. Sie können die Rekursion auch mit einer Funktion anzeigen, die sich selbst aufruft.

Darüber hinaus können Sie hier Benutzer anzeigen und sie dazu auffordern und ihre Eingaben in das System einfach genug anzeigen. Es ist ein ziemlich flexibles System, das Sie meiner Meinung nach ziemlich nützlich finden werden!

Ampt
quelle
Danke, ich kenne das Sequenzdiagramm, aber es scheint mir besser für oop geeignet zu sein. In meinem Fall sind die Dinge etwas chaotischer, was bedeutet, dass ich zum Beispiel ungefähr 20 Funktionen / Helfer habe, die auf mehrere Module verteilt sind. Wie würde ich das Modul angeben, zu dem die Funktion gehört? In Anbetracht dessen, dass einige Funktionen auch während des Imports umbenannt werden.
Leonardo
1
Ich würde sagen, dass es keine Rolle spielt, wie viele Module Sie haben - das obige Beispiel ist auch überhaupt nicht in Ordnung. Benennen Sie sie einfach, damit Sie sie später finden können: ModulA / Funktion1, ModulB / Funktion2 usw. Für 20 Funktionen wird es größer, aber definitiv nicht unmöglich zu verstehen. Sie können auch die Zeile für eine Funktion nach ihrer letzten Verwendung beenden und eine weitere Funktionszeile darunter einfügen, um horizontalen Platz in Ihrem Diagramm zu sparen.
Ampt
5

Ich denke, ein Anrufdiagramm wäre die am besten geeignete Visualisierung. Wenn Sie sich dafür entscheiden, dies nicht von Hand zu tun, gibt es ein nettes kleines Tool namens pyan, das statische Analysen für eine Python-Datei durchführt und mithilfe einer graphviz-Punktdatei (die in ein Bild gerendert werden kann) ein visualisiertes Aufrufdiagramm erstellen kann. Es gab ein paar Gabeln, aber die mit den meisten Funktionen scheint https://github.com/davidfraser/pyan zu sein .

Sie müssen nur alle Dateien angeben, die verarbeitet werden sollen, wenn Sie den Befehl ausführen:

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

oder

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

Sie können das Diagramm mit dem '-n' übersichtlicher gestalten, wodurch die Linien entfernt werden, die zeigen, wo eine Funktion definiert wurde.

ruhig
quelle