[pypy-svn] r7749 - in pypy/trunk/src: goal pypy/objspace/flow pypy/translator pypy/translator/tool pypy/translator/tool/pygame

arigo at codespeak.net arigo at codespeak.net
Sat Dec 4 13:38:00 CET 2004


Author: arigo
Date: Sat Dec  4 13:37:58 2004
New Revision: 7749

Added:
   pypy/trunk/src/pypy/translator/tool/graphpage.py
      - copied, changed from r7715, pypy/trunk/src/pypy/translator/tool/pygame/flowviewer.py
   pypy/trunk/src/pypy/translator/tool/graphserver.py
   pypy/trunk/src/pypy/translator/tool/pygame/graphclient.py   (contents, props changed)
Removed:
   pypy/trunk/src/pypy/translator/tool/pygame/flowviewer.py
Modified:
   pypy/trunk/src/goal/translate_pypy.py
   pypy/trunk/src/pypy/objspace/flow/model.py
   pypy/trunk/src/pypy/translator/tool/flowtrace.py
   pypy/trunk/src/pypy/translator/tool/make_dot.py
   pypy/trunk/src/pypy/translator/tool/pygame/drawgraph.py
   pypy/trunk/src/pypy/translator/tool/pygame/graphdisplay.py
   pypy/trunk/src/pypy/translator/translator.py
Log:
translate_pypy can now be a "web" server sending graphs to the "browser" in
translator/tool/pygame/.  (The protocol is not HTTP, though.)  It is useful
both for inspecting graphs generated by translate_pypy on a remote
supercomputer, and to track the progress of a running translate_pypy -- the
"server" is started early and runs in parallel with the annotation phase.  
(In the latter case, red blocks correspond to functions that containing
pending blocks, not just dead-locked blocked blocks.)

To enable this, translate_pypy must be run with a port number to listen to.  
For the stand-alone graph client run pypy/translator/tool/pygame/graphclient.



Modified: pypy/trunk/src/goal/translate_pypy.py
==============================================================================
--- pypy/trunk/src/goal/translate_pypy.py	(original)
+++ pypy/trunk/src/goal/translate_pypy.py	Sat Dec  4 13:37:58 2004
@@ -4,6 +4,8 @@
 """
 Command-line options for translate_pypy:
 
+   port     Listen on the given port number for connexions
+                (see pypy/translator/tool/pygame/graphclient.py)
    -text    Don't start the Pygame viewer
    -no-a    Don't infer annotations, just translate everything
    -no-c    Don't generate the C code
@@ -46,6 +48,8 @@
     # caches (as far as analyzing the entry_point is concerned) 
     entry_point() 
     t = Translator(entry_point, verbose=True, simplifying=True)
+    if listen_port:
+        run_async_server()
     if not options['-no-a']:
         a = t.annotate([])
         a.simplify()
@@ -138,6 +142,13 @@
             cleanup(*cleanup_args)
     return threading.Thread(target=_run_in_thread, args=())
 
+def run_async_server():
+    from pypy.translator.tool import graphpage, graphserver
+    homepage = graphpage.TranslatorPage(t)
+    graphserver.run_server(homepage, port=listen_port, background=True)
+    options['-text'] = True
+
+
 if __name__ == '__main__':
 
     options = {'-text': False,
@@ -149,12 +160,16 @@
                '-tcc':  False,
                '-no-d': False,
                }
+    listen_port = None
     for arg in sys.argv[1:]:
         if arg in ('-h', '--help'):
             print __doc__.strip()
             sys.exit()
-        assert arg in options, "unknown option %r" % (arg,)
-        options[arg] = True
+        try:
+            listen_port = int(arg)
+        except ValueError:
+            assert arg in options, "unknown option %r" % (arg,)
+            options[arg] = True
     if options['-tcc']:
         os.environ['PYPY_CC'] = 'tcc -shared -o "%s.so" "%s.c"'
     if options['-no-d']:
@@ -186,14 +201,15 @@
         print "don't know about", x
 
     def run_server():
-        from pypy.translator.tool.pygame.flowviewer import TranslatorLayout
+        from pypy.translator.tool.graphpage import TranslatorPage
+        from pypy.translator.tool.pygame.graphclient import get_layout
         from pypy.translator.tool.pygame.graphdisplay import GraphDisplay
         import pygame
 
         if not options['-no-mark-some-objects']:
             find_someobjects(t, quiet=True)
 
-        display = GraphDisplay(TranslatorLayout(t))
+        display = GraphDisplay(get_layout(TranslatorPage(t)))
         async_quit = display.async_quit
         return display.run, async_quit, pygame.quit
 

Modified: pypy/trunk/src/pypy/objspace/flow/model.py
==============================================================================
--- pypy/trunk/src/pypy/objspace/flow/model.py	(original)
+++ pypy/trunk/src/pypy/objspace/flow/model.py	Sat Dec  4 13:37:58 2004
@@ -43,8 +43,8 @@
             return self._onlyex 
 
     def show(self):
-        from pypy.translator.tool.pygame.flowviewer import SingleGraphLayout
-        SingleGraphLayout(self).display()
+        from pypy.translator.tool.graphpage import SingleGraphPage
+        SingleGraphPage(self).display()
 
 class Link:
     def __init__(self, args, target, exitcase=None):

Modified: pypy/trunk/src/pypy/translator/tool/flowtrace.py
==============================================================================
--- pypy/trunk/src/pypy/translator/tool/flowtrace.py	(original)
+++ pypy/trunk/src/pypy/translator/tool/flowtrace.py	Sat Dec  4 13:37:58 2004
@@ -260,12 +260,12 @@
     """Shows the control flow graph with annotations if computed.
     Requires 'dot' and pygame."""
     from pypy.translator.tool.pygame.graphdisplay import GraphDisplay
-    from pypy.translator.tool.pygame.flowviewer import FlowGraphLayout
+    from pypy.translator.tool.graphpage import FlowGraphPage
     from pypy.translator.translator import Translator
     t = Translator(func)
     t.simplify()
     #t.annotate([int])
-    GraphDisplay(FlowGraphLayout(t)).run()
+    FlowGraphPage(t).display()
 
 def timeit(num, func, *args):
     from time import time as now

Copied: pypy/trunk/src/pypy/translator/tool/graphpage.py (from r7715, pypy/trunk/src/pypy/translator/tool/pygame/flowviewer.py)
==============================================================================
--- pypy/trunk/src/pypy/translator/tool/pygame/flowviewer.py	(original)
+++ pypy/trunk/src/pypy/translator/tool/graphpage.py	Sat Dec  4 13:37:58 2004
@@ -1,27 +1,52 @@
-import autopath
 import inspect
-from pypy.translator.tool.pygame.drawgraph import GraphLayout
-from pypy.translator.tool.make_dot import DotGen
+from pypy.objspace.flow.model import traverse
+from pypy.translator.tool.make_dot import DotGen, make_dot, make_dot_graphs
 from pypy.interpreter.pycode import CO_VARARGS, CO_VARKEYWORDS
 from pypy.annotation import model
 from pypy.annotation.classdef import ClassDef
 from pypy.tool.uid import uid
 
 
-class SingleGraphLayout(GraphLayout):
-    """ A GraphLayout showing a single precomputed FlowGraph."""
+class GraphPage:
+    """Base class for the server-side content of one of the 'pages'
+    (one graph) sent over to and displayed by the client.
+    """
+    def __init__(self, *args):
+        self.args = args
+
+    def content(self):
+        """Compute the content of the page.
+        This doesn't modify the page in place; it returns a new GraphPage.
+        """
+        if hasattr(self, 'source'):
+            return self
+        else:
+            new = self.__class__()
+            new.source = ''  # '''dot source'''
+            new.links  = {}  # {'word': 'statusbar text'}
+            new.compute(*self.args)   # defined in subclasses
+            return new
+
+    def followlink(self, word):
+        raise KeyError
+
+    def display(self):
+        "Display a graph page locally."
+        from pypy.translator.tool.pygame.graphclient import get_layout
+        get_layout(self).display()
 
-    def __init__(self, graph):
-        from pypy.translator.tool.make_dot import make_dot
-        fn = make_dot(graph.name, graph, target='plain')
-        GraphLayout.__init__(self, fn)
 
+class SingleGraphPage(GraphPage):
+    """ A GraphPage showing a single precomputed FlowGraph."""
 
-class VariableHistoryGraphLayout(GraphLayout):
-    """ A GraphLayout showing the history of variable bindings. """
+    def compute(self, graph):
+        self.source = make_dot(graph.name, graph, target=None)
 
-    def __init__(self, translator, name, info, caused_by, history, func_names):
-        self.links = {}
+
+class VariableHistoryGraphPage(GraphPage):
+    """ A GraphPage showing the history of variable bindings. """
+
+    def compute(self, translator, name, info, caused_by, history, func_names):
         self.linkinfo = {}
         self.translator = translator
         self.func_names = func_names
@@ -44,9 +69,7 @@
                 label += '\\n' + self.createlink(caused_by)
             dotgen.emit_node(str(n+1), shape="box", label=label)
             dotgen.emit_edge(str(n+1), str(n))
-        links = self.links  # GraphLayout.__init__ will override it with {}
-        GraphLayout.__init__(self, dotgen.generate(target='plain'))
-        self.links.update(links)
+        self.source = dotgen.generate(target=None)
 
     def createlink(self, position_key, wording='Caused by a call from'):
         fn, block, pos = position_key
@@ -66,14 +89,13 @@
     def followlink(self, funcname):
         fn, block, pos = self.linkinfo[funcname]
         # It would be nice to focus on the block
-        return FlowGraphLayout(self.translator, [fn], self.func_names)
+        return FlowGraphPage(self.translator, [fn], self.func_names)
 
 
-class FlowGraphLayout(GraphLayout):
-    """ A GraphLayout showing a Flow Graph (or a few flow graphs).
+class FlowGraphPage(GraphPage):
+    """ A GraphPage showing a Flow Graph (or a few flow graphs).
     """
-    def __init__(self, translator, functions=None, func_names=None):
-        from pypy.translator.tool.make_dot import make_dot_graphs
+    def compute(self, translator, functions=None, func_names=None):
         self.translator = translator
         self.annotator = translator.annotator
         self.func_names = func_names or {}
@@ -84,8 +106,7 @@
             for block, was_annotated in self.annotator.annotated.items():
                 if not was_annotated:
                     block.fillcolor = "red"
-        fn = make_dot_graphs(graphs[0].name + "_graph", gs, target='plain')
-        GraphLayout.__init__(self, fn)
+        self.source = make_dot_graphs(graphs[0].name+"_graph", gs, target=None)
         # make the dictionary of links -- one per annotated variable
         self.binding_history = {}
         self.current_value = {}
@@ -109,7 +130,7 @@
         caused_by = self.caused_by[varname]
         history = list(self.binding_history.get(varname, []))
         history.reverse()
-        return VariableHistoryGraphLayout(self.translator, varname, cur_value,
+        return VariableHistoryGraphPage(self.translator, varname, cur_value,
                                           caused_by, history, self.func_names)
 
 
@@ -127,10 +148,10 @@
     return '\\n'.join(lines)
 
 
-class ClassDefLayout(GraphLayout):
-    """A GraphLayout showing the attributes of a class.
+class ClassDefPage(GraphPage):
+    """A GraphPage showing the attributes of a class.
     """
-    def __init__(self, translator, cdef):
+    def compute(self, translator, cdef):
         self.translator = translator
         dotgen = DotGen(cdef.cls.__name__, rankdir="LR")
 
@@ -152,14 +173,14 @@
             prevcdef = cdef
             cdef = cdef.basedef
         
-        GraphLayout.__init__(self, dotgen.generate(target='plain'))
+        self.source = dotgen.generate(target=None)
 
 
-class TranslatorLayout(GraphLayout):
-    """A GraphLayout showing a the call graph between functions
+class TranslatorPage(GraphPage):
+    """A GraphPage showing a the call graph between functions
     as well as the class hierarchy."""
 
-    def __init__(self, translator):
+    def compute(self, translator):
         self.translator = translator
         self.object_by_name = {}
         self.name_by_object = {}
@@ -168,10 +189,16 @@
 
         # show the call graph
         functions = translator.functions
+        blocked_functions = {}
         if translator.annotator:
-            blocked_functions = translator.annotator.blocked_functions
-        else:
-            blocked_functions = {}
+            # don't use translator.annotator.blocked_functions here because
+            # it is not populated until the annotator finishes.
+            annotated = translator.annotator.annotated
+            for fn, graph in translator.flowgraphs.items():
+                def visit(node):
+                    if annotated.get(node) is False:
+                        blocked_functions[fn] = True
+                traverse(visit, graph)
         highlight_functions = getattr(translator, 'highlight_functions', {}) # XXX
         dotgen.emit_node('entry', fillcolor="green", shape="octagon",
                          label="Translator\\nEntry Point")
@@ -201,7 +228,7 @@
                 dotgen.emit_node(nameof(classdef), label=data, shape="box")
                 dotgen.emit_edge(nameof(classdef.basedef), nameof(classdef))
         
-        GraphLayout.__init__(self, dotgen.generate(target='plain'))
+        self.source = dotgen.generate(target=None)
 
         # link the function names to the individual flow graphs
         for name, obj in self.object_by_name.iteritems():
@@ -232,9 +259,9 @@
     def followlink(self, name):
         obj = self.object_by_name[name]
         if isinstance(obj, ClassDef):
-            return ClassDefLayout(self.translator, obj)
+            return ClassDefPage(self.translator, obj)
         else:
-            return FlowGraphLayout(self.translator, [obj], self.name_by_object)
+            return FlowGraphPage(self.translator, [obj], self.name_by_object)
 
 
 def nameof(obj, cache={}):
@@ -246,22 +273,3 @@
         result = '%s__0x%x' % (getattr(obj, '__name__', ''), uid(obj))
         cache[obj] = result
         return result
-
-# ____________________________________________________________
-
-if __name__ == '__main__':
-    from pypy.translator.translator import Translator
-    from pypy.translator.test import snippet
-    from pypy.translator.tool.pygame.graphdisplay import GraphDisplay
-    
-    t = Translator(snippet.powerset)
-    #t.simplify()
-    a = t.annotate([int])
-    a.simplify()
-    GraphDisplay(FlowGraphLayout(t)).run()
-
-##    t = Translator(snippet._methodcall1)
-##    t.simplify()
-##    a = t.annotate([int])
-##    a.simplify()
-##    GraphDisplay(TranslatorLayout(t)).run()

Added: pypy/trunk/src/pypy/translator/tool/graphserver.py
==============================================================================
--- (empty file)
+++ pypy/trunk/src/pypy/translator/tool/graphserver.py	Sat Dec  4 13:37:58 2004
@@ -0,0 +1,77 @@
+"""
+A socket server for GraphPages.
+"""
+
+import autopath, sys, thread, struct, marshal
+
+
+def run_server(homepage, port=8888, quiet=False, background=False):
+    import socket
+    server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    server_sock.bind(('', port))
+    server_sock.listen(5)
+    if not quiet:
+        print >> sys.stderr, 'Accepting connexions on port %d...' % port
+    def accept_connexions():
+        while True:
+            conn, addr = server_sock.accept()
+            if not quiet:
+                print >> sys.stderr, 'Connected by %r.' % (addr,)
+            thread.start_new_thread(serve_connexion, (conn, homepage))
+    if background:
+        thread.start_new_thread(accept_connexions, ())
+    else:
+        accept_connexions()
+
+
+def recv_all(s, count):
+    buf = ''
+    while len(buf) < count:
+        data = s.recv(count - len(buf))
+        if not data:
+            raise SystemExit   # thread exit, rather
+        buf += data
+    return buf
+
+def recv_msg(s):
+    hdr_size = struct.calcsize("!i")
+    msg_size, = struct.unpack("!i", recv_all(s, hdr_size))
+    msg = recv_all(s, msg_size)
+    return marshal.loads(msg)
+
+def send_msg(s, msg):
+    data = marshal.dumps(msg)
+    s.sendall(struct.pack("!i", len(data)) + data)
+
+
+def serve_connexion(s, homepage):
+    pages = {0: homepage}
+    while True:
+        key, link = recv_msg(s)
+        page = pages[key]
+        if link is not None:
+            try:
+                page = page.content().followlink(link)
+                key = len(pages)
+            except KeyError:
+                page = MissingPage()
+                key = -1
+            pages[key] = page
+        page = page.content()
+        reply = {
+            'key': key,
+            'dot': page.source,
+            'links': page.links,
+            }
+        send_msg(s, reply)
+
+
+class MissingPage:
+    links  = {}
+    source = '''
+digraph error {
+msg [shape="box", label="Error: a link has gone missing.", color="black", fillcolor="red", style="filled"];
+}
+'''
+    def content(self):
+        return self

Modified: pypy/trunk/src/pypy/translator/tool/make_dot.py
==============================================================================
--- pypy/trunk/src/pypy/translator/tool/make_dot.py	(original)
+++ pypy/trunk/src/pypy/translator/tool/make_dot.py	Sat Dec  4 13:37:58 2004
@@ -23,6 +23,8 @@
 
     def generate(self, storedir=None, target='ps'):
         source = self.get_source()
+        if target is None:
+            return source    # unprocessed
         if storedir is None:
             storedir = udir
         pdot = storedir.join('%s.dot' % self.graphname)

Modified: pypy/trunk/src/pypy/translator/tool/pygame/drawgraph.py
==============================================================================
--- pypy/trunk/src/pypy/translator/tool/pygame/drawgraph.py	(original)
+++ pypy/trunk/src/pypy/translator/tool/pygame/drawgraph.py	Sat Dec  4 13:37:58 2004
@@ -79,6 +79,9 @@
         from pypy.translator.tool.pygame.graphdisplay import GraphDisplay
         GraphDisplay(self).run()
 
+    def reload(self):
+        return self
+
 class Node:
     def __init__(self, name, x, y, w, h, label, style, shape, color, fillcolor):
         self.name = name

Deleted: /pypy/trunk/src/pypy/translator/tool/pygame/flowviewer.py
==============================================================================
--- /pypy/trunk/src/pypy/translator/tool/pygame/flowviewer.py	Sat Dec  4 13:37:58 2004
+++ (empty file)
@@ -1,267 +0,0 @@
-import autopath
-import inspect
-from pypy.translator.tool.pygame.drawgraph import GraphLayout
-from pypy.translator.tool.make_dot import DotGen
-from pypy.interpreter.pycode import CO_VARARGS, CO_VARKEYWORDS
-from pypy.annotation import model
-from pypy.annotation.classdef import ClassDef
-from pypy.tool.uid import uid
-
-
-class SingleGraphLayout(GraphLayout):
-    """ A GraphLayout showing a single precomputed FlowGraph."""
-
-    def __init__(self, graph):
-        from pypy.translator.tool.make_dot import make_dot
-        fn = make_dot(graph.name, graph, target='plain')
-        GraphLayout.__init__(self, fn)
-
-
-class VariableHistoryGraphLayout(GraphLayout):
-    """ A GraphLayout showing the history of variable bindings. """
-
-    def __init__(self, translator, name, info, caused_by, history, func_names):
-        self.links = {}
-        self.linkinfo = {}
-        self.translator = translator
-        self.func_names = func_names
-        dotgen = DotGen('binding')
-        label = "Most recent binding of %s\\n\\n%s" % (name, nottoowide(info))
-        if info.origin is not None:
-            label += "\\n" + self.createlink(info.origin, 'Originated at')
-        if caused_by is not None:
-            label += '\\n' + self.createlink(caused_by)
-        if info.caused_by_merge is not None:
-            data = 'unionof%r' % (info.caused_by_merge,)
-            label += '\\n%s' % nottoowide(data)
-        
-        dotgen.emit_node('0', shape="box", color="red", label=label)
-        for n, (data, caused_by) in zip(range(len(history)), history):
-            label = nottoowide(data)
-            if data.origin is not None:
-                label += "\\n" + self.createlink(data.origin, 'Originated at')
-            if caused_by is not None:
-                label += '\\n' + self.createlink(caused_by)
-            dotgen.emit_node(str(n+1), shape="box", label=label)
-            dotgen.emit_edge(str(n+1), str(n))
-        links = self.links  # GraphLayout.__init__ will override it with {}
-        GraphLayout.__init__(self, dotgen.generate(target='plain'))
-        self.links.update(links)
-
-    def createlink(self, position_key, wording='Caused by a call from'):
-        fn, block, pos = position_key
-        basename = self.func_names.get(fn, fn.func_name)
-        linkname = basename
-        n = 1
-        while self.linkinfo.get(linkname, position_key) != position_key:
-            n += 1
-            linkname = '%s_%d' % (basename, n)
-        self.linkinfo[linkname] = position_key
-        # It would be nice to get the block name somehow
-        blockname = block.__class__.__name__
-        self.links[linkname] = '%s, %s, position %r:\n%s' % (basename,
-                                        blockname, pos, block.operations[pos])
-        return '%s %s' % (wording, linkname)
-
-    def followlink(self, funcname):
-        fn, block, pos = self.linkinfo[funcname]
-        # It would be nice to focus on the block
-        return FlowGraphLayout(self.translator, [fn], self.func_names)
-
-
-class FlowGraphLayout(GraphLayout):
-    """ A GraphLayout showing a Flow Graph (or a few flow graphs).
-    """
-    def __init__(self, translator, functions=None, func_names=None):
-        from pypy.translator.tool.make_dot import make_dot_graphs
-        self.translator = translator
-        self.annotator = translator.annotator
-        self.func_names = func_names or {}
-        functions = functions or translator.functions
-        graphs = [translator.getflowgraph(func) for func in functions]
-        gs = [(graph.name, graph) for graph in graphs]
-        if self.annotator and self.annotator.blocked_functions:
-            for block, was_annotated in self.annotator.annotated.items():
-                if not was_annotated:
-                    block.fillcolor = "red"
-        fn = make_dot_graphs(graphs[0].name + "_graph", gs, target='plain')
-        GraphLayout.__init__(self, fn)
-        # make the dictionary of links -- one per annotated variable
-        self.binding_history = {}
-        self.current_value = {}
-        self.caused_by = {}
-        if self.annotator:
-            for var in self.annotator.bindings:
-                s_value = self.annotator.binding(var)
-                info = '%s: %s' % (var.name, s_value)
-                self.links[var.name] = info
-                self.current_value[var.name] = s_value
-                self.caused_by[var.name] = (
-                    self.annotator.binding_caused_by.get(var))
-            for var, history in self.annotator.bindingshistory.items():
-                cause_history = (
-                    self.annotator.binding_cause_history.get(var, []))
-                self.binding_history[var.name] = zip(history, cause_history)
-
-    def followlink(self, varname):
-        # clicking on a variable name shows its binding history
-        cur_value = self.current_value[varname]
-        caused_by = self.caused_by[varname]
-        history = list(self.binding_history.get(varname, []))
-        history.reverse()
-        return VariableHistoryGraphLayout(self.translator, varname, cur_value,
-                                          caused_by, history, self.func_names)
-
-
-def nottoowide(text, width=72):
-    parts = str(text).split(' ')
-    lines = []
-    line = parts.pop(0)
-    for s in parts:
-        if len(line)+len(s) < width:
-            line = line + ' ' + s
-        else:
-            lines.append(line)
-            line = s
-    lines.append(line)
-    return '\\n'.join(lines)
-
-
-class ClassDefLayout(GraphLayout):
-    """A GraphLayout showing the attributes of a class.
-    """
-    def __init__(self, translator, cdef):
-        self.translator = translator
-        dotgen = DotGen(cdef.cls.__name__, rankdir="LR")
-
-        def writecdef(cdef):
-            dotgen.emit_node(nameof(cdef), color="red", shape="octagon",
-                             label=repr(cdef.cls))
-            attrs = cdef.attrs.items()
-            attrs.sort()
-            for name, attrdef in attrs:
-                s_value = attrdef.s_value
-                dotgen.emit_node(name, shape="box", label=nottoowide(s_value))
-                dotgen.emit_edge(nameof(cdef), name, label=name)
-
-        prevcdef = None
-        while cdef is not None:
-            writecdef(cdef)
-            if prevcdef:
-                dotgen.emit_edge(nameof(cdef), nameof(prevcdef), color="red")
-            prevcdef = cdef
-            cdef = cdef.basedef
-        
-        GraphLayout.__init__(self, dotgen.generate(target='plain'))
-
-
-class TranslatorLayout(GraphLayout):
-    """A GraphLayout showing a the call graph between functions
-    as well as the class hierarchy."""
-
-    def __init__(self, translator):
-        self.translator = translator
-        self.object_by_name = {}
-        self.name_by_object = {}
-        dotgen = DotGen('translator')
-        dotgen.emit('mclimit=15.0')
-
-        # show the call graph
-        functions = translator.functions
-        if translator.annotator:
-            blocked_functions = translator.annotator.blocked_functions
-        else:
-            blocked_functions = {}
-        highlight_functions = getattr(translator, 'highlight_functions', {}) # XXX
-        dotgen.emit_node('entry', fillcolor="green", shape="octagon",
-                         label="Translator\\nEntry Point")
-        for func in functions:
-            name = func.func_name
-            class_ = getattr(func, 'class_', None)
-            if class_ is not None:
-                name = '%s.%s' % (class_.__name__, name)
-            data = self.labelof(func, name)
-            if func in blocked_functions:
-                kw = {'fillcolor': 'red'}
-            elif func in highlight_functions:
-                kw = {'fillcolor': '#ffcccc'}
-            else:
-                kw = {}
-            dotgen.emit_node(nameof(func), label=data, shape="box", **kw)
-        dotgen.emit_edge('entry', nameof(functions[0]), color="green")
-        for f1, f2 in translator.callgraph.itervalues():
-            dotgen.emit_edge(nameof(f1), nameof(f2))
-
-        # show the class hierarchy
-        if self.translator.annotator:
-            dotgen.emit_node(nameof(None), color="red", shape="octagon",
-                             label="Root Class\\nobject")
-            for classdef in self.translator.annotator.getuserclassdefinitions():
-                data = self.labelof(classdef, classdef.cls.__name__)
-                dotgen.emit_node(nameof(classdef), label=data, shape="box")
-                dotgen.emit_edge(nameof(classdef.basedef), nameof(classdef))
-        
-        GraphLayout.__init__(self, dotgen.generate(target='plain'))
-
-        # link the function names to the individual flow graphs
-        for name, obj in self.object_by_name.iteritems():
-            if isinstance(obj, ClassDef):
-                #data = '%s.%s' % (obj.cls.__module__, obj.cls.__name__)
-                data = repr(obj.cls)
-            else:
-                func = obj
-                try:
-                    source = inspect.getsource(func)
-                except IOError:   # e.g. when func is defined interactively
-                    source = func.func_name
-                data = '%s:%d\n%s' % (func.func_globals.get('__name__', '?'),
-                                      func.func_code.co_firstlineno,
-                                      source.split('\n')[0])
-            self.links[name] = data
-
-    def labelof(self, obj, objname):
-        name = objname
-        i = 1
-        while name in self.object_by_name:
-            i += 1
-            name = '%s__%d' % (objname, i)
-        self.object_by_name[name] = obj
-        self.name_by_object[obj] = name
-        return name
-
-    def followlink(self, name):
-        obj = self.object_by_name[name]
-        if isinstance(obj, ClassDef):
-            return ClassDefLayout(self.translator, obj)
-        else:
-            return FlowGraphLayout(self.translator, [obj], self.name_by_object)
-
-
-def nameof(obj, cache={}):
-    # NB. the purpose of the cache is not performance, but to ensure that
-    # two objects that compare equal get the same name
-    try:
-        return cache[obj]
-    except KeyError:
-        result = '%s__0x%x' % (getattr(obj, '__name__', ''), uid(obj))
-        cache[obj] = result
-        return result
-
-# ____________________________________________________________
-
-if __name__ == '__main__':
-    from pypy.translator.translator import Translator
-    from pypy.translator.test import snippet
-    from pypy.translator.tool.pygame.graphdisplay import GraphDisplay
-    
-    t = Translator(snippet.powerset)
-    #t.simplify()
-    a = t.annotate([int])
-    a.simplify()
-    GraphDisplay(FlowGraphLayout(t)).run()
-
-##    t = Translator(snippet._methodcall1)
-##    t.simplify()
-##    a = t.annotate([int])
-##    a.simplify()
-##    GraphDisplay(TranslatorLayout(t)).run()

Added: pypy/trunk/src/pypy/translator/tool/pygame/graphclient.py
==============================================================================
--- (empty file)
+++ pypy/trunk/src/pypy/translator/tool/pygame/graphclient.py	Sat Dec  4 13:37:58 2004
@@ -0,0 +1,79 @@
+#! /usr/bin/env python
+"""
+Client for a graph server (either in-process or over a socket).
+"""
+
+import autopath
+from pypy.translator.tool.pygame.drawgraph import GraphLayout
+from pypy.translator.tool.graphserver import send_msg, recv_msg, MissingPage
+from pypy.tool.udir import udir
+from py.process import cmdexec
+
+
+DOT_FILE   = udir.join('graph.dot')
+PLAIN_FILE = udir.join('graph.plain')
+
+
+class ClientGraphLayout(GraphLayout):
+
+    def __init__(self, connexion, key, dot, links, **ignored):
+        # generate a temporary .dot file and call dot on it
+        DOT_FILE.write(dot)
+        cmdexec('dot -Tplain %s>%s' % (str(DOT_FILE),str(PLAIN_FILE)))
+        GraphLayout.__init__(self, PLAIN_FILE)
+        self.connexion = connexion
+        self.key = key
+        self.links.update(links)
+
+    def followlink(self, name):
+        return self.connexion.download(self.key, name)
+
+    def reload(self):
+        return self.connexion.download(self.key)
+
+
+class InProcessConnexion:
+
+    def download(self, page, link=None):
+        if link is not None:
+            try:
+                page = page.content().followlink(link)
+            except KeyError:
+                page = MissingPage()
+        key = page
+        page = page.content()
+        return ClientGraphLayout(self, key, page.source, page.links)
+
+
+class SocketConnexion:
+
+    def __init__(self, s):
+        self.s = s
+
+    def download(self, key, link=None):
+        send_msg(self.s, (key, link))
+        data = recv_msg(self.s)
+        return ClientGraphLayout(self, **data)
+
+
+def get_layout(homepage):
+    return InProcessConnexion().download(homepage)
+
+def get_remote_layout(hostname, port=8888):
+    import socket
+    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    s.connect((hostname, port))
+    return SocketConnexion(s).download(0)
+
+
+if __name__ == '__main__':
+    import sys
+    if len(sys.argv) != 2 or sys.argv[1].count(':') != 1:
+        print >> sys.stderr, 'Connects to a graph server like goal/translate_pypy.'
+        print >> sys.stderr, 'Usage:  %s hostname:port' % (sys.argv[0],)
+        print >> sys.stderr, '   or   %s :port' % (sys.argv[0],)
+        sys.exit(2)
+    hostname, port = sys.argv[1].split(':')
+    port = int(port)
+    layout = get_remote_layout(hostname, port)
+    layout.display()

Modified: pypy/trunk/src/pypy/translator/tool/pygame/graphdisplay.py
==============================================================================
--- pypy/trunk/src/pypy/translator/tool/pygame/graphdisplay.py	(original)
+++ pypy/trunk/src/pypy/translator/tool/pygame/graphdisplay.py	Sat Dec  4 13:37:58 2004
@@ -89,6 +89,7 @@
         '/': 'search',
         'n': 'find_next',
         'p': 'find_prev',
+        'r': 'reload',
         'left' : ('pan', (-1, 0)),
         'right' : ('pan', (1, 0)),
         'up' : ('pan', (0, -1)),
@@ -114,6 +115,7 @@
         Backspace       Go back in history
         Meta Left       Go back in history
         Meta Right      Go forward in history
+        R               Reload the page
 
         F or /          Search for text
         N               Find next occurrence
@@ -388,6 +390,12 @@
             self.layout = self.viewer.graphlayout
             self.updated_viewer()
 
+    def reload(self):
+        self.setstatusbar('reloading...')
+        self.redraw_now()
+        newlayout = self.layout.reload()
+        self.setlayout(newlayout)
+
     def setstatusbar(self, text, fgcolor=None, bgcolor=None):
         info = (text, fgcolor or self.STATUSBAR_FGCOLOR, bgcolor or self.STATUSBAR_BGCOLOR)
         if info != self.statusbarinfo:
@@ -438,10 +446,13 @@
     def notifyclick(self, pos):
         word = self.viewer.at_position(pos)
         if word in self.layout.links:
+            self.setstatusbar('loading...')
+            self.redraw_now()
             newlayout = self.layout.followlink(word)
             if newlayout is not None:
                 self.setlayout(newlayout)
                 return
+            self.setstatusbar('')
         node = self.viewer.node_at_position(pos)
         if node:
             self.look_at_node(node)
@@ -539,12 +550,7 @@
                                      bumpscale*t*(1-t))
                 self.viewer.setcenter(cx1*(1-t) + cx2*t, cy1*(1-t) + cy2*t)
                 self.updated_viewer(keep_highlight=keep_highlight)
-                self.viewer.render()
-                if self.statusbarinfo:
-                    self.drawstatusbar()
-                else:
-                    self.status_bar_height = 0
-                pygame.display.flip()
+                self.redraw_now()
         return moving
 
     def peek(self, typ):
@@ -619,7 +625,16 @@
     
     def quit(self):
         raise StopIteration
-    
+
+    def redraw_now(self):
+        self.viewer.render()
+        if self.statusbarinfo:
+            self.drawstatusbar()
+        else:
+            self.status_bar_height = 0
+        pygame.display.flip()
+        self.must_redraw = False
+
     def run(self):
         self.dragging = self.click_origin = self.click_time = None
         events = self.events = []
@@ -628,13 +643,7 @@
             while True:
 
                 if self.must_redraw and not events:
-                    self.viewer.render()
-                    if self.statusbarinfo:
-                        self.drawstatusbar()
-                    else:
-                        self.status_bar_height = 0
-                    pygame.display.flip()
-                    self.must_redraw = False
+                    self.redraw_now()
 
                 if not events:
                     events.append(pygame.event.wait())

Modified: pypy/trunk/src/pypy/translator/translator.py
==============================================================================
--- pypy/trunk/src/pypy/translator/translator.py	(original)
+++ pypy/trunk/src/pypy/translator/translator.py	Sat Dec  4 13:37:58 2004
@@ -108,8 +108,8 @@
     def view(self, *functions):
         """Shows the control flow graph with annotations if computed.
         Requires 'dot' and pygame."""
-        from pypy.translator.tool.pygame.flowviewer import FlowGraphLayout
-        FlowGraphLayout(self).display()
+        from pypy.translator.tool.graphpage import FlowGraphPage
+        FlowGraphPage(self).display()
 
     def simplify(self, func=None):
         """Simplifies the control flow graph (default: for all functions)."""



More information about the Pypy-commit mailing list