~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to dotgraph.py

  • Committer: Robert Collins
  • Date: 2005-09-28 05:43:19 UTC
  • mto: (147.2.6) (364.1.3 bzrtools)
  • mto: This revision was merged to the branch mainline in revision 324.
  • Revision ID: robertc@robertcollins.net-20050928054319-2c2e9e3048bbc215
find_branch -> open_containing change

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python2.4
 
2
from subprocess import Popen, PIPE
 
3
from urllib import urlencode
 
4
from xml.sax.saxutils import escape
 
5
import os.path
 
6
import errno
 
7
import tempfile
 
8
import shutil
 
9
 
 
10
mail_map = {}
 
11
 
 
12
class NoDot(Exception):
 
13
    def __init__(self):
 
14
        Exception.__init__(self, "Can't find dot!")
 
15
 
 
16
class NoRsvg(Exception):
 
17
    def __init__(self):
 
18
        Exception.__init__(self, "Can't find rsvg!")
 
19
 
 
20
class Node(object):
 
21
    def __init__(self, name, color=None, label=None, rev_id=None,
 
22
                 cluster=None):
 
23
        self.name = name
 
24
        self.color = color
 
25
        self.label = label
 
26
        self.committer = None
 
27
        self.rev_id = rev_id
 
28
        self.node_style = []
 
29
        self.cluster = cluster
 
30
 
 
31
    def get_committer(self):
 
32
        if self.committer is not None:
 
33
            if '@' in self.committer:
 
34
                try:
 
35
                    return mail_map[self.committer]
 
36
                except KeyError:
 
37
                    pass
 
38
            return self.committer
 
39
        elif self.rev_id is not None:
 
40
            try:
 
41
                first_segment = '-'.join(self.rev_id.split('-')[:-2])\
 
42
                    .strip(' ')
 
43
            except ValueError:
 
44
                first_segment = []
 
45
            if '@' in first_segment:
 
46
                try:
 
47
                    return mail_map[first_segment]
 
48
                except KeyError:
 
49
                    return first_segment
 
50
 
 
51
    def get_label(self):
 
52
        label = None
 
53
        committer = self.get_committer()
 
54
        if committer is not None:
 
55
            label = "%s\\n%s" % (self.name, committer)
 
56
        return label
 
57
 
 
58
    def define(self):
 
59
        attributes = []
 
60
        style = []
 
61
        if self.color is not None:
 
62
            attributes.append('fillcolor="%s"' % self.color)
 
63
            style.append('filled')
 
64
        style.extend(self.node_style)
 
65
        if len(style) > 0:
 
66
            attributes.append('style="%s"' % ",".join(style))
 
67
        label = self.get_label()
 
68
        if label is not None:
 
69
            attributes.append('label="%s"' % label)
 
70
        attributes.append('shape="box"')
 
71
        if len(attributes) > 0:
 
72
            return '%s[%s]' % (self.name, " ".join(attributes))
 
73
 
 
74
    def __str__(self):
 
75
        return self.name
 
76
 
 
77
def dot_output(relations):
 
78
    defined = set()
 
79
    yield "digraph G\n"
 
80
    yield "{\n"
 
81
    clusters = set()
 
82
    def rel_appropriate(start, end, cluster):
 
83
        if cluster is None:
 
84
            return start.cluster is None or end.cluster is None
 
85
        else:
 
86
            return start.cluster==cluster and end.cluster==cluster
 
87
 
 
88
    for (start, end) in relations:
 
89
        if start.cluster is not None:
 
90
            clusters.add(start.cluster)
 
91
        if end.cluster is not None:
 
92
            clusters.add(end.cluster)
 
93
    clusters = list(clusters)
 
94
    clusters.append(None)
 
95
    for index, cluster in enumerate(clusters):
 
96
        if cluster is not None:
 
97
            yield "subgraph cluster_%s\n" % index
 
98
            yield "{\n"
 
99
            yield '    label="%s"\n' % cluster
 
100
        for (start, end) in relations:
 
101
            if start.name not in defined and start.cluster == cluster:
 
102
                defined.add(start.name)
 
103
                my_def = start.define()
 
104
                if my_def is not None:
 
105
                    yield "    %s;\n" % my_def
 
106
            if end.name not in defined and end.cluster == cluster:
 
107
                defined.add(end.name)
 
108
                my_def = end.define()
 
109
                if my_def is not None:
 
110
                    yield "    %s;\n" % my_def
 
111
            if rel_appropriate(start, end, cluster):
 
112
                yield "    %s->%s;\n" % (start.name, end.name)
 
113
        if cluster is not None:
 
114
            yield "}\n"
 
115
    yield "}\n"
 
116
 
 
117
def invoke_dot_aa(input, out_file, file_type='png'):
 
118
    """\
 
119
    Produce antialiased Dot output, invoking rsvg on an intermediate file.
 
120
    rsvg only supports png, jpeg and .ico files."""
 
121
    tempdir = tempfile.mkdtemp()
 
122
    try:
 
123
        temp_file = os.path.join(tempdir, 'temp.svg')
 
124
        invoke_dot(input, temp_file, 'svg')
 
125
        cmdline = ['rsvg', temp_file, out_file]
 
126
        try:
 
127
            rsvg_proc = Popen(cmdline)
 
128
        except OSError, e:
 
129
            if e.errno == errno.ENOENT:
 
130
                raise NoRsvg()
 
131
        status = rsvg_proc.wait()
 
132
    finally:
 
133
        shutil.rmtree(tempdir)
 
134
    return status
 
135
 
 
136
def invoke_dot(input, out_file=None, file_type='svg', antialias=None):
 
137
    cmdline = ['dot', '-T%s' % file_type]
 
138
    if out_file is not None:
 
139
        cmdline.extend(('-o', out_file))
 
140
    try:
 
141
        dot_proc = Popen(cmdline, stdin=PIPE)
 
142
    except OSError, e:
 
143
        if e.errno == errno.ENOENT:
 
144
            raise NoDot()
 
145
        else:
 
146
            raise
 
147
    for line in input:
 
148
        dot_proc.stdin.write(line)
 
149
    dot_proc.stdin.close()
 
150
    return dot_proc.wait()