Python – Draw a Call Graph

diagramsflowchartpython

I'm maintaining an old code base written in python. In particular there is a complex piece of code that from a module calls other functions from other modules that call other functions and so on. It is not OOP, just functions and modules.
I've tried to keep track where the flow begins and ends anytime I call the main function but I feel I need to draw this because I'm getting lost in the sub-calls.

What concerns me is that each function calls multiple external functions within their body to complete their task and return the value to the caller.

How can I draw this? Meaning what kind of chart/graphic would be appropriate to document this kind of behavior/code?

So, I don't think would be useful to draw an UML diagram, neither a flowchart.
A call graph, maybe?

Best Answer

I think what you're looking for here is a Sequence Diagram. These allow you to visualize the order in which various modules call eachother via the use of arrows.

Constructing one is simple:

  1. Draw your starting class with a dotted line below it.
  2. Draw the next class/method in the call trace with a dotted line below that
  3. Connect the lines with an arrow, vertically positioned below the last arrow you drew
  4. Repeat steps 2-3 for all calls in your trace

Example

Let's assume we have the following code we want to create a sequence diagram for:

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

The first thing we'll draw is the entry point (main) connecting to the method long_division. Note that this creates a box in long_division, signifying the scope of the method call. For this simple example, the box will be the entire height of our sequence diagram due to the fact that this is the only thing run.

enter image description here

Now we call find_largest_fit to find the largest multiple that fits within our working number, and returns it to us. We draw a line from long_division to find_largest_fit with another box to signify scope for the function call. Note how the box ends when the multiplier is returned; this is the end of that functions scope!

enter image description here

Repeat a few times for a larger number and your chart should look something like this:

enter image description here

Notes

You can choose whether you want to label the calls with the variable names passed, or their values if you only want to document one specific case. You can also show recursion with a function calling itself.

Additionally, you can show users in here and prompt them and show their input into the system easily enough. It's a fairly flexible system that I think you'll find rather useful!

Related Topic