13
13
dot_str_body = ""
14
14
dot_str_end = "}"
15
15
16
+
16
17
class Visualiser (object ):
17
18
# Total number of nodes
18
19
node_count = 0
@@ -22,8 +23,9 @@ class Visualiser(object):
22
23
edges = []
23
24
nodes = []
24
25
25
- def __init__ (self , ignore_args = None , show_argument_name = True , show_return_value = True , node_properties_kwargs = {}):
26
- #If enabled shows keyword arguments ordered by keys
26
+ def __init__ (self , ignore_args = None , show_argument_name = True ,
27
+ show_return_value = True , node_properties_kwargs = {}):
28
+ # If enabled shows keyword arguments ordered by keys
27
29
self .show_argument_name = show_argument_name
28
30
# If enables shows the return value at every nodes
29
31
self .show_return_value = show_return_value
@@ -36,8 +38,6 @@ def __init__(self, ignore_args=None, show_argument_name=True, show_return_value=
36
38
else :
37
39
self .ignore_args = ['node_num' ] + ignore_args
38
40
39
-
40
-
41
41
@classmethod
42
42
def write_image (cls , filename = "out.png" ):
43
43
try :
@@ -78,8 +78,15 @@ def make_frames(cls):
78
78
@classmethod
79
79
def write_gif (cls , name = "out.gif" , delay = 3 ):
80
80
images = []
81
- imgs = glob .glob ("frames/*.png" )
82
- for filename in sorted (imgs , key = lambda fn : int (fn .split ("_" )[1 ].split ("." )[0 ])):
81
+
82
+ # sort frames images in ascending order to number in image filename
83
+ # image filename: frames/temp_1.png
84
+ sorted_images = sorted (
85
+ glob .glob ("frames/*.png" ),
86
+ key = lambda fn : int (fn .split ("_" )[1 ].split ("." )[0 ])
87
+ )
88
+
89
+ for filename in sorted_images :
83
90
images .append (imageio .imread (filename ))
84
91
print ("Writing gif..." )
85
92
imageio .mimsave (name , images , duration = delay )
@@ -107,114 +114,138 @@ def make_animation(cls, filename="out.gif", delay=3):
107
114
except :
108
115
print ("Error saving gif." )
109
116
110
-
111
- def extract_signature_label_arg_string (self , * args , ** kwargs ):
117
+ def extract_arg_strings (self , * args , ** kwargs ):
112
118
"""
113
- Returns function signature arguments as string and function label arguments as string.
114
- label_argument string contains only the arguments that are not in ignore_args
115
- signature_argument_string contains all the arguments available for the function
119
+ Returns function signature arguments function label arguments as
120
+ string.
121
+ label_args_string contains only the arguments that are not in
122
+ ignore_args.
123
+ signature_args_string contains all the arguments available for the
124
+ function.
116
125
"""
117
- # If show_argument flag is True(default)
118
- # Then argument_string is:
119
- # a=1, b=31, c=0
120
- signature_argument_string = ', ' .join ([repr (a ) for a in args ] +
121
- [f"{ key } ={ repr (value )} " for key , value in kwargs .items ()])
122
-
123
- label_argument_string = ', ' .join ([repr (a ) for a in args ] +
124
- [f"{ key } ={ repr (value )} "
125
- for key , value in kwargs .items ()
126
- if key not in self .ignore_args ])
127
-
128
- # If show_argument flag is False
129
- # Then argument_string is:
130
- # 1, 31, 0
131
- if not self .show_argument_name :
132
- signature_argument_string = ', ' .join ([repr (value ) for value in args ] +
133
- [f"{ repr (value )} " for key , value in kwargs .items ()])
134
-
135
- label_argument_string = ', ' .join ([repr (a ) for a in args ] +
136
- [f"{ repr (value )} "
137
- for key , value in kwargs .items ()
138
- if key not in self .ignore_args ])
139
-
140
- return signature_argument_string , label_argument_string
126
+
127
+ def get_kwargs_strings (ignore_args = []):
128
+ """Returns list of kwargs in string format from given kwargs items
129
+
130
+ Args:
131
+ ignore_args (list, optional) : list of ignored arguments.
132
+ Default to [].
133
+
134
+ Returns:
135
+ strings_list: list of kwargs in string format
136
+ """
137
+
138
+ strings_list = []
139
+
140
+ for key , value in kwargs .items ():
141
+ if key not in ignore_args :
142
+ if not self .show_argument_name :
143
+ strings_list .append (f"{ repr (value )} " )
144
+ else :
145
+ strings_list .append (f"{ key } ={ repr (value )} " )
146
+
147
+ return strings_list
148
+
149
+ args_string = [repr (a ) for a in args ]
150
+ signature_kwargs_string = get_kwargs_strings ()
151
+ label_kwargs_string = get_kwargs_strings (ignore_args = self .ignore_args )
152
+
153
+ signature_args_string = ', ' .join (
154
+ args_string + signature_kwargs_string )
155
+ label_args_string = ', ' .join (args_string + label_kwargs_string )
156
+
157
+ return signature_args_string , label_args_string
141
158
142
159
def __call__ (self , fn ):
143
- @wraps (fn )
160
+ @ wraps (fn )
144
161
def wrapper (* args , ** kwargs ):
145
162
global dot_str_body
146
163
# Increment total number of nodes when a call is made
147
164
self .node_count += 1
148
165
149
- # Update kwargs by adding dummy keyword node_num which helps to uniquely identify each node
166
+ # Update kwargs by adding dummy keyword node_num which helps to
167
+ # uniquely identify each node
150
168
kwargs .update ({'node_num' : self .node_count })
151
169
# Order all the keyword arguments
152
170
kwargs = OrderedDict (sorted (kwargs .items ()))
153
171
154
-
155
172
"""Details about current Function"""
156
173
# Get signature and label arguments strings for current function
157
- current_function_argument_string , current_function_label_argument_string = self .extract_signature_label_arg_string (* args , ** kwargs )
174
+ (signature_args_string ,
175
+ label_args_string ) = self .extract_arg_strings (
176
+ * args , ** kwargs )
158
177
159
178
# Details about current function
160
- current_function_name = fn .__name__
179
+ function_name = fn .__name__
161
180
162
181
# Current function signature looks as follows:
163
182
# foo(1, 31, 0) or foo(a=1, b=31, c=0)
164
- current_function_signature = f"{ current_function_name } (" \
165
- f"{ current_function_argument_string } )"
166
- current_function_label = f"{ current_function_name } ({ current_function_label_argument_string } )"
183
+ function_signature = f"{ function_name } ({ signature_args_string } )"
184
+ function_label = f"{ function_name } ({ label_args_string } )"
167
185
""""""
168
186
169
187
"""Details about caller function"""
170
- caller_function_frame = sys ._getframe (1 )
188
+ caller_func_frame = sys ._getframe (1 )
171
189
# All the argument names in caller/parent function
172
- caller_function_argument_names = caller_function_frame .f_code .co_varnames [
173
- : fn .__code__ .co_argcount ]
174
- caller_function_locals = caller_function_frame .f_locals
190
+ caller_func_arg_names = caller_func_frame .f_code .co_varnames [
191
+ : fn .__code__ .co_argcount ]
192
+ caller_func_locals = caller_func_frame .f_locals
175
193
# Sort all the locals of caller function
176
- caller_function_locals = OrderedDict (sorted (caller_function_locals .items ()))
194
+ caller_func_locals = OrderedDict (
195
+ sorted (caller_func_locals .items ()))
196
+
197
+ caller_func_kwargs = dict ()
198
+
177
199
# Extract only those locals that are in arguments
178
- caller_function_kwargs = {key : value for key , value in caller_function_locals .items () if key in caller_function_argument_names }
200
+ for key , value in caller_func_locals .items ():
201
+ if key in caller_func_arg_names :
202
+ caller_func_kwargs [key ] = value
179
203
180
204
# If the nodes has parent node get node_num from parent node
181
205
if self .stack :
182
- caller_function_kwargs .update ({'node_num' : self .stack [- 1 ]})
183
- caller_function_kwargs = OrderedDict (sorted (caller_function_kwargs .items ()))
206
+ caller_func_kwargs .update ({'node_num' : self .stack [- 1 ]})
207
+
208
+ caller_func_kwargs = OrderedDict (
209
+ sorted (caller_func_kwargs .items ()))
184
210
185
- caller_function_argument_string , caller_function_label_argument_string = self .extract_signature_label_arg_string (** caller_function_kwargs )
211
+ (caller_func_args_string ,
212
+ caller_func_label_args_string ) = self .extract_arg_strings (
213
+ ** caller_func_kwargs )
186
214
187
215
# Caller Function
188
- caller_function_name = caller_function_frame .f_code .co_name
216
+ caller_func_name = caller_func_frame .f_code .co_name
189
217
190
218
# Extract the names of arguments only
191
- caller_func_signature = f"{ caller_function_name } ({ caller_function_argument_string } )"
192
- caller_func_label = f"{ caller_function_name } ({ caller_function_label_argument_string } )"
219
+ caller_func_signature = "{}({})" .format (
220
+ caller_func_name , caller_func_args_string )
221
+ caller_func_label = "{}({})" .format (
222
+ caller_func_name , caller_func_label_args_string )
193
223
""""""
194
224
195
- if caller_function_name == '<module>' :
196
- print (f"Drawing for { current_function_signature } " )
225
+ if caller_func_name == '<module>' :
226
+ print (f"Drawing for { function_signature } " )
197
227
198
228
# Push node_count to stack
199
229
self .stack .append (self .node_count )
200
230
# Before actual function call delete keyword 'node_num' from kwargs
201
231
del kwargs ['node_num' ]
202
232
203
- self .edges .append (f'"{ caller_func_signature } " -> "{ current_function_signature } "' )
233
+ self .edges .append (
234
+ f'"{ caller_func_signature } " -> "{ function_signature } "' )
204
235
205
236
# Construct node string to be rendered in graphviz
206
- node_string = f'"{ current_function_signature } " [label="{ current_function_label } "'
237
+ node_string = f'"{ function_signature } " [label="{ function_label } "'
207
238
208
239
if self .node_properties_kwargs :
209
- node_string += ", " + ", " .join ([f'{ key } ="{ value } "' for key , value in self .node_properties_kwargs .items ()])
240
+ node_string += ", " + \
241
+ ", " .join ([f'{ key } ="{ value } "' for key ,
242
+ value in self .node_properties_kwargs .items ()])
210
243
211
- # current_function_label = current_function_label + ", ".join([f"{key}={value}" for key, value in self.node_properties_kwargs.items()])
212
244
self .nodes .append (node_string )
213
245
214
246
# Return after function call
215
247
result = fn (* args , ** kwargs )
216
248
217
-
218
249
# Pop from tha stack after returning
219
250
self .stack .pop ()
220
251
@@ -223,20 +254,24 @@ def wrapper(*args, **kwargs):
223
254
# If shape is set to record
224
255
# Then separate function label and return value by a row
225
256
if "record" in self .node_properties_kwargs .values ():
226
- current_function_label = "{" + current_function_label + f"|{ result } }}"
257
+ function_label = "{" + \
258
+ function_label + f"|{ result } }}"
227
259
else :
228
- current_function_label += f"\n => { result } "
260
+ function_label += f"\n => { result } "
229
261
230
- child_node = pydot .Node (name = current_function_signature , label = current_function_label , ** self .node_properties_kwargs )
262
+ child_node = pydot .Node (name = function_signature ,
263
+ label = function_label ,
264
+ ** self .node_properties_kwargs )
231
265
self .graph .add_node (child_node )
232
266
233
267
# If the function is called by another function
234
- if caller_function_name not in ['<module>' , 'main' ]:
235
- parent_node = pydot .Node (name = caller_func_signature , label = caller_func_label , ** self .node_properties_kwargs )
268
+ if caller_func_name not in ['<module>' , 'main' ]:
269
+ parent_node = pydot .Node (name = caller_func_signature ,
270
+ label = caller_func_label ,
271
+ ** self .node_properties_kwargs )
236
272
self .graph .add_node (parent_node )
237
273
edge = pydot .Edge (parent_node , child_node )
238
274
self .graph .add_edge (edge )
239
275
240
-
241
276
return result
242
- return wrapper
277
+ return wrapper
0 commit comments