aboutsummaryrefslogtreecommitdiff
blob: 702e062520c1353f0abfed4839e9e3233836cb74 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
import os, sys, re
import msgstruct

this_dir = os.path.dirname(os.path.abspath(__file__))
GRAPHSERVER = os.path.join(this_dir, 'graphserver.py')


def display_dot_file(dotfile, wait=True, save_tmp_file=None):
    """ Display the given dot file in a subprocess.
    """
    if not os.path.exists(str(dotfile)):
        raise IOError("No such file: %s" % (dotfile,))
    import graphpage
    page = graphpage.DotFileGraphPage(str(dotfile))
    display_page(page, wait=wait, save_tmp_file=save_tmp_file)

def display_page(page, wait=True, save_tmp_file=None):
    messages = [(msgstruct.CMSG_INIT, msgstruct.MAGIC)]
    history = [page]
    pagecache = {}

    def getpage(graph_id):
        page = history[graph_id]
        try:
            return pagecache[page]
        except KeyError:
            result = page.content()
            pagecache.clear()    # a cache of a single entry should be enough
            pagecache[page] = result
            return result

    def reload(graph_id):
        page = getpage(graph_id)
        if save_tmp_file:
            f = open(save_tmp_file, 'w')
            f.write(page.source)
            f.close()
        messages.extend(page_messages(page, graph_id))
        send_graph_messages(io, messages)
        del messages[:]

    io = spawn_handler()
    reload(0)

    if wait:
        try:
            while True:
                msg = io.recvmsg()
                # handle server-side messages
                if msg[0] == msgstruct.MSG_RELOAD:
                    graph_id = msg[1]
                    pagecache.clear()
                    reload(graph_id)
                elif msg[0] == msgstruct.MSG_FOLLOW_LINK:
                    graph_id = msg[1]
                    word = msg[2]
                    page = getpage(graph_id)
                    try:
                        page = page.followlink(word)
                    except KeyError:
                        io.sendmsg(msgstruct.CMSG_MISSING_LINK)
                    else:
                        # when following a link from an older page, assume that
                        # we can drop the more recent history
                        graph_id += 1
                        history[graph_id:] = [page]
                        reload(graph_id)
        except EOFError:
            pass
        except Exception, e:
            send_error(io, e)
            raise
        io.close()

def page_messages(page, graph_id):
    import graphparse
    return graphparse.parse_dot(graph_id, page.source, page.links,
                                getattr(page, 'fixedfont', False))

def send_graph_messages(io, messages):
    ioerror = None
    for msg in messages:
        try:
            io.sendmsg(*msg)
        except IOError, ioerror:
            break
    # wait for MSG_OK or MSG_ERROR
    try:
        while True:
            msg = io.recvmsg()
            if msg[0] == msgstruct.MSG_OK:
                break
    except EOFError:
        ioerror = ioerror or IOError("connexion unexpectedly closed "
                                     "(graphserver crash?)")
    if ioerror is not None:
        raise ioerror

def send_error(io, e):
    try:
        errmsg = str(e)
        if errmsg:
            errmsg = '%s: %s' % (e.__class__.__name__, errmsg)
        else:
            errmsg = '%s' % (e.__class__.__name__,)
        io.sendmsg(msgstruct.CMSG_SAY, errmsg)
    except Exception:
        pass

def spawn_handler():
    gsvar = os.environ.get('GRAPHSERVER')
    if not gsvar:
        try:
            return spawn_sshgraphserver_handler()
        except Exception, e:
            return spawn_local_handler()
    else:
        try:
            host, port = gsvar.split(':')
            host = host or '127.0.0.1'
            port = int(port)
        except ValueError:
            raise ValueError("$GRAPHSERVER must be set to HOST:PORT, got %r" %
                             (gvvar,))
        return spawn_graphserver_handler((host, port))

def spawn_local_handler():
    if hasattr(sys, 'pypy_objspaceclass'):
        python = 'python'
    else:
        python = sys.executable
    cmdline = '"%s" -u "%s" --stdio' % (python, GRAPHSERVER)
    child_in, child_out = os.popen2(cmdline, 'tb')
    io = msgstruct.FileIO(child_out, child_in)
    return io

def spawn_graphserver_handler(address):
    import socket
    s = socket.socket()
    s.connect(address)
    return msgstruct.SocketIO(s)

def spawn_sshgraphserver_handler():
    import tempfile, getpass
    tmpdir = tempfile.gettempdir()
    user = getpass.getuser()
    fn = os.path.join(tmpdir, 'dotviewer-sshgraphsrv-%s' % user)
    f = open(fn, 'r')
    port = int(f.readline().rstrip())
    f.close()
    return spawn_graphserver_handler(('127.0.0.1', port))