Example #1
0
 def _create_target_graph(self, targets, predicate=None):
   predicate = predicate or (lambda x: True)
   graph = Graph()
   for target in self._collect_dependencies(targets, predicate):
     graph.add_vertex(target.id)
     for dep in self._target_dependencies(target):
       if predicate(dep):
         graph.add_edge(Graph.Edge(target.id, dep.id))
   return graph
Example #2
0
  def test_topological_ordering(self):
    graph = self._char_graph('abc', ('ab', 'bc'))
    self.assertEquals(tuple('abc'), tuple(graph.topological_ordering()))
    self.assertEquals(tuple('abc'), tuple(graph.topological_ordering(stable=True)))

    graph = graph.transposed
    self.assertEquals(tuple('cba'), tuple(graph.topological_ordering()))
    self.assertEquals(tuple('cba'), tuple(graph.topological_ordering(stable=True)))

    graph = Graph(vertices='cabzyx')
    self.assertEquals(tuple('abcxyz'), tuple(graph.topological_ordering(stable=True)))

    graph.add_edge(Graph.Edge('x', 'a'))
    self.assertEquals(tuple('bcxayz'), tuple(graph.topological_ordering(stable=True)))

    with self.assertRaises(Graph.CycleError):
      self._char_graph('abc', ('ab', 'bc', 'ca')).topological_ordering()

    with self.assertRaises(Graph.CycleError):
      self._char_graph('abc', ('ab', 'bc', 'ba')).topological_ordering()

    with self.assertRaises(Graph.CycleError):
      self._char_graph('ab', ('ab', 'ba')).topological_ordering()
Example #3
0
  def test_graph_search(self):
    graph = Graph()
    graph.add_vertex('a')
    graph.add_vertex('b')
    graph.add_vertex('c')
    graph.add_vertex('d')
    graph.add_vertex('george')
    graph.add_vertex('dave')
    graph.add_edge(Graph.Edge('a', 'b'))
    graph.add_edge(Graph.Edge('b', 'a'))
    graph.add_edge(Graph.Edge('b', 'c'))
    graph.add_edge(Graph.Edge('a', 'd'))
    graph.add_edge(Graph.Edge('a', 'dave'))
    graph.add_edge(Graph.Edge('george', 'dave'))

    expected = '''
Vertices:
  a
  b
  c
  d
  dave
  george

Edges:
  a -> b
  a -> d
  a -> dave
  b -> a
  b -> c
  george -> dave
    '''
    self.assertEquals(expected.strip(), str(graph))
    self.assertEquals(set('ac'), graph.outgoing_vertices('b'), 'outgoing(b)')
    self.assertEquals(set('a'), graph.incoming_vertices('b'), 'incoming(b)')
    self.assertEquals(set('b'), graph.incoming_vertices('c'), 'incoming(c)')

    self.assertEquals(set('abcd').union(['dave']), graph.search_set('a'), 'search a -> ')
    self.assertEquals(set('c'), graph.search_set('c'), 'search c ->')
    self.assertEquals(set('abc'), graph.search_set('c', adjacent=graph.incoming_vertices),
                      'search <- c')

    self.assertEquals({Graph.Edge('a', 'b'), Graph.Edge('a', 'c')},
                {Graph.Edge('a', 'b'), Graph.Edge('a', 'b'), Graph.Edge('a', 'c')})
Example #4
0
  def generate_gv(self, targets):
    all_targets = set()
    skipped_targets = set()
    def target_filter(target):
      if not self.get_options().include_tests:
        test_names = ('test', 'tests', 'testing')
        if target.has_label('tests') or any(part in test_names for part in target.id.split('.')):
          skipped_targets.add(target)
          return False
      all_targets.add(target)
      return True

    self._work_log('Generating target graph.')
    graph = self._create_target_graph(targets, predicate=target_filter)
    if skipped_targets:
      print(''.join('\n  skipped {}'.format(target.id) for target in skipped_targets))

    compress_projects = self.get_options().compress_projects
    if compress_projects:
      with self._work_block('Compressing projects.'):
        target_to_project = self._target_to_project_map(all_targets)
        # Make an exception for 3rdparty.
        for target in all_targets:
          if '3rdparty' in target.id:
            target_to_project[target.id] = target.id

        new_graph = Graph()
        for vertex in graph.vertices:
          new_graph.add_vertex(target_to_project[vertex])
        for edge in graph.edges:
          new_graph.add_edge(Graph.Edge(target_to_project[edge.src], target_to_project[edge.dst]))

        graph = new_graph
        all_targets = { target_to_project.get(target.id) for target in all_targets }

        target_to_project = { target: target for target in all_targets }
        project_to_targets = { target: {target} for target in all_targets }
    else:
      with self._work_block('Calculating target project groups.'):
        target_to_project, project_to_targets = self._get_target_project_maps(targets)

    with self._work_block('Finding subgraphs.'):
      groups = sorted(map(sorted, project_to_targets.values()))

    target_to_color = {}
    if self.get_options().rainbow:
      with self._work_block('Assigning colors.'):
        for index, group in enumerate(groups):
          color = '"{hue}, {saturation}, {value}"'.format(
            hue = ((index * 32) % 360) / 360.0,
            saturation = 0.5 if index%2==0 else 0.2,
            value = 0.9 if index%2==0 else 0.7,
          )
          for vertex in group:
            if '3rdparty' in vertex:
              continue
            target_to_color[vertex] = color

    with self._work_block('Assigning vertex names.'):
      alphabet = [chr(c) for c in range(ord('a'), ord('z')+1)]
      vertex_names = {}
      counter = [0]
      for group in groups:
        for vertex in group:
          if vertex in vertex_names:
            raise ValueError('Vertex is duplicated between disjoint sets! {}'.format(vertex))
          vertex_names[vertex] = ''.join(alphabet[c] for c in counter)
          i = len(counter)-1
          while True:
            if i < 0:
              counter.insert(0, 0)
              break
            counter[i] += 1
            if counter[i] >= len(alphabet):
              counter[i] = 0
            else:
              break
            i -= 1

    with self._work_block('Generating .gv file.'):
      name = vertex_names.get
      external_color = 'maroon'
      yield 'digraph "{}" {{'.format(' '.join(sys.argv[1:]))
      yield '  rankdir=LR;'
      yield '  compound=true;'
      if self.get_options().concentrate_edges:
        yield '  concentrate=true;'
      self._work_log('Generating target vertices.')
      for index, group in enumerate(groups):
        label = None
        yield '  subgraph project_{} {{'.format(index)
        yield '    color=black;'
        for vertex in group:
          if label is None:
            label = vertex[:vertex.find('.')]
            yield '    label = "{}";'.format(label)
          args = {
            'label': '"{}"'.format(vertex),
            'shape': 'box',
            'color': 'black',
            'style': 'filled'
          }
          if '3rdparty' in vertex:
            args['shape'] = 'ellipse'
            args['color'] = external_color
            args['fillcolor'] = 'pink'
          else:
            args['fillcolor'] = target_to_color.get(vertex, 'lightgrey')
          yield '    {name} {args};'.format(
            name=name(vertex),
            args=self._dot_args(args),
          )
        yield '  }'
      self._work_log('Generating edges.')
      for edge in graph.edges:
        same_project = target_to_project[edge.src] == target_to_project[edge.dst]
        args = {
          'color': target_to_color.get(edge.dst, 'black'),
        }
        if '3rdparty' in edge.dst:
          args['color'] = external_color
          args['arrowsize'] = 0.5
          args['style'] = 'dashed'
        elif same_project:
          args['weight'] = self.get_options().dot_project_weight
        yield '  {src} -> {dst}{args};'.format(
          src=name(edge.src),
          dst=name(edge.dst),
          args=self._dot_args(args)
        )
      yield '}'