|
1 | | -import inspect |
2 | | - |
3 | 1 | import sys |
4 | 2 | from functools import wraps |
5 | 3 | from collections import OrderedDict |
6 | 4 | import pydot |
7 | 5 |
|
8 | | - |
9 | 6 | class Visualiser(object): |
10 | 7 | node_count = 0 |
11 | 8 | graph = pydot.Dot(graph_type="digraph") |
12 | | - def __init__(self, ignore_args): |
| 9 | + def __init__(self, ignore_args, show_argument_name=True): |
| 10 | + self.show_argument_name = show_argument_name |
13 | 11 | self.ignore_args = ignore_args |
14 | 12 |
|
15 | 13 | @classmethod |
16 | 14 | def write_image(self, filename="out"): |
17 | 15 | self.graph.write_png(f"{filename}.png") |
18 | 16 |
|
19 | | - |
20 | | - |
21 | 17 | def __call__(self, fn): |
22 | 18 | @wraps(fn) |
23 | 19 | def wrapper(*args, **kwargs): |
24 | | - |
| 20 | + # Order all the keyword arguments |
25 | 21 | kwargs = OrderedDict(sorted(kwargs.items())) |
26 | | - argstr = ', '.join( |
| 22 | + |
| 23 | + # If show_argument flag is True(default) |
| 24 | + # Then argument_string is: |
| 25 | + # a=1, b=31, c=0 |
| 26 | + argument_string = ', '.join( |
27 | 27 | [repr(a) for a in args] + |
28 | | - ["%s=%s" % (a, repr(b)) for a, b in kwargs.items()]) |
| 28 | + [f"{key}={repr(value)}" for key, value in kwargs.items()]) |
29 | 29 |
|
30 | | - # Current Function |
31 | | - current_func_name = fn.__name__ |
32 | | - current_func_args = argstr |
33 | | - current_func_signature = f"{current_func_name}({current_func_args})" |
| 30 | + current_function_label_argument_string = ', '.join( |
| 31 | + [repr(a) for a in args] + |
| 32 | + [f"{key}={repr(value)}" for key, value in kwargs.items() if key not in self.ignore_args]) |
| 33 | + |
| 34 | + # If show_argument flag is False |
| 35 | + # Then argument_string is: |
| 36 | + # 1, 31, 0 |
| 37 | + if self.show_argument_name == False: |
| 38 | + argument_string = ', '.join( |
| 39 | + [repr(value) for value in args] + |
| 40 | + [f"{repr(value)}" for key, value in kwargs.items()]) |
| 41 | + |
| 42 | + current_function_label_argument_string = ', '.join( |
| 43 | + [repr(a) for a in args] + |
| 44 | + [f"{repr(value)}" for key, value in kwargs.items() if key not in self.ignore_args]) |
| 45 | + |
| 46 | + # Details about current function |
| 47 | + current_function_name = fn.__name__ |
| 48 | + current_function_argument_string = argument_string |
| 49 | + # Current function signature looks as follows: |
| 50 | + # foo(1, 31, 0) or foo(a=1, b=31, c=0) |
| 51 | + current_function_signature = f"{current_function_name}({current_function_argument_string})" |
| 52 | + current_function_label = f"{current_function_name}({current_function_label_argument_string})" |
34 | 53 |
|
35 | 54 | # Caller Function |
36 | | - caller_func_name = sys._getframe(1).f_code.co_name |
37 | | - caller_func_arg_names = sys._getframe(1).f_code.co_varnames[:fn.__code__.co_argcount] |
| 55 | + caller_function_name = sys._getframe(1).f_code.co_name |
| 56 | + # Extract the names of arguments only |
| 57 | + caller_function_argument_names = sys._getframe(1).f_code.co_varnames[:fn.__code__.co_argcount] |
| 58 | + caller_function_locals = sys._getframe(1).f_locals |
38 | 59 |
|
39 | | - caller_func_locals = sys._getframe(1).f_locals |
40 | | - caller_func_locals = OrderedDict(sorted(caller_func_locals.items())) |
| 60 | + # Sort all the locals of caller function |
| 61 | + caller_function_locals = OrderedDict(sorted(caller_function_locals.items())) |
41 | 62 |
|
42 | | - caller_func_args = ', '.join( |
43 | | - [f"{key}={value}" for key, value in caller_func_locals.items() if (key in caller_func_arg_names)]) |
44 | | - caller_func_signature = f"{caller_func_name}({caller_func_args})" |
45 | 63 |
|
46 | | - caller_func_args = ', '.join( |
47 | | - [f"{key}={value}" for key, value in caller_func_locals.items() if |
48 | | - (key in caller_func_arg_names and key not in self.ignore_args)]) |
49 | | - caller_func_node_label = f"{caller_func_name}({caller_func_args})" |
| 64 | + # Extract only those locals which are in function signature |
| 65 | + caller_function_argument_string = ', '.join( |
| 66 | + [f"{key}={value}" for key, value in caller_function_locals.items() if (key in caller_function_argument_names)]) |
| 67 | + caller_function_label_argument_string = ', '.join( |
| 68 | + [f"{key}={value}" for key, value in caller_function_locals.items() if |
| 69 | + (key in caller_function_argument_names and key not in self.ignore_args)]) |
50 | 70 |
|
51 | | - if caller_func_name == '<module>': |
52 | | - print(f"Drawing for {current_func_signature}") |
| 71 | + if self.show_argument_name == False: |
| 72 | + caller_function_argument_string = ', '.join( |
| 73 | + [f"{value}" for key, value in caller_function_locals.items() if |
| 74 | + (key in caller_function_argument_names)]) |
| 75 | + caller_function_label_argument_string = ', '.join( |
| 76 | + [f"{value}" for key, value in caller_function_locals.items() if |
| 77 | + (key in caller_function_argument_names and key not in self.ignore_args)]) |
53 | 78 |
|
54 | | - result = fn(*args, **kwargs) |
| 79 | + caller_func_signature = f"{caller_function_name}({caller_function_argument_string})" |
| 80 | + caller_func_label = f"{caller_function_name}({caller_function_label_argument_string})" |
55 | 81 |
|
56 | | - argstr = ', '.join( |
57 | | - [repr(a) for a in args] + |
58 | | - ["%s=%s" % (a, repr(b)) for a, b in kwargs.items() if a not in self.ignore_args]) |
59 | | - current_node_label = f"{current_func_name}({argstr})" |
| 82 | + if caller_function_name == '<module>': |
| 83 | + print(f"Drawing for {current_function_signature}") |
60 | 84 |
|
61 | | - # # Child Node |
62 | | - child_label = current_node_label |
63 | | - child_name = current_func_signature |
| 85 | + result = fn(*args, **kwargs) |
| 86 | + |
| 87 | + # #Child Node |
| 88 | + child_label = current_function_label |
| 89 | + child_name = current_function_signature |
64 | 90 | v = pydot.Node(name=child_name, label=child_label) |
65 | 91 | self.graph.add_node(v) |
66 | 92 |
|
67 | 93 | # Parent Node |
68 | 94 | u = None |
69 | 95 |
|
70 | | - if caller_func_name != '<module>': |
71 | | - print(f"Called {current_func_signature} by {caller_func_signature}") |
72 | | - u = pydot.Node(name=caller_func_signature, label=caller_func_node_label) |
| 96 | + if caller_function_name != '<module>': |
| 97 | + print(f"Called {current_function_signature} by {caller_func_signature}") |
| 98 | + u = pydot.Node(name=caller_func_signature, label=caller_func_label) |
73 | 99 | self.graph.add_node(u) |
74 | 100 | edge = pydot.Edge(u, v) |
75 | 101 | self.graph.add_edge(edge) |
76 | 102 |
|
77 | 103 | return result |
78 | 104 | return wrapper |
79 | | - |
80 | | - |
81 | | - |
82 | | - |
83 | | -@Visualiser(ignore_args=['node_num']) |
84 | | -def fact(n, node_num): |
85 | | - if n == 1: |
86 | | - Visualiser.node_count += 1 |
87 | | - return 1 |
88 | | - |
89 | | - Visualiser.node_count += 1 |
90 | | - return n * fact(n=n - 1, node_num=Visualiser.node_count) |
91 | | - |
|
0 commit comments