~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to dotgraph.py

  • Committer: Aaron Bentley
  • Date: 2005-06-07 18:52:04 UTC
  • Revision ID: abentley@panoramicfeedback.com-20050607185204-5fc1f0e3d393b909
Added NEWS, obsoleted bzr-pull

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