Veliko
Veliko

Reputation: 817

Is there a way to visualize the call relationships in a set of python functions?

Imagine you have a folder with many python modules and each of them contains functions. I would like to visualize a graph with vertices = function names. I want to define the edges as follows: if function F1 calls F2 then the vertex of F1 is connected to F2 with a directed edge.

Minimal example with a single module:

# --- module.py ---

def F1():
    pass

def F2():
    F1()

def F3():
    F1()

def F4():
    F2()

The desired output would be a graph with vertices: V = {F1, F2, F3, F4} and edges E = {F2->F1, F3->F1, F4->F2}.

Is there a convenient library to do this? Otherwise I imagine I can covert the module to string, identify the functions and find internal calls with some regex expressions.

Thanks

Upvotes: 2

Views: 1109

Answers (1)

python_user
python_user

Reputation: 7073

Not exactly sure if it's a 100% working model but it can hopefully be of some starting point. The idea behind this is to use a decorator that is going to update a dictionary graph with values based on inspect.currentframe and inspect.getouterframes. This answer (explains how to get the name of the caller) was modified slightly to get the results.

The entry point is called '<module>' so you can probably discard that from your graph if you only care about functions calling each other.

import inspect
from collections import defaultdict

graph = defaultdict(set)

def called(fun):
    def inner(*args, **kwargs):
        cur_frame = inspect.currentframe()
        cal_frame = inspect.getouterframes(cur_frame)
        graph[cal_frame[1].function].add(fun.__name__)
        return fun(*args, **kwargs)
    return inner


@called
def f1():
    pass

@called
def f2():
    f1()
    f3()

@called
def f3():
    f1()

@called
def f4():
    f2()

v = {f1, f2, f3, f4}
f2()

print(graph)

Output

{'<module>': {'f2'}, 'f2': {'f1', 'f3'}, 'f3': {'f1'}}

Upvotes: 1

Related Questions