mirror of
https://abf.rosa.ru/djam/urpm-tools.git
synced 2025-02-23 09:22:47 +00:00
LOG - repograph: new option --tab-separated
This commit is contained in:
parent
be12c9cebe
commit
35a3dde9f5
1 changed files with 415 additions and 173 deletions
|
@ -56,10 +56,6 @@ from rpm5utils.urpmgraphs.algorithms.cycles import simple_cycles
|
|||
import gettext
|
||||
|
||||
gettext.install('urpm-tools')
|
||||
#import rpm5utils.urpmgraphs
|
||||
#from rpm5utils.urpmgraphs.algorithms import cycles
|
||||
#from rpm5utils.urpmgraphs.classes import digraph
|
||||
|
||||
|
||||
synthesis_arch = "synthesis.hdlist.cz"
|
||||
synthesis_arch_renamed = "synthesis.hdlist.gz"
|
||||
|
@ -72,6 +68,9 @@ loopdotfile = "loopgraph"
|
|||
altdotfile = "altgraph"
|
||||
default_output = "sys.stdout"
|
||||
timeout = 5
|
||||
nodes_file = "nodes.txt"
|
||||
edges_file = "edges.txt"
|
||||
modedef = ['normal', 'cross-repo', 'broken']
|
||||
|
||||
re_search_unver = re.compile("([^\[\]]+)[\[\]]")
|
||||
re_search_verrel = re.compile("\[(== |> |< |>= |<= )([\{\}+=0-9a-zA-Z_\.]*:)?([[\{\}+=0-9a-zA-Z_\.]+)(-[[\{\}+=0-9a-zA-Z_\.]+)?([^\[\]]*)\]$")
|
||||
|
@ -87,7 +86,7 @@ def ParseCommandLine():
|
|||
parser.add_argument("repository", action="store", nargs=1,
|
||||
metavar="REPOSITORY", help="URL or local PATH to repository.")
|
||||
parser.add_argument("--cross", "-c", action="store", nargs='+', metavar="CROSS_REPO",
|
||||
help=_("Search for cross-repository references in CROSS_REPO(s) repositories."))
|
||||
help=_("Search for cross-repository references in CROSS_REPO(s) repositories."))
|
||||
|
||||
parser.add_argument("--quiet", "-q", action="store_false",
|
||||
help=_("Hide service messages. (About progress status etc.)"))
|
||||
|
@ -128,6 +127,10 @@ def ParseCommandLine():
|
|||
graphgroup.add_argument("--nograph", "-n", action="store_true",
|
||||
help=_("Do not output graph. Tool will not start working if --quiet, --nograph are present \
|
||||
and --verbose is not. (If there is nothing to output - then nothing has to be done.)"))
|
||||
|
||||
parser.add_argument("--tab-separated", "-t", action="store_true",
|
||||
help=_("Output in tab-separated format. If OUTPUT_FILE is not present, files \"" + nodes_file + "\" and \"" + edges_file + "\" \
|
||||
will be created in the current directory. Otherwise these files will be written to the selected directory."))
|
||||
return parser.parse_args()
|
||||
|
||||
def exit_proc(arg):
|
||||
|
@ -139,8 +142,9 @@ def exit_proc(arg):
|
|||
err_loops = arg.loops
|
||||
err_alternatives = arg.alternatives
|
||||
err_different = arg.different
|
||||
err_iftab = arg.tab_separated
|
||||
|
||||
if (err_output != None) and not ((err_loops or err_alternatives) and (err_different)):
|
||||
if (err_output != None) and not ((err_loops or err_alternatives) and (err_different)) and not err_iftab:
|
||||
err_output.close()
|
||||
if os.path.isdir(err_tmp_dir):
|
||||
shutil.rmtree(err_tmp_dir)
|
||||
|
@ -207,10 +211,25 @@ def CheckOutput(arg):
|
|||
ifloops = arg.loops
|
||||
ifalternatives = arg.alternatives
|
||||
ifdifferent = arg.different
|
||||
iftab = arg.tab_separated
|
||||
|
||||
if (file_output == "sys.stdout") or (file_output == "stdout"):
|
||||
arg.output = sys.stdout
|
||||
return
|
||||
if iftab:
|
||||
if ifdifferent:
|
||||
if ifalternatives:
|
||||
arg.output = './' + altdotfile + '/'
|
||||
elif ifloops:
|
||||
arg.output = './' + loopdotfile + '/'
|
||||
else:
|
||||
arg.output = './'
|
||||
file_output = arg.output
|
||||
else:
|
||||
arg.output = sys.stdout
|
||||
return
|
||||
else:
|
||||
if iftab:
|
||||
arg.output = './' + file_output + '/'
|
||||
file_output = arg.output
|
||||
if((ifloops or ifalternatives) and ifdifferent): # check for dir
|
||||
if(os.path.isdir(file_output)):
|
||||
print _("Error: directory %s already exists") % file_output
|
||||
|
@ -233,6 +252,22 @@ def CheckOutput(arg):
|
|||
file_output = file_output + '/'
|
||||
arg.output = file_output
|
||||
else:
|
||||
if iftab:
|
||||
if os.path.isfile(file_output + nodes_file):
|
||||
print _("Error: File %s already exists") % (file_output + nodes_file)
|
||||
arg.output = None
|
||||
exit_proc(arg)
|
||||
if os.path.isfile(file_output + edges_file):
|
||||
print _("Error: File %s already exists") % (file_output + edges_file)
|
||||
arg.output = None
|
||||
exit_proc(arg)
|
||||
try:
|
||||
os.makedirs(file_output)
|
||||
except:
|
||||
print _("Error: directory %s was not created") % file_output
|
||||
arg.output = None
|
||||
exit_proc(arg)
|
||||
return
|
||||
if(os.path.isfile(file_output)):
|
||||
print _("Error: File %s already exists") % file_output
|
||||
arg.output = None
|
||||
|
@ -378,10 +413,14 @@ def RPMNameFilter(rpmname, disttagepoch):
|
|||
string = rpmname.split('-')
|
||||
lastpart = string.pop()
|
||||
tmp = lastpart.split('.')
|
||||
tmp.pop()
|
||||
issrc = (tmp.pop() == "src")
|
||||
ismageia = 0
|
||||
if tmp[-1].startswith("mga"):
|
||||
tmp.pop()
|
||||
ismageia = 1
|
||||
lastpart = '.'.join(tmp)
|
||||
if (lastpart[0].isdigit() or (not lastpart.startswith(disttagepoch))) and\
|
||||
(not lastpart.isdigit()):
|
||||
((not lastpart.isdigit()) or issrc or ismageia):
|
||||
name = '-'.join(string[:-1])
|
||||
else:
|
||||
name = '-'.join(string[:-2])
|
||||
|
@ -825,10 +864,14 @@ def AssignColors(dict_depend, count_depend, arg):
|
|||
"""
|
||||
ifnotquiet = arg.quiet
|
||||
ifchangecolors = arg.whatrequires
|
||||
iftab = arg.tab_separated
|
||||
|
||||
dict_colors = {}
|
||||
dict_count = {}
|
||||
|
||||
if iftab:
|
||||
return dict_colors
|
||||
|
||||
if ifnotquiet:
|
||||
print _("Calculating colors.")
|
||||
sorted_count = sorted(count_depend)
|
||||
|
@ -913,50 +956,116 @@ def OutputGraphBody(some_list, dict_color, file_output, packagename, node_type):
|
|||
tmp_string = tmp_string + '};\n\n'
|
||||
file_output.write(tmp_string)
|
||||
|
||||
|
||||
def OutputGraphTail(file_output):
|
||||
"""Finish the graph.
|
||||
"""
|
||||
file_output.write('}\n')
|
||||
|
||||
def GetNodesEdges(dict_depend):
|
||||
nodes = {}
|
||||
edges = []
|
||||
for packagename in dict_depend:
|
||||
if packagename not in nodes:
|
||||
nodes[packagename] = 0
|
||||
for pkg in dict_depend[packagename]:
|
||||
if pkg not in nodes:
|
||||
nodes[pkg] = 0
|
||||
mode = dict_depend[packagename][pkg]
|
||||
edges.append([packagename, pkg, mode])
|
||||
if mode == 2:
|
||||
nodes[pkg] = 2
|
||||
return (nodes, edges)
|
||||
|
||||
def OutputNodes(dict_depend, nodes, arg):
|
||||
"""Output nodes.
|
||||
"""
|
||||
output_dir = arg.output
|
||||
file_output = output_dir + nodes_file
|
||||
if os.path.isfile(file_output):
|
||||
print _("Error: File %s already exists") % file_output
|
||||
arg.output = None
|
||||
exit_proc(arg)
|
||||
sorted_list = sorted(nodes)
|
||||
try:
|
||||
NODES = open(file_output, "w")
|
||||
except:
|
||||
print _("Error: File %s cannot be created") % file_output
|
||||
arg.output = None
|
||||
exit_proc(arg)
|
||||
|
||||
for pkg in sorted_list:
|
||||
NODES.write('\t'.join([pkg, modedef[nodes[pkg]]]))
|
||||
NODES.write('\n')
|
||||
|
||||
NODES.close()
|
||||
|
||||
def OutputEdges(dict_depend, edges, arg):
|
||||
"""Output edges.
|
||||
"""
|
||||
output_dir = arg.output
|
||||
file_output = output_dir + edges_file
|
||||
if os.path.isfile(file_output):
|
||||
print _("Error: File %s already exists") % file_output
|
||||
arg.output = None
|
||||
exit_proc(arg)
|
||||
try:
|
||||
EDGES = open(file_output, "w")
|
||||
except:
|
||||
print _("Error: File %s cannot be created") % file_output
|
||||
arg.output = None
|
||||
exit_proc(arg)
|
||||
|
||||
for (node1, node2, mode) in edges:
|
||||
EDGES.write('\t'.join([node1, node2, modedef[mode]]))
|
||||
EDGES.write('\n')
|
||||
|
||||
EDGES.close()
|
||||
|
||||
def OutputGraph(dict_depend, dict_color, arg):
|
||||
"""Output the graph.
|
||||
"""
|
||||
file_output = arg.output
|
||||
iftab = arg.tab_separated
|
||||
if arg.whatrequires:
|
||||
selected_node = arg.whatrequires[0]
|
||||
elif arg.requires_recursive:
|
||||
selected_node = arg.requires_recursive[0]
|
||||
else:
|
||||
selected_node = None
|
||||
OutputGraphHead(file_output)
|
||||
|
||||
if (selected_node):
|
||||
file_output.write('"' + selected_node + '" [color="0.4 1.0 1.0"];\n')
|
||||
sorted_list = sorted(dict_depend)
|
||||
for packagename in sorted_list:
|
||||
if not dict_depend[packagename]:
|
||||
continue
|
||||
usual_list = []
|
||||
cross_list = []
|
||||
missed_list = []
|
||||
for pkg in dict_depend[packagename]:
|
||||
mode = dict_depend[packagename][pkg]
|
||||
if (mode == 0):
|
||||
usual_list.append(pkg)
|
||||
elif (mode == 1):
|
||||
cross_list.append(pkg)
|
||||
elif (mode == 2):
|
||||
missed_list.append(pkg)
|
||||
if not iftab:
|
||||
OutputGraphHead(file_output)
|
||||
|
||||
if (len(usual_list) > 0):
|
||||
OutputGraphBody(usual_list, dict_color, file_output, packagename, 0)
|
||||
if (len(cross_list) > 0):
|
||||
OutputGraphBody(cross_list, dict_color, file_output, packagename, 1)
|
||||
if (len(missed_list) > 0):
|
||||
OutputGraphBody(missed_list, None, file_output, packagename, 2)
|
||||
if (selected_node):
|
||||
file_output.write('"' + selected_node + '" [color="0.4 1.0 1.0"];\n')
|
||||
sorted_list = sorted(dict_depend)
|
||||
for packagename in sorted_list:
|
||||
if not dict_depend[packagename]:
|
||||
continue
|
||||
usual_list = []
|
||||
cross_list = []
|
||||
missed_list = []
|
||||
for pkg in dict_depend[packagename]:
|
||||
mode = dict_depend[packagename][pkg]
|
||||
if (mode == 0):
|
||||
usual_list.append(pkg)
|
||||
elif (mode == 1):
|
||||
cross_list.append(pkg)
|
||||
elif (mode == 2):
|
||||
missed_list.append(pkg)
|
||||
|
||||
OutputGraphTail(file_output)
|
||||
if (len(usual_list) > 0):
|
||||
OutputGraphBody(usual_list, dict_color, file_output, packagename, 0)
|
||||
if (len(cross_list) > 0):
|
||||
OutputGraphBody(cross_list, dict_color, file_output, packagename, 1)
|
||||
if (len(missed_list) > 0):
|
||||
OutputGraphBody(missed_list, None, file_output, packagename, 2)
|
||||
|
||||
OutputGraphTail(file_output)
|
||||
else: #iftab == TRUE
|
||||
(nodes, edges) = GetNodesEdges(dict_depend)
|
||||
OutputNodes(dict_depend, nodes, arg)
|
||||
OutputEdges(dict_depend, edges, arg)
|
||||
|
||||
def CountPor(number):
|
||||
tmp = number / 10
|
||||
|
@ -973,74 +1082,206 @@ def LeadingZeroes(number, por):
|
|||
def OutputLoopGraph(loops, colors, arg):
|
||||
"""Output graph(s) of loops.
|
||||
"""
|
||||
iftab = arg.tab_separated
|
||||
ifdifferent = arg.different
|
||||
if arg.whatrequires:
|
||||
selected_node = arg.whatrequires[0]
|
||||
elif arg.requires_recursive:
|
||||
selected_node = arg.requires_recursive[0]
|
||||
else:
|
||||
selected_node = None
|
||||
|
||||
output = arg.output
|
||||
file_output = output
|
||||
if not ifdifferent:
|
||||
OutputGraphHead(file_output)
|
||||
if (selected_node):
|
||||
file_output.write('"' + selected_node + '" [color="0.4 1.0 1.0"];\n')
|
||||
if not iftab:
|
||||
if arg.whatrequires:
|
||||
selected_node = arg.whatrequires[0]
|
||||
elif arg.requires_recursive:
|
||||
selected_node = arg.requires_recursive[0]
|
||||
else:
|
||||
selected_node = None
|
||||
|
||||
length = len(colors)
|
||||
por = CountPor(length)
|
||||
for i in range(length):
|
||||
if ifdifferent:
|
||||
filename = output + loopdotfile + LeadingZeroes(i, por) + '.dot'
|
||||
file_output = open(filename, 'w')
|
||||
output = arg.output
|
||||
file_output = output
|
||||
if not ifdifferent:
|
||||
OutputGraphHead(file_output)
|
||||
if (selected_node):
|
||||
file_output.write('"' + selected_node + '" [color="0.4 1.0 1.0"];\n')
|
||||
OutputGraphLoopBody(loops[i], colors[i], file_output)
|
||||
if ifdifferent:
|
||||
OutputGraphTail(file_output)
|
||||
file_output.close()
|
||||
|
||||
if not ifdifferent:
|
||||
OutputGraphTail(file_output)
|
||||
length = len(colors)
|
||||
por = CountPor(length)
|
||||
for i in range(length):
|
||||
if ifdifferent:
|
||||
filename = output + loopdotfile + LeadingZeroes(i, por) + '.dot'
|
||||
try:
|
||||
file_output = open(filename, 'w')
|
||||
except:
|
||||
print _("Error: file %s cannot be created") % filename
|
||||
arg.output = None
|
||||
exit_proc(arg)
|
||||
OutputGraphHead(file_output)
|
||||
if (selected_node):
|
||||
file_output.write('"' + selected_node + '" [color="0.4 1.0 1.0"];\n')
|
||||
OutputGraphLoopBody(loops[i], colors[i], file_output)
|
||||
if ifdifferent:
|
||||
OutputGraphTail(file_output)
|
||||
file_output.close()
|
||||
|
||||
if not ifdifferent:
|
||||
OutputGraphTail(file_output)
|
||||
else: #tab-separated
|
||||
output = arg.output
|
||||
if not ifdifferent:
|
||||
filename = output + edges_file
|
||||
try:
|
||||
file_output = open(filename, 'w')
|
||||
except:
|
||||
print _("Error: file %s cannot be created") % filename
|
||||
arg.output = None
|
||||
exit_proc(arg)
|
||||
length = len(loops)
|
||||
pot = CountPor(length)
|
||||
nodes = []
|
||||
i = 0
|
||||
for loop in loops:
|
||||
if ifdifferent:
|
||||
dirname = output + loopdotfile + LeadingZeroes(i, pot) + '/'
|
||||
filename = dirname + edges_file
|
||||
try:
|
||||
os.makedirs(dirname)
|
||||
file_output = open(filename, 'w')
|
||||
except:
|
||||
print _("Error: file %s cannot be created") % filename
|
||||
arg.output = None
|
||||
exit_proc(arg)
|
||||
nodes = []
|
||||
j = 0
|
||||
for pkg in loop:
|
||||
if j == 0:
|
||||
j = 1
|
||||
file_output.write(str(pkg))
|
||||
else:
|
||||
file_output.write('\t' + str(pkg))
|
||||
if pkg not in nodes:
|
||||
nodes.append(pkg)
|
||||
file_output.write('\n')
|
||||
if ifdifferent:
|
||||
file_output.close()
|
||||
filename = dirname + nodes_file
|
||||
try:
|
||||
file_output = open(filename, 'w')
|
||||
except:
|
||||
print _("Error: file %s cannot be created") % filename
|
||||
arg.output = None
|
||||
exit_proc(arg)
|
||||
for pkg in nodes:
|
||||
file_output.write(str(pkg) + '\n')
|
||||
file_output.close()
|
||||
i = i + 1
|
||||
if not ifdifferent:
|
||||
filename = output + nodes_file
|
||||
try:
|
||||
file_output = open(filename, 'w')
|
||||
except:
|
||||
print _("Error: file %s cannot be created") % filename
|
||||
arg.output = None
|
||||
exit_proc(arg)
|
||||
for pkg in nodes:
|
||||
file_output.write(str(pkg) + '\n')
|
||||
file_output.close()
|
||||
|
||||
def OutputAltGraph(alternatives, colors, arg):
|
||||
"""Output graph(s) of alternatives.
|
||||
"""
|
||||
iftab = arg.tab_separated
|
||||
ifdifferent = arg.different
|
||||
if arg.whatrequires:
|
||||
selected_node = arg.whatrequires[0]
|
||||
elif arg.requires_recursive:
|
||||
selected_node = arg.requires_recursive[0]
|
||||
else:
|
||||
selected_node = None
|
||||
|
||||
output = arg.output
|
||||
file_output = output
|
||||
if not ifdifferent:
|
||||
OutputGraphHead(file_output)
|
||||
if (selected_node):
|
||||
file_output.write('"' + selected_node + '" [color="0.4 1.0 1.0"];\n')
|
||||
if not iftab:
|
||||
if arg.whatrequires:
|
||||
selected_node = arg.whatrequires[0]
|
||||
elif arg.requires_recursive:
|
||||
selected_node = arg.requires_recursive[0]
|
||||
else:
|
||||
selected_node = None
|
||||
|
||||
i = 0
|
||||
length = len(colors)
|
||||
por = CountPor(length)
|
||||
for phrase in alternatives:
|
||||
if ifdifferent:
|
||||
filename = output + altdotfile + LeadingZeroes(i, por) + '.dot'
|
||||
file_output = open(filename, 'w')
|
||||
output = arg.output
|
||||
file_output = output
|
||||
if not ifdifferent:
|
||||
OutputGraphHead(file_output)
|
||||
if (selected_node):
|
||||
file_output.write('"' + selected_node + '" [color="0.4 1.0 1.0"];\n')
|
||||
OutputGraphAltBody(phrase, alternatives[phrase], colors[i], file_output)
|
||||
if ifdifferent:
|
||||
OutputGraphTail(file_output)
|
||||
file_output.close()
|
||||
i = i + 1
|
||||
|
||||
if not ifdifferent:
|
||||
OutputGraphTail(file_output)
|
||||
i = 0
|
||||
length = len(colors)
|
||||
por = CountPor(length)
|
||||
for phrase in alternatives:
|
||||
if ifdifferent:
|
||||
filename = output + altdotfile + LeadingZeroes(i, por) + '.dot'
|
||||
try:
|
||||
file_output = open(filename, 'w')
|
||||
except:
|
||||
print _("Error: file %s cannot be created") % filename
|
||||
arg.output = None
|
||||
exit_proc(arg)
|
||||
OutputGraphHead(file_output)
|
||||
if (selected_node):
|
||||
file_output.write('"' + selected_node + '" [color="0.4 1.0 1.0"];\n')
|
||||
OutputGraphAltBody(phrase, alternatives[phrase], colors[i], file_output)
|
||||
if ifdifferent:
|
||||
OutputGraphTail(file_output)
|
||||
file_output.close()
|
||||
i = i + 1
|
||||
|
||||
if not ifdifferent:
|
||||
OutputGraphTail(file_output)
|
||||
else: #tab-separated
|
||||
output = arg.output
|
||||
if not ifdifferent:
|
||||
filename = output + edges_file
|
||||
try:
|
||||
file_output = open(filename, 'w')
|
||||
except:
|
||||
print _("Error: file %s cannot be created") % filename
|
||||
arg.output = None
|
||||
exit_proc(arg)
|
||||
length = len(alternatives)
|
||||
pot = CountPor(length)
|
||||
nodes = []
|
||||
i = 0
|
||||
for phrase in alternatives:
|
||||
if ifdifferent:
|
||||
dirname = output + altdotfile + LeadingZeroes(i, pot) + '/'
|
||||
filename = dirname + edges_file
|
||||
try:
|
||||
os.makedirs(dirname)
|
||||
file_output = open(filename, 'w')
|
||||
except:
|
||||
print _("Error: file %s cannot be created") % filename
|
||||
arg.output = None
|
||||
exit_proc(arg)
|
||||
nodes = []
|
||||
file_output.write(str(phrase))
|
||||
for pkg in alternatives[phrase]:
|
||||
file_output.write('\t' + str(pkg))
|
||||
if pkg not in nodes:
|
||||
nodes.append(pkg)
|
||||
file_output.write('\n')
|
||||
if ifdifferent:
|
||||
file_output.close()
|
||||
filename = dirname + nodes_file
|
||||
try:
|
||||
file_output = open(filename, 'w')
|
||||
except:
|
||||
print _("Error: file %s cannot be created") % filename
|
||||
arg.output = None
|
||||
exit_proc(arg)
|
||||
for pkg in nodes:
|
||||
file_output.write(str(pkg) + '\n')
|
||||
file_output.close()
|
||||
i = i + 1
|
||||
if not ifdifferent:
|
||||
filename = output + nodes_file
|
||||
try:
|
||||
file_output = open(filename, 'w')
|
||||
except:
|
||||
print _("Error: file %s cannot be created") % filename
|
||||
arg.output = None
|
||||
exit_proc(arg)
|
||||
for pkg in nodes:
|
||||
file_output.write(str(pkg) + '\n')
|
||||
file_output.close()
|
||||
|
||||
def BuildGraph(dict_depend):
|
||||
"""Build additional structures.
|
||||
|
@ -1389,6 +1630,7 @@ def main(args):
|
|||
ifbroken = arg.broken
|
||||
ifoptact = ifloops or ifalternatives or ifbroken
|
||||
ifunprovided = arg.unprovided
|
||||
iftab = arg.tab_separated
|
||||
|
||||
arg.crossurl = []
|
||||
arg.tmp_dir = ""
|
||||
|
@ -1396,7 +1638,7 @@ def main(args):
|
|||
file_output = arg.output[0]
|
||||
else:
|
||||
file_output = default_output
|
||||
arg.output = None
|
||||
arg.output = file_output
|
||||
if (not ifnotquiet) and (not ifverbose) and (ifnograph):
|
||||
print _("Do not use -q/--quiet and -n/--nograph without -v/--verbose together.")
|
||||
print _("That way there is no information to output anywhere. Nothing will be done.")
|
||||
|
@ -1412,6 +1654,8 @@ def main(args):
|
|||
for i in crossrange:
|
||||
arg.crossurl.append(CheckURLPATH(arg.cross[i], arg))
|
||||
CheckOptions(arg)
|
||||
CheckOutput(arg)
|
||||
|
||||
arg.tmp_dir = tempfile.mkdtemp() + '/'
|
||||
#get all needed files
|
||||
GetFile(arg.repository, synthesis_arch, arg.tmp_dir, arg)
|
||||
|
@ -1439,8 +1683,6 @@ def main(args):
|
|||
if (answer):
|
||||
(dict_depend, count_depend, dict_asks, dict_provides, dict_cross_asks, dict_cross_provides) = answer
|
||||
|
||||
arg.output = file_output
|
||||
CheckOutput(arg)
|
||||
if (ifoptact): ##REMAKE (MUTUALLY EXCLUSIVE)
|
||||
if (ifloops):
|
||||
loops = FindLoops(dict_depend, arg)
|
||||
|
|
Loading…
Add table
Reference in a new issue