Partially wroking ASCII dependency graph.
This commit is contained in:
parent
860f834aad
commit
6ffcdc1166
@ -44,7 +44,15 @@ def spec(parser, args):
|
|||||||
print "Normalized"
|
print "Normalized"
|
||||||
print "------------------------------"
|
print "------------------------------"
|
||||||
spec.normalize()
|
spec.normalize()
|
||||||
print spec.tree(color=True, indent=2)
|
print spec.tree(color=True, indent=2, cover='paths')
|
||||||
|
|
||||||
|
print
|
||||||
|
print spec.topological_sort(reverse=True)
|
||||||
|
print
|
||||||
|
print "Graph"
|
||||||
|
print "------------------------------"
|
||||||
|
print spec.graph()
|
||||||
|
return
|
||||||
|
|
||||||
print "Concretized"
|
print "Concretized"
|
||||||
print "------------------------------"
|
print "------------------------------"
|
||||||
|
@ -93,6 +93,7 @@
|
|||||||
import sys
|
import sys
|
||||||
import itertools
|
import itertools
|
||||||
import hashlib
|
import hashlib
|
||||||
|
from heapq import *
|
||||||
from StringIO import StringIO
|
from StringIO import StringIO
|
||||||
from operator import attrgetter
|
from operator import attrgetter
|
||||||
|
|
||||||
@ -712,6 +713,15 @@ def flat_dependencies(self, **kwargs):
|
|||||||
raise InconsistentSpecError("Invalid Spec DAG: %s" % e.message)
|
raise InconsistentSpecError("Invalid Spec DAG: %s" % e.message)
|
||||||
|
|
||||||
|
|
||||||
|
def index(self):
|
||||||
|
"""Return DependencyMap that points to all the dependencies in this
|
||||||
|
spec."""
|
||||||
|
dm = DependencyMap()
|
||||||
|
for spec in self.traverse():
|
||||||
|
dm[spec.name] = spec
|
||||||
|
return dm
|
||||||
|
|
||||||
|
|
||||||
def flatten(self):
|
def flatten(self):
|
||||||
"""Pull all dependencies up to the root (this spec).
|
"""Pull all dependencies up to the root (this spec).
|
||||||
Merge constraints for dependencies with the same name, and if they
|
Merge constraints for dependencies with the same name, and if they
|
||||||
@ -1316,6 +1326,174 @@ def tree(self, **kwargs):
|
|||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def graph(self, **kwargs):
|
||||||
|
N = kwargs.get('node', 'o') # Node character
|
||||||
|
out = kwargs.get('out', sys.stdout)
|
||||||
|
indent = kwargs.get('indent', 0)
|
||||||
|
indent *= ' '
|
||||||
|
|
||||||
|
topo_order = self.topological_sort(reverse=True)
|
||||||
|
clone = self.copy()
|
||||||
|
nodes = clone.index()
|
||||||
|
|
||||||
|
def ordered_deps(node):
|
||||||
|
deps = node.dependencies
|
||||||
|
return sorted((d for d in deps), reverse=True)
|
||||||
|
|
||||||
|
frontier = []
|
||||||
|
|
||||||
|
debug = True
|
||||||
|
debug = False
|
||||||
|
|
||||||
|
def back_edge(end, start):
|
||||||
|
assert(end < start)
|
||||||
|
|
||||||
|
if (start - end) > 1:
|
||||||
|
if debug:
|
||||||
|
out.write(" " * 80)
|
||||||
|
|
||||||
|
out.write(indent)
|
||||||
|
out.write("| " * (end + 1))
|
||||||
|
out.write("|_" * (start - end - 2))
|
||||||
|
out.write("|/ ")
|
||||||
|
out.write("/ " * (len(frontier) - start))
|
||||||
|
out.write("\n")
|
||||||
|
|
||||||
|
if debug:
|
||||||
|
out.write(" " * 80)
|
||||||
|
|
||||||
|
out.write(indent)
|
||||||
|
out.write("| " * end)
|
||||||
|
out.write("|/")
|
||||||
|
out.write("| " * (start - end - 1))
|
||||||
|
out.write(" /" * (len(frontier) - start))
|
||||||
|
out.write("\n")
|
||||||
|
|
||||||
|
|
||||||
|
def connect_deps(i, deps):
|
||||||
|
if len(deps) == 1 and deps in frontier:
|
||||||
|
j = frontier.index(deps)
|
||||||
|
if j < i:
|
||||||
|
back_edge(j, i)
|
||||||
|
else:
|
||||||
|
if i < j:
|
||||||
|
frontier.pop(j)
|
||||||
|
frontier.insert(i, deps)
|
||||||
|
back_edge(i, j+1)
|
||||||
|
|
||||||
|
elif deps:
|
||||||
|
frontier.insert(i, deps)
|
||||||
|
|
||||||
|
|
||||||
|
def add_deps_to_frontier(node, i):
|
||||||
|
deps = ordered_deps(node)
|
||||||
|
connect_deps(i, deps)
|
||||||
|
for d in deps:
|
||||||
|
del nodes[d].dependents[node.name]
|
||||||
|
|
||||||
|
|
||||||
|
name = topo_order.pop()
|
||||||
|
add_deps_to_frontier(nodes[name], 0)
|
||||||
|
|
||||||
|
if debug:
|
||||||
|
out.write("%-80s" % frontier)
|
||||||
|
|
||||||
|
out.write(indent)
|
||||||
|
out.write('%s %s\n' % (N, name))
|
||||||
|
|
||||||
|
while topo_order:
|
||||||
|
if debug:
|
||||||
|
out.write("%-80s" % frontier)
|
||||||
|
|
||||||
|
# Find last i, len(frontier[i]) > 1
|
||||||
|
i = len(frontier) - 1
|
||||||
|
for f in frontier[-1::-1]:
|
||||||
|
if len(f) > 1: break
|
||||||
|
i -= 1
|
||||||
|
|
||||||
|
# Expand frontier until there are enough columns for all children.
|
||||||
|
if i >= 0:
|
||||||
|
out.write(indent)
|
||||||
|
out.write("| " * i)
|
||||||
|
out.write("|\ ")
|
||||||
|
out.write("\ " * (len(frontier) - i - 1))
|
||||||
|
out.write("\n")
|
||||||
|
|
||||||
|
name = frontier[i].pop(0)
|
||||||
|
deps = [name]
|
||||||
|
|
||||||
|
connect_deps(i, deps)
|
||||||
|
|
||||||
|
else:
|
||||||
|
name = topo_order.pop()
|
||||||
|
node = nodes[name]
|
||||||
|
|
||||||
|
# Find the next node in topo order and remove it from
|
||||||
|
# the frontier. Since specs are single-rooted DAGs,
|
||||||
|
# the node is always there. If the graph had multiple
|
||||||
|
# roots, we'd need to handle that case case of a new root.
|
||||||
|
i, elt = next(f for f in enumerate(frontier) if name in f[1])
|
||||||
|
frontier.pop(i)
|
||||||
|
|
||||||
|
out.write("| " * i)
|
||||||
|
out.write("%s " % N)
|
||||||
|
out.write("| " * (len(frontier) - i))
|
||||||
|
out.write(" %s\n" % name)
|
||||||
|
|
||||||
|
if node.dependencies:
|
||||||
|
add_deps_to_frontier(node, i)
|
||||||
|
elif frontier:
|
||||||
|
if debug:
|
||||||
|
out.write(" " * 80)
|
||||||
|
|
||||||
|
out.write("| " * i)
|
||||||
|
out.write(" /" * (len(frontier) - i))
|
||||||
|
out.write("\n")
|
||||||
|
|
||||||
|
|
||||||
|
out.write("\n")
|
||||||
|
out.write("%s\n" % frontier)
|
||||||
|
|
||||||
|
# Reverse the lines in the output
|
||||||
|
#return '\n'.join(reversed(out.getvalue().split('\n')))
|
||||||
|
|
||||||
|
return "" #out.getvalue()
|
||||||
|
|
||||||
|
|
||||||
|
def topological_sort(self, **kwargs):
|
||||||
|
"""Return a list of dependency specs sorted topologically.
|
||||||
|
This spec is not modified in the process."""
|
||||||
|
reverse = kwargs.get('reverse', False)
|
||||||
|
if not reverse:
|
||||||
|
parents = lambda s: s.dependents
|
||||||
|
children = lambda s: s.dependencies
|
||||||
|
else:
|
||||||
|
parents = lambda s: s.dependencies
|
||||||
|
children = lambda s: s.dependents
|
||||||
|
|
||||||
|
spec = self.copy()
|
||||||
|
nodes = spec.index()
|
||||||
|
|
||||||
|
topo_order = []
|
||||||
|
remaining = [name for name in nodes.keys() if not parents(nodes[name])]
|
||||||
|
heapify(remaining)
|
||||||
|
|
||||||
|
while remaining:
|
||||||
|
name = heappop(remaining)
|
||||||
|
topo_order.append(name)
|
||||||
|
|
||||||
|
node = nodes[name]
|
||||||
|
for dep in children(node).values():
|
||||||
|
del parents(dep)[node.name]
|
||||||
|
if not parents(dep):
|
||||||
|
heappush(remaining, dep.name)
|
||||||
|
|
||||||
|
if any(parents(s) for s in spec.traverse()):
|
||||||
|
raise ValueError("Spec has cycles!")
|
||||||
|
else:
|
||||||
|
return topo_order
|
||||||
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return str(self)
|
return str(self)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user