@@ -142,7 +142,7 @@ def print_wf(dic_wf: dict, wf: str):
142142 print_wf (dic_wf , wf )
143143
144144
145- def get_table_producers (table : str , dic_wf_all : dict , case_sensitive = False ):
145+ def get_table_producers (table : str , dic_wf_all : dict , case_sensitive = False , reverse = False ):
146146 """Find all workflows that have this table as output."""
147147 list_producers = []
148148 if not case_sensitive :
@@ -151,7 +151,7 @@ def get_table_producers(table: str, dic_wf_all: dict, case_sensitive=False):
151151 for wf , dic_wf in dic_wf_all .items ():
152152 # Loop over devices
153153 for dev in dic_wf :
154- outputs = [o if case_sensitive else o .lower () for o in dic_wf [dev ]["outputs" ]]
154+ outputs = [o if case_sensitive else o .lower () for o in dic_wf [dev ]["inputs" if reverse else " outputs" ]]
155155 if table in outputs :
156156 list_producers .append (wf )
157157 return list (dict .fromkeys (list_producers )) # Remove duplicities
@@ -179,12 +179,7 @@ def get_workflow_inputs(wf: str, dic_wf_all: dict):
179179
180180
181181def get_tree_for_workflow (
182- wf : str ,
183- dic_wf_all : dict ,
184- dic_wf_tree = None ,
185- case_sensitive = False ,
186- level = 0 ,
187- levels_max = 0 ,
182+ wf : str , dic_wf_all : dict , dic_wf_tree = None , case_sensitive = False , level = 0 , levels_max = 0 , reverse = False
188183):
189184 """Get the dependency tree of tables and workflows needed to run this workflow."""
190185 # print(level, levels_max)
@@ -194,40 +189,47 @@ def get_tree_for_workflow(
194189 msg_fatal (f"Workflow { wf } not found" )
195190 if wf not in dic_wf_tree :
196191 dic_wf_tree [wf ] = dic_wf_all [wf ]
197- inputs = get_workflow_inputs (wf , dic_wf_all )
192+ if reverse :
193+ inputs = get_workflow_outputs (wf , dic_wf_all )
194+ symbol_direction = "->"
195+ else :
196+ inputs = get_workflow_inputs (wf , dic_wf_all )
197+ symbol_direction = "<-"
198198 if inputs :
199- print (f"{ level * ' ' } { wf } <- { inputs } " )
199+ print (f"{ level * ' ' } { wf } { symbol_direction } { inputs } " )
200200 if levels_max < 0 or level < levels_max :
201201 for tab in inputs :
202- producers = get_table_producers (tab , dic_wf_all , case_sensitive )
202+ producers = get_table_producers (tab , dic_wf_all , case_sensitive , reverse )
203203 if producers :
204- print (f"{ level * ' ' + ' ' } { tab } <- { producers } " )
204+ print (f"{ level * ' ' + ' ' } { tab } { symbol_direction } { producers } " )
205205 for p in producers :
206206 if p not in dic_wf_tree : # avoid infinite recursion
207207 get_tree_for_workflow (
208- p ,
209- dic_wf_all ,
210- dic_wf_tree ,
211- case_sensitive ,
212- level + 1 ,
213- levels_max ,
208+ p , dic_wf_all , dic_wf_tree , case_sensitive , level + 1 , levels_max , reverse
214209 )
215210 return dic_wf_tree
216211
217212
218- def get_tree_for_table (tab : str , dic_wf_all : dict , dic_wf_tree = None , case_sensitive = False , levels_max = 0 ):
213+ def get_tree_for_table (tab : str , dic_wf_all : dict , dic_wf_tree = None , case_sensitive = False , levels_max = 0 , reverse = False ):
219214 """Get the dependency tree of tables and workflows needed to produce this table."""
220215 if dic_wf_tree is None :
221216 dic_wf_tree = {}
222- producers = get_table_producers (tab , dic_wf_all , case_sensitive )
217+ producers = get_table_producers (tab , dic_wf_all , case_sensitive , reverse )
218+ symbol_direction = "<-"
219+ if reverse :
220+ symbol_direction = "->"
223221 if producers :
224- print (f"{ tab } <- { producers } " )
225- if levels_max != 0 : # Search for more dependencies only if needed.
222+ print (f"{ tab } { symbol_direction } { producers } " )
223+ if levels_max == 0 : # Add producers in the dependency dictionary.
224+ for p in producers :
225+ if p not in dic_wf_tree :
226+ dic_wf_tree [p ] = dic_wf_all [p ]
227+ else : # Search for more dependencies if needed.
226228 print ("\n Workflow dependency tree:\n " )
227229 for p in producers :
228- get_tree_for_workflow (p , dic_wf_all , dic_wf_tree , case_sensitive , 0 , levels_max )
230+ get_tree_for_workflow (p , dic_wf_all , dic_wf_tree , case_sensitive , 0 , levels_max , reverse )
229231 else :
230- print (" No producers found" )
232+ print (f' No { "consumers" if reverse else " producers" } found' )
231233 return dic_wf_tree
232234
233235
@@ -236,8 +238,22 @@ def main():
236238 parser = argparse .ArgumentParser (
237239 description = "Find dependencies required to produce a given table or to run a given workflow."
238240 )
239- parser .add_argument ("-t" , dest = "table" , type = str , nargs = "+" , help = "table(s)" )
240- parser .add_argument ("-w" , dest = "workflow" , type = str , nargs = "+" , help = "workflow(s)" )
241+ parser .add_argument (
242+ "-t" , dest = "table" , type = str , nargs = "+" , help = "table(s) for normal (backward) search (i.e. find producers)"
243+ )
244+ parser .add_argument (
245+ "-w" , dest = "workflow" , type = str , nargs = "+" , help = "workflow(s) for normal (backward) search (i.e. find inputs)"
246+ )
247+ parser .add_argument (
248+ "-T" , dest = "table_rev" , type = str , nargs = "+" , help = "table(s) for reverse (forward) search (i.e. find consumers)"
249+ )
250+ parser .add_argument (
251+ "-W" ,
252+ dest = "workflow_rev" ,
253+ type = str ,
254+ nargs = "+" ,
255+ help = "workflow(s) for reverse (forward) search (i.e. find outputs)" ,
256+ )
241257 parser .add_argument (
242258 "-c" ,
243259 dest = "case" ,
@@ -266,10 +282,12 @@ def main():
266282 help = "maximum number of workflow tree levels (default = 0, include all if < 0)" ,
267283 )
268284 args = parser .parse_args ()
269- if not (args .table or args .workflow ):
285+ if not (args .table or args .workflow or args . table_rev or args . workflow_rev ):
270286 parser .error ("Provide table(s) and/or workflow(s)" )
271287 tables = args .table
272288 workflows = args .workflow
289+ tables_rev = args .table_rev
290+ workflows_rev = args .workflow_rev
273291 case_sensitive = args .case
274292 graph_suffix = args .suffix
275293 list_exclude = args .exclude
@@ -302,35 +320,42 @@ def main():
302320 dic_deps = {}
303321
304322 # Find table dependencies
305- if tables :
306- for table in tables :
307- print (f"\n Table: { table } \n " )
308- if not table :
309- msg_fatal ("Bad table" )
310- # producers = get_table_producers(table, dic_wf_all_simple, case_sensitive)
311- # if not producers:
312- # print("No producers found")
313- # return
314- # print(producers)
315- # print_workflows(dic_wf_all_simple, producers)
316- get_tree_for_table (table , dic_wf_all_simple , dic_deps , case_sensitive , n_levels )
323+ for t , reverse in zip ((tables , tables_rev ), (False , True )):
324+ if t :
325+ for table in t :
326+ print (f"\n Table: { table } \n " )
327+ if not table :
328+ msg_fatal ("Bad table" )
329+ # producers = get_table_producers(table, dic_wf_all_simple, case_sensitive)
330+ # if not producers:
331+ # print("No producers found")
332+ # return
333+ # print(producers)
334+ # print_workflows(dic_wf_all_simple, producers)
335+ get_tree_for_table (table , dic_wf_all_simple , dic_deps , case_sensitive , n_levels , reverse )
317336
318337 # Find workflow dependencies
319- if workflows :
320- for workflow in workflows :
321- print (f"\n Workflow: { workflow } \n " )
322- if not workflow :
323- msg_fatal ("Bad workflow" )
324- # print_workflows(dic_wf_all_simple, [workflow])
325- get_tree_for_workflow (workflow , dic_wf_all_simple , dic_deps , case_sensitive , 0 , n_levels )
338+ for w , reverse in zip ((workflows , workflows_rev ), (False , True )):
339+ if w :
340+ for workflow in w :
341+ print (f"\n Workflow: { workflow } \n " )
342+ if not workflow :
343+ msg_fatal ("Bad workflow" )
344+ # print_workflows(dic_wf_all_simple, [workflow])
345+ get_tree_for_workflow (workflow , dic_wf_all_simple , dic_deps , case_sensitive , 0 , n_levels , reverse )
326346
327347 # Print the tree dictionary with dependencies
328348 # print("\nTree\n")
329349 # print(dic_deps)
330350
331351 # Produce topology graph.
332352 if graph_suffix and dic_deps :
333- basename = "_" .join ((tables if tables else []) + (workflows if workflows else []))
353+ names_all = []
354+ for names in (tables , tables_rev , workflows , workflows_rev ):
355+ if names :
356+ names_all += names
357+ names_all = list (dict .fromkeys (names_all )) # Remove duplicities
358+ basename = "_" .join (names_all )
334359 # Set a short file name when the full name would be longer than 255 characters.
335360 if len (basename ) > 251 :
336361 basename = "o2_dependencies_" + hashlib .sha1 (basename .encode (), usedforsecurity = False ).hexdigest ()
@@ -375,7 +400,7 @@ def main():
375400 dot += dot_workflows + dot_tables + dot_deps
376401 dot += "}\n "
377402 try :
378- with open (path_file_dot , "w" ) as file_dot :
403+ with open (path_file_dot , "w" , encoding = "utf-8" ) as file_dot :
379404 file_dot .write (dot )
380405 except IOError :
381406 msg_fatal (f"Failed to open file { path_file_dot } " )
0 commit comments