Skip to content

Commit b1ee278

Browse files
authored
Dependency finder: Add reverse mode (#10108)
1 parent 543ee01 commit b1ee278

File tree

1 file changed

+73
-48
lines changed

1 file changed

+73
-48
lines changed

Scripts/find_dependencies.py

Lines changed: 73 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -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

181181
def 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("\nWorkflow 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"\nTable: {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"\nTable: {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"\nWorkflow: {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"\nWorkflow: {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

Comments
 (0)