github.com/hwaf/hwaf@v0.0.0-20140814122253-5465f73b20f1/py-hwaftools/orch/graph.py (about) 1 #!/usr/bin/env python 2 ''' 3 Simple implementation of a data structure providing a directed 4 acyclic graph with subgraphs. 5 ''' 6 from collections import namedtuple 7 8 def odictify(kwds = None): 9 if kwds: 10 return tuple(kwds.items()) 11 return () 12 def Node(name, **options): 13 return namedtuple('Node','name options')(name,odictify(options)) 14 def Edge(tail, head, **options): 15 return namedtuple('Edge','tail head options')(tail, head, odictify(options)) 16 17 def opt_to_str(options): 18 if not options: 19 return '' 20 return ','.join(['%s="%s"' % kv for kv in options]) 21 22 class Graph(object): 23 def __init__(self, name, **options): 24 self.name = name 25 self.options = options 26 self._subgraphs = list() 27 self.nodes = set() 28 self.edges = set() 29 30 def get_subgraph(self, name): 31 for sg in self._subgraphs: 32 if sg.name == name: 33 return sg 34 return 35 36 def set_subgraph(self, sg): 37 for ind, oldsg in enumerate(self._subgraphs): 38 if oldsg.name == name: 39 self._subgraphs[ind] = sg 40 return oldsg 41 self._subgraphs.append(sg) 42 return 43 44 def graph(self, name = None): 45 if name is None or name == self.name: 46 return self 47 g = self.get_subgraph(name) 48 if not g: 49 g = Graph(name) 50 self.set_subgraph(g) 51 return g 52 53 def add_subgraph(self, name, **options): 54 already = self.get_subgraph(name) 55 if already: 56 already.options = options 57 return already 58 g = Graph(name, **options) 59 self._subgraphs.append(g) 60 return g 61 62 def add_node(self, name, graph_name = None, **options): 63 self.graph(graph_name).nodes.add(Node(name,**options)) 64 65 def add_edge(self, tail, head, graph_name = None, **options): 66 self.graph(graph_name).edges.add(Edge(tail,head,**options)) 67 68 def todot(self, depth = 0): 69 ret = [] 70 tab = ' ' 71 72 indent = tab * depth 73 74 graphtype = "digraph " 75 if depth: 76 graphtype = "subgraph cluster" 77 ret.append('%s%s%s {' % (indent, graphtype, self.name)) 78 79 for k,v in self.options.items(): 80 ret.append('%s%s = "%s";' % (indent, k, v)) 81 82 for sg in self._subgraphs: 83 ret.append(sg.todot(depth+1)) 84 85 indent = tab * (depth + 1) 86 for node in self.nodes: 87 ret.append('%s"%s" [%s];' % (indent, node.name, opt_to_str(node.options))) 88 89 for edge in self.edges: # fixme: this ignore edge options 90 ret.append('%s"%s" -> "%s" [%s];' % (indent, edge.tail, edge.head, 91 opt_to_str(edge.options))) 92 93 ret.append('%s}' % indent) 94 return '\n'.join(ret) 95 96 def __str__(self): 97 return self.todot()