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()