Нарисуйте график звонков

12

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

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

Как я могу это нарисовать? Имеется в виду, какой тип диаграммы / графики будет подходящим для документирования такого поведения / кода?

Поэтому я не думаю, что было бы полезно рисовать диаграмму UML, а также блок-схему. График вызовов, может быть?

Leonardo
источник
doxygen - будет генерировать графы вызовов / вызовов, я не уверен, насколько сильно он поддерживает Python. Я знаю, что вы можете документировать код Python для него.
gbjbaanb
Я пробовал pycallgraph, но он слишком сложный / слишком глубокий, чтобы его использовать. Это связано со сложностью моего кода, потому что он смешивает простой python с django и внешним вызовом API url. Вот почему я хотел нарисовать его от руки только с учетом необходимой части. Проблема в том, что я не знаю, какой график использовать, чтобы иметь полное представление о системе
Леонардо
5
Если это просто, чтобы помочь вам понять это, просто нарисуйте все, что приходит естественно. Вы всегда можете привести это в порядок позже, если это входит в формальную документацию.
Джоншарп

Ответы:

9

Я думаю, что вы ищете здесь диаграмму последовательности . Они позволяют визуализировать порядок, в котором различные модули вызывают друг друга с помощью стрелок.

Построить одно просто:

  1. Нарисуйте свой начальный класс с пунктирной линией под ним.
  2. Нарисуйте следующий класс / метод в трассировке вызова с пунктирной линией ниже
  3. Соедините линии стрелкой, расположенной вертикально под последней стрелкой, которую вы нарисовали.
  4. Повторите шаги 2-3 для всех вызовов в вашей трассировке

пример

Предположим, у нас есть следующий код, для которого мы хотим создать диаграмму последовательности:

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

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

введите описание изображения здесь

Теперь мы позвоним, find_largest_fitчтобы найти наибольшее число, которое соответствует нашему рабочему номеру, и вернем его нам. Мы рисуем линию от long_divisionк find_largest_fitс другим полем, чтобы указать область для вызова функции. Обратите внимание, как окно заканчивается, когда возвращается множитель; это конец этой функции!

введите описание изображения здесь

Повторите несколько раз для большего числа, и ваш график должен выглядеть примерно так:

введите описание изображения здесь

Заметки

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

Кроме того, вы можете показывать здесь пользователей, предлагать им и достаточно легко показывать их ввод в систему. Это довольно гибкая система, которую, я думаю, вы найдете довольно полезной!

Ampt
источник
Спасибо, я знаю диаграмму последовательности, но мне кажется, что она больше подходит для упс. В моем случае все немного более грязно, например, у меня около 20 функций / помощников, распределенных по нескольким модулям. Как бы я указал модуль, которому принадлежит функция? Учитывая, что некоторые функции также переименовываются во время импорта ..
Леонардо
1
Я бы сказал, что не имеет значения, сколько у вас модулей - приведенный выше пример тоже не работает. Просто назовите их так, чтобы вы могли найти их позже, ModuleA / function1, ModuleB / Function2 и т. Д. Для 20 функций это будет больше, но определенно не невозможно понять. Еще одна мысль, которую вы можете сделать, это завершить строку функции после ее последнего использования и поместить другую строку функции под ней, чтобы сохранить горизонтальное пространство на диаграмме.
День
6

Я думаю, что граф вызовов будет наиболее подходящей визуализацией. Если вы решите не делать это вручную, есть хороший небольшой инструмент, pyanкоторый выполняет статический анализ файла python и может генерировать визуализированный граф вызовов с помощью файла graphviz dot (который может быть визуализирован в изображение). Было несколько разветвлений, но наиболее полнофункциональный - https://github.com/davidfraser/pyan .

Вам просто нужно указать все файлы, которые вы хотите обработать при запуске команды:

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

или же

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

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

Серен
источник