Narysuj wykres połączeń

12

Utrzymuję starą bazę kodu napisaną w języku python. W szczególności istnieje złożony fragment kodu, który z modułu wywołuje inne funkcje z innych modułów, które wywołują inne funkcje i tak dalej. To nie jest OOP, tylko funkcje i moduły.
Próbowałem śledzić, gdzie zaczyna się i kończy przepływ za każdym razem, gdy wywołuję główną funkcję, ale czuję, że muszę to narysować, ponieważ gubię się w wywołaniach podrzędnych.

To, co mnie martwi, to to, że każda funkcja wywołuje wiele funkcji zewnętrznych w swoim ciele, aby zakończyć zadanie i zwrócić wartość wywołującemu.

Jak mogę to narysować? Czyli jaki rodzaj wykresu / grafiki byłby odpowiedni do udokumentowania tego rodzaju zachowania / kodu?

Nie sądzę więc, aby rysowanie diagramu UML, ani schematu blokowego było przydatne. Może wykres połączeń?

Leonardo
źródło
doxygen - wygeneruje wykresy wywołań / wywołań, nie jestem pewien, jakie wsparcie ma dla Pythona. Wiem, że możesz udokumentować dla niego kod Pythona.
gbjbaanb
Próbowałem pycallgraph, ale jest to zbyt skomplikowane / zbyt głębokie, aby go użyć. Wynika to ze złożoności mojego kodu, ponieważ łączy on zwykły python z django i zewnętrznym wywołaniem adresu URL API. Dlatego chciałem to narysować ręcznie, biorąc pod uwagę tylko odpowiednią część, której potrzebuję. Problem polega na tym, że nie wiem, jakiego rodzaju wykresu użyć, aby w pełni zrozumieć system
Leonardo
5
Jeśli ma to tylko pomóc ci to zrozumieć, po prostu narysuj wszystko, co przychodzi naturalnie. Zawsze możesz to uporządkować później, jeśli przejdzie do formalnej dokumentacji.
jonrsharpe

Odpowiedzi:

9

Myślę, że tutaj szukasz diagramu sekwencji . Umożliwiają one wizualizację kolejności, w której różne moduły wywołują się nawzajem za pomocą strzałek.

Konstruowanie jednego jest proste:

  1. Narysuj klasę początkową z kropkowaną linią poniżej.
  2. Narysuj następną klasę / metodę w ścieżce wywołania, pod nią kropkowaną linię
  3. Połącz linie ze strzałką ustawioną pionowo poniżej ostatniej narysowanej strzałki
  4. Powtórz kroki 2-3 dla wszystkich połączeń w swoim śladzie

Przykład

Załóżmy, że mamy następujący kod, dla którego chcemy utworzyć diagram sekwencji:

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

Najpierw narysujemy punkt wejścia ( main) łączący się z metodą long_division. Zauważ, że tworzy to pole w long_division, oznaczające zakres wywołania metody. W tym prostym przykładzie ramka będzie miała całą wysokość naszego schematu sekwencji, ponieważ jest to jedyna rzecz, którą można uruchomić.

wprowadź opis zdjęcia tutaj

Teraz dzwonimy, find_largest_fitaby znaleźć największą wielokrotność, która mieści się w naszym numerze roboczym i zwraca go nam. Rysujemy linię od long_divisiondo find_largest_fitz innym polem, aby zaznaczyć zakres wywołania funkcji. Zwróć uwagę, jak pole kończy się po zwróceniu mnożnika; to koniec zakresu funkcji!

wprowadź opis zdjęcia tutaj

Powtórz kilka razy dla większej liczby, a twój wykres powinien wyglądać mniej więcej tak:

wprowadź opis zdjęcia tutaj

Notatki

Możesz wybrać, czy chcesz oznaczać połączenia przekazanymi nazwami zmiennych, czy ich wartości, jeśli chcesz udokumentować tylko jeden konkretny przypadek. Możesz także wyświetlić rekurencję z funkcją wywołującą samą siebie.

Ponadto możesz tutaj wyświetlać użytkowników, monitować ich i dość łatwo wprowadzać dane do systemu. To dość elastyczny system, który wydaje mi się przydatny!

Ampt
źródło
Dzięki, znam schemat sekwencji, ale wydaje mi się, że jest bardziej odpowiedni dla oop. W moim przypadku sprawy są nieco bardziej niechlujne, co oznacza, że ​​na przykład mam około 20 funkcji / pomocników rozmieszczonych w wielu modułach. Czy powinienem określić moduł, do którego należy funkcja? Biorąc pod uwagę, że niektóre funkcje są również zmieniane podczas importu.
Leonardo
1
Powiedziałbym, że nie ma znaczenia, ile masz modułów - powyższy przykład w ogóle też nie działa. Wystarczy je nazwać, aby można je było później znaleźć: moduł A / funkcja 1, moduł B / funkcja 2 itd. Dla 20 funkcji będzie on większy, ale na pewno nie niemożliwy do zrozumienia. Kolejną rzeczą, którą możesz zrobić, to zakończyć linię dla funkcji po jej ostatnim użyciu i umieścić pod nią kolejną linię funkcji, aby zaoszczędzić miejsce na diagramie.
Ampt
6

Myślę, że wykres połączeń byłby najbardziej odpowiednią wizualizacją. Jeśli zdecydujesz się nie robić tego ręcznie, istnieje ładne małe narzędzie o nazwie, pyanktóre wykonuje analizę statyczną pliku Pythona i może wygenerować wizualizowany wykres wywołania za pomocą pliku kropki graphviz (który może być renderowany na obraz). Było kilka rozwidleń, ale najbardziej w pełni funkcjonalny wydaje się być https://github.com/davidfraser/pyan .

Musisz tylko określić wszystkie pliki, które chcesz przetworzyć po uruchomieniu polecenia:

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

lub

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

Możesz uczynić wykres czystszym za pomocą „-n”, który usuwa linie pokazujące, gdzie zdefiniowano funkcję.

spokojny
źródło