4 generates call graph of given python code file
5 in dot format input for graphviz.
8 * statically tried to figure out functions calls
9 * does not understand classes
10 * algorithm is naive and may not statically find
21 except: import sets; set = sets.Set
23 def annotate_ast_list(ast_list):
25 if code in symbol.sym_name: code = symbol.sym_name[code]
26 else: code = token.tok_name[code]
29 for index, item in enumerate(ast_list):
30 if index == 0: continue
31 if isinstance(item, list):
32 ast_list[index] = annotate_ast_list(item)
35 def get_atom_name(atom):
37 first_child_code = first_child[0]
38 if first_child_code != token.NAME: return None
41 def get_fn_call_data(ast_list):
42 if len(ast_list) < 3: return None
43 first_child, second_child = ast_list[1:3]
44 first_child_code = first_child[0]
45 if first_child_code != symbol.atom: return None
46 fn_name = get_atom_name(first_child)
48 second_child_code = second_child[0]
49 if second_child_code != symbol.trailer: return None
51 if len(second_child) < 3: return None
52 if second_child[1][0] == token.LPAR and second_child[-1][0] == token.RPAR:
56 def find_fn_call(ast_list, calls):
58 if code == symbol.power:
59 fn_name = get_fn_call_data(ast_list)
60 if fn_name != None and getattr(__builtins__, fn_name, None) == None: calls.add(fn_name)
62 for item in ast_list[1:]:
63 if isinstance(item, list):
64 find_fn_call(item, calls)
66 def process_fn(fn_ast_list, call_graph):
67 dummy, dummy, func_name = fn_ast_list[:3]
68 dummy, func_name = func_name
71 find_fn_call(fn_ast_list, calls)
73 call_graph[func_name] = list(calls)
75 def construct_call_graph(ast_list, call_graph):
77 if code == symbol.funcdef:
78 process_fn(ast_list, call_graph)
80 for item in ast_list[1:]:
81 if isinstance(item, list):
82 construct_call_graph(item, call_graph)
86 def generate_dot_code(python_code):
87 ast = parser.suite(python_code)
88 ast_list = parser.ast2list(ast)
89 #annotated_ast_list = annotate_ast_list(ast_list)
90 #pprint.pprint(annotated_ast_list)
93 construct_call_graph(ast_list, call_graph)
94 #pprint.pprint(call_graph)
98 dot.append("digraph G {")
99 dot.append("rankdir=LR")
100 for from_fn, to_fns in call_graph.iteritems():
102 dot.append('%s;' % from_fn)
105 if to_fn not in call_graph: continue
106 dot.append('%s -> %s;' % (from_fn, to_fn))
109 return '\n'.join(dot)
111 if __name__ == '__main__':
112 oparser = optparse.OptionParser()
114 oparser.add_option('-i', '--input-file', default=None, metavar='FILE', help='python code file to process')
116 options, args = oparser.parse_args()
118 if options.input_file:
119 python_code = open(options.input_file).read()
121 python_code = sys.stdin.read()
123 dot_code = generate_dot_code(python_code)