[pypy-svn] r5973 - in pypy/trunk/src: goal goal/translate_pypy pypy/translator pypy/translator/tool pypy/translator/tool/pygame

arigo at codespeak.net arigo at codespeak.net
Sun Aug 15 23:27:26 CEST 2004


Author: arigo
Date: Sun Aug 15 23:27:22 2004
New Revision: 5973

Added:
   pypy/trunk/src/goal/translate_pypy.py
      - copied, changed from r5957, pypy/trunk/src/goal/translate_pypy/translate_pypy.py
   pypy/trunk/src/pypy/translator/tool/pygame/flowviewer.py   (contents, props changed)
   pypy/trunk/src/pypy/translator/tool/pygame/graphdisplay.py
      - copied, changed from r5966, pypy/trunk/src/pypy/translator/tool/pygame/graphviewer.py
Removed:
   pypy/trunk/src/goal/translate_pypy/
   pypy/trunk/src/pypy/translator/tool/pygame/graphviewer.py
Modified:
   pypy/trunk/src/pypy/translator/annrpython.py
   pypy/trunk/src/pypy/translator/tool/make_dot.py
   pypy/trunk/src/pypy/translator/tool/pygame/drawgraph.py   (contents, props changed)
   pypy/trunk/src/pypy/translator/translator.py
Log:
Got rid of the HTML code generation, to use the Pygame-based graph viewer
instead.  All data is now viewed as dot graphs.  This has the interesting
effect that it is more natural to view the list of all functions as a call
graph.

M    pypy/translator/translator.py
M    pypy/translator/annrpython.py

       Records who-calls-who information.

AM   pypy/translator/tool/pygame/flowviewer.py

       Generates graphs from a Translator instance: call graphs,
       class hierarchy, and (using make_dot) individual flow
       graphs.

MM   pypy/translator/tool/pygame/drawgraph.py
D    pypy/translator/tool/pygame/graphviewer.py
A    pypy/translator/tool/pygame/graphdisplay.py

       Hyperlinks between graphs: clicking on a highlighted word
       can point the graph displayer to another graph.  The Left arrow
       key goes back.

M    pypy/translator/tool/make_dot.py

       Divided the class DotGen into a general-purpose class DotGen and
       a walker class FlowGraphDotGen for flow graphs specifically.

A    goal/translate_pypy.py
D    goal/translate_pypy

       Deleted the whole HTML/HTTP server code.  (It's still in the history
       if needed.)


Copied: pypy/trunk/src/goal/translate_pypy.py (from r5957, pypy/trunk/src/goal/translate_pypy/translate_pypy.py)
==============================================================================
--- pypy/trunk/src/goal/translate_pypy/translate_pypy.py	(original)
+++ pypy/trunk/src/goal/translate_pypy.py	Sun Aug 15 23:27:22 2004
@@ -6,7 +6,6 @@
 from pypy.objspace.std.objspace import StdObjSpace, W_Object
 from pypy.objspace.std.intobject import W_IntObject
 from pypy.translator.translator import Translator
-from pypy.translator.annrpython import AnnotatorError
 
 
 # __________  Entry point  __________
@@ -45,15 +44,15 @@
             return
         print "don't know about", x
 
-    def run_server(background=False, port=8000):
-        import graphserver
-        server = graphserver.Server(t)
-        print >> sys.stderr, '* View the blocks at http://127.0.0.1:%d/' % port
+    def run_server(background=False):
+        from pypy.translator.tool.pygame.flowviewer import TranslatorLayout
+        from pypy.translator.tool.pygame.graphdisplay import GraphDisplay
+        display = GraphDisplay(TranslatorLayout(t))
         if background:
             import thread
-            thread.start_new_thread(server.serve, ())
+            thread.start_new_thread(display.run, ())
         else:
-            server.serve()
+            display.run()
 
     t = Translator(entry_point, verbose=True, simplifying=True)
     try:
@@ -77,4 +76,10 @@
         import pdb
         pdb.post_mortem(tb)
     else:
+##        print '-'*60
+##        src = t.pyrex()
+##        g = open('code.pyx', 'w')
+##        g.write(src)
+##        print 'Wrote', g.name
+##        g.close()
         run_server()

Modified: pypy/trunk/src/pypy/translator/annrpython.py
==============================================================================
--- pypy/trunk/src/pypy/translator/annrpython.py	(original)
+++ pypy/trunk/src/pypy/translator/annrpython.py	Sun Aug 15 23:27:22 2004
@@ -20,7 +20,7 @@
 
     def __init__(self, translator=None):
         self.translator = translator
-        self.pendingblocks = []  # list of (block, list-of-SomeValues-args)
+        self.pendingblocks = []  # list of (fn, block, list-of-SomeValues-args)
         self.bindings = {}       # map Variables to SomeValues
         self.annotated = {}      # set of blocks already seen
         self.notify = {}         # {block: {factory-to-invalidate-when-done}}
@@ -29,16 +29,17 @@
 
     #___ convenience high-level interface __________________
 
-    def build_types(self, func_or_flowgraph, input_arg_types):
+    def build_types(self, func_or_flowgraph, input_arg_types, func=None):
         """Recursively build annotations about the specific entry point."""
         if isinstance(func_or_flowgraph, FunctionGraph):
             flowgraph = func_or_flowgraph
         else:
+            func = func_or_flowgraph
             if self.translator is None:
                 from pypy.translator.translator import Translator
-                self.translator = Translator(func_or_flowgraph)
+                self.translator = Translator(func)
                 self.translator.annotator = self
-            flowgraph = self.translator.getflowgraph(func_or_flowgraph)
+            flowgraph = self.translator.getflowgraph(func)
         # make input arguments and set their type
         input_arg_types = list(input_arg_types)
         nbarg = len(flowgraph.getargs())
@@ -51,7 +52,7 @@
             inputcells.append(t)
         
         # register the entry point
-        self.addpendingblock(flowgraph.startblock, inputcells)
+        self.addpendingblock(func, flowgraph.startblock, inputcells)
         # recursively proceed until no more pending block is left
         self.complete()
         return self.binding(flowgraph.getreturnvar())
@@ -90,19 +91,20 @@
 
     #___ medium-level interface ____________________________
 
-    def addpendingblock(self, block, cells):
+    def addpendingblock(self, fn, block, cells):
         """Register an entry point into block with the given input cells."""
+        assert fn in self.translator.flowgraphs
         for a in cells:
             assert isinstance(a, annmodel.SomeObject)
-        self.pendingblocks.append((block, cells))
+        self.pendingblocks.append((fn, block, cells))
 
     def complete(self):
         """Process pending blocks until none is left."""
         while self.pendingblocks:
             # XXX don't know if it is better to pop from the head or the tail.
             # but suspect from the tail is better in the new Factory model.
-            block, cells = self.pendingblocks.pop()
-            self.processblock(block, cells)
+            fn, block, cells = self.pendingblocks.pop()
+            self.processblock(fn, block, cells)
         if False in self.annotated.values():
             raise AnnotatorError('%d blocks are still blocked' %
                                  self.annotated.values().count(False))
@@ -130,7 +132,9 @@
     #___ interface for annotator.factory _______
 
     def recursivecall(self, func, factory, *args):
-        graph = self.translator.getflowgraph(func)
+        parent_fn, parent_block, parent_index = factory.position_key
+        graph = self.translator.getflowgraph(func, parent_fn,
+                                             factory.position_key)
         # self.notify[graph.returnblock] is a dictionary of
         # FuncCallFactories (call points to this func) which triggers a
         # reflow whenever the return block of this graph has been analysed.
@@ -168,14 +172,14 @@
             for extra in func.func_defaults[-missingargs:]:
                 inputcells.append(annmodel.immutablevalue(extra))
         inputcells.extend(extracells)
-        self.addpendingblock(block, inputcells)
+        self.addpendingblock(func, block, inputcells)
         # get the (current) return value
         v = graph.getreturnvar()
         return self.bindings.get(v, annmodel.SomeImpossibleValue())
 
     def reflowfromposition(self, position_key):
-        block, index = position_key
-        self.reflowpendingblock(block)
+        fn, block, index = position_key
+        self.reflowpendingblock(fn, block)
 
 
     #___ simplification (should be moved elsewhere?) _______
@@ -213,7 +217,7 @@
 
     #___ flowing annotations in blocks _____________________
 
-    def processblock(self, block, cells):
+    def processblock(self, fn, block, cells):
         # Important: this is not called recursively.
         # self.flowin() can only issue calls to self.addpendingblock().
         # The analysis of a block can be in three states:
@@ -234,7 +238,7 @@
         if not self.annotated[block]:
             self.annotated[block] = True
             try:
-                self.flowin(block)
+                self.flowin(fn, block)
             except BlockedInference, e:
                 #print '_'*60
                 #print 'Blocked at %r:' % (e.break_at,)
@@ -247,8 +251,8 @@
                     setattr(e, '__annotator_block', block)
                 raise
 
-    def reflowpendingblock(self, block):
-        self.pendingblocks.append((block, None))
+    def reflowpendingblock(self, fn, block):
+        self.pendingblocks.append((fn, block, None))
         assert block in self.annotated
         self.annotated[block] = False  # must re-flow
 
@@ -267,17 +271,17 @@
         if unions != oldcells:
             self.bindinputargs(block, unions)
 
-    def flowin(self, block):
+    def flowin(self, fn, block):
         #print 'Flowing', block, [self.binding(a) for a in block.inputargs]
         for i in range(len(block.operations)):
             try:
-                self.bookkeeper.enter((block, i))
+                self.bookkeeper.enter((fn, block, i))
                 self.consider_op(block.operations[i])
             finally:
                 self.bookkeeper.leave()
         for link in block.exits:
             cells = [self.binding(a) for a in link.args]
-            self.addpendingblock(link.target, cells)
+            self.addpendingblock(fn, link.target, cells)
         if block in self.notify:
             # invalidate some factories when this block is done
             for factory in self.notify[block]:

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	Sun Aug 15 23:27:22 2004
@@ -9,49 +9,40 @@
 from pypy.tool.udir import udir
 from std.process import cmdexec
 
-debug = 0
-
 class DotGen:
-    def get_source(self, funcgraph):
-        content = "\n".join(self.lines)
-        return """
-digraph test { 
-node [fontname=Times];
-edge [fontname=Times];
-%(content)s
-}""" % locals()
-
-    def getsubgraph(self, name, node):
-        self.blocks = {}
+    
+    def __init__(self, graphname):
+        self.graphname = graphname
         self.lines = []
-        self.prefix = name
-        traverse(self, node)
-        content = "\n".join(self.lines)
-        return "subgraph %s {\n%s}" % (name, content) 
-
-    def getdigraph(self, name, node):
-        self.blocks = {}
-        self.lines = []
-        self.prefix = name
-        traverse(self, node)
-        content = "\n".join(self.lines)
-        return "digraph %s {\n%s}" % (name, content) 
-
-    def getgraph(self, name, subgraphlist):
-        content = "\n".join(subgraphlist)
-        return "digraph %s {\n%s}" % (name, content)
+        self.source = None
+        self.emit("digraph %s {" % graphname)
 
-    def blockname(self, block):
-        i = id(block)
-        try:
-            return self.blocks[i]
-        except KeyError:
-            self.blocks[i] = name = "%s_%d" % (self.prefix, len(self.blocks))
-            return name
+    def generate(self, storedir=None, target='ps'):
+        source = self.get_source()
+        if storedir is None:
+            storedir = udir
+        pdot = storedir.join('%s.dot' % self.graphname)
+        pdot.write(source)
+        ptarget = pdot.new(ext=target)
+        cmdexec('dot -T%s %s>%s' % (target, str(pdot),str(ptarget)))
+        return ptarget
+
+    def get_source(self):
+        if self.source is None:
+            self.emit("}")
+            self.source = '\n'.join(self.lines)
+            del self.lines
+        return self.source
 
     def emit(self, line):
         self.lines.append(line)
 
+    def enter_subgraph(self, name):
+        self.emit("subgraph %s {" % (name,))
+
+    def leave_subgraph(self):
+        self.emit("}")
+
     def emit_edge(self, name1, name2, label="", 
                   style="dashed", 
                   color="black", 
@@ -59,7 +50,8 @@
                   weight="5",
                   ):
         d = locals()
-        attrs = [('%s="%s"' % (x, d[x])) for x in d if isinstance(x, str)]
+        attrs = [('%s="%s"' % (x, d[x]))
+                 for x in ['label', 'style', 'color', 'dir', 'weight']]
         self.emit('edge [%s];' % ", ".join(attrs))
         self.emit('%s -> %s' % (name1, name2))
 
@@ -71,9 +63,28 @@
                   style="filled",
                   ):
         d = locals()
-        attrs = [('%s="%s"' % (x, d[x])) for x in d if isinstance(x, str)]
+        attrs = [('%s="%s"' % (x, d[x]))
+                 for x in ['shape', 'label', 'color', 'fillcolor', 'style']]
         self.emit('%s [%s];' % (name, ", ".join(attrs)))
 
+
+class FlowGraphDotGen(DotGen):
+
+    def emit_subgraph(self, name, node):
+        self.blocks = {}
+        self.prefix = name
+        self.enter_subgraph(name)
+        traverse(self, node)
+        self.leave_subgraph()
+
+    def blockname(self, block):
+        i = id(block)
+        try:
+            return self.blocks[i]
+        except KeyError:
+            self.blocks[i] = name = "%s_%d" % (self.prefix, len(self.blocks))
+            return name
+
     def visit(self, obj):
         # ignore for now 
         return
@@ -138,14 +149,7 @@
     os.system('gv %s' % fn)
 
 def make_dot_graphs(basefilename, graphs, storedir=None, target='ps'):
-    if storedir is None:
-        storedir = udir
-
-    dotgen = DotGen()
-    dest = storedir.join('%s.dot' % basefilename)
-    #dest = storedir.dirname().join('%s.dot' % name)
-    subgraphs = []
-    basefilename = '_'+basefilename
+    dotgen = FlowGraphDotGen(basefilename)
     names = {basefilename: True}
     for graphname, graph in graphs:
         if graphname in names:
@@ -154,17 +158,8 @@
                 i += 1
             graphname = graphname + str(i)
         names[graphname] = True
-        subgraphs.append(dotgen.getsubgraph(graphname, graph))
-    source = dotgen.getgraph(basefilename, subgraphs)
-    #print source
-    dest.write(source)
-    psdest = dest.new(ext=target)
-    out = cmdexec('dot -T%s %s>%s' % (target, str(dest),str(psdest)))
-## This is the old code, which doesnt work on binary files on windows    
-##    out = cmdexec('dot -T%s %s' % (target, str(dest)))
-##    psdest.write(out)
-    #print "wrote", psdest
-    return psdest
+        dotgen.emit_subgraph(graphname, graph)
+    return dotgen.generate(storedir, target)
 
 
 if __name__ == '__main__':

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	Sun Aug 15 23:27:22 2004
@@ -24,7 +24,7 @@
 
     def __init__(self, filename):
         # parse the layout file (.plain format)
-        lines = open(filename, 'r').readlines()
+        lines = open(str(filename), 'r').readlines()
         for i in range(len(lines)-2, -1, -1):
             if lines[i].endswith('\\\n'):   # line ending in '\'
                 lines[i] = lines[i][:-2] + lines[i+1]
@@ -44,6 +44,7 @@
                 self.edges.append(Edge(self.nodes, *line[1:]))
             if line[0] == 'stop':
                 break
+        self.links = {}
 
 class Node:
     def __init__(self, name, x, y, w, h, label, style, shape, color, fillcolor):
@@ -441,13 +442,3 @@
         for item in lst:
             total += lst
         return total
-
-
-def build_layout(graphs, name=None):
-    """ Build a GraphLayout from a list of control flow graphs.
-    """
-    from pypy.translator.tool.make_dot import make_dot_graphs
-    name = name or graphs[0].name
-    gs = [(graph.name, graph) for graph in graphs]
-    fn = make_dot_graphs(name, gs, target='plain')
-    return GraphLayout(str(fn))

Added: pypy/trunk/src/pypy/translator/tool/pygame/flowviewer.py
==============================================================================
--- (empty file)
+++ pypy/trunk/src/pypy/translator/tool/pygame/flowviewer.py	Sun Aug 15 23:27:22 2004
@@ -0,0 +1,176 @@
+import autopath
+import inspect
+from 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, factory
+
+
+class FlowGraphLayout(GraphLayout):
+    """ A GraphLayout showing a Flow Graph (or a few flow graphs).
+    """
+    def __init__(self, translator, functions=None):
+        from pypy.translator.tool.make_dot import make_dot_graphs
+        self.translator = translator
+        self.annotator = translator.annotator
+        functions = functions or translator.functions
+        graphs = [translator.getflowgraph(func) for func in functions]
+        gs = [(graph.name, graph) for graph in graphs]
+        fn = make_dot_graphs(graphs[0].name, gs, target='plain')
+        GraphLayout.__init__(self, fn)
+        # make the dictionary of links -- one per annotated variable
+        self.binding_history = {}
+        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
+            for var, history in self.annotator.bindingshistory.items():
+                self.binding_history[var.name] = history
+
+    def followlink(self, varname):
+        # clicking on a variable name shows its binding history
+        dotgen = DotGen('binding')
+        data = "Most recent binding\\n\\n%s" % nottoowide(self.links[varname])
+        dotgen.emit_node('0', shape="box", color="red", label=data)
+        history = list(self.binding_history.get(varname, []))
+        history.reverse()
+        for n, data in zip(range(len(history)), history):
+            dotgen.emit_node(str(n+1), shape="box", label=nottoowide(data))
+            dotgen.emit_edge(str(n+1), str(n))
+        return GraphLayout(dotgen.generate(target='plain'))
+
+
+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__)
+
+        def writecdef(cdef):
+            dotgen.emit_node(nameof(cdef), color="red", shape="octagon",
+                             label=repr(cdef.cls))
+            attrs = cdef.attrs.items()
+            attrs.sort()
+            for name, s_value in attrs:
+                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 = {}
+        dotgen = DotGen('translator')
+        
+        # show the call graph
+        functions = translator.functions
+        dotgen.emit_node('entry', fillcolor="green", shape="octagon",
+                         label="Translator\\nEntry Point")
+        for func in functions:
+            data = self.labelof(func, func.func_name)
+            dotgen.emit_node(nameof(func), label=data, shape="box")
+        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, factory.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  %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
+        return name
+
+    def followlink(self, name):
+        obj = self.object_by_name[name]
+        if isinstance(obj, factory.ClassDef):
+            return ClassDefLayout(self.translator, obj)
+        else:
+            return FlowGraphLayout(self.translator, [obj])
+
+
+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__', ''), id(obj))
+        cache[obj] = result
+        return result
+
+# ____________________________________________________________
+
+if __name__ == '__main__':
+    from pypy.translator.translator import Translator
+    from pypy.translator.test import snippet
+    from 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()

Copied: pypy/trunk/src/pypy/translator/tool/pygame/graphdisplay.py (from r5966, pypy/trunk/src/pypy/translator/tool/pygame/graphviewer.py)
==============================================================================
--- pypy/trunk/src/pypy/translator/tool/pygame/graphviewer.py	(original)
+++ pypy/trunk/src/pypy/translator/tool/pygame/graphdisplay.py	Sun Aug 15 23:27:22 2004
@@ -1,9 +1,9 @@
 from __future__ import generators
 import autopath
-import sys, os, re, time
+import os, time
 import pygame
 from pygame.locals import *
-from drawgraph import GraphRenderer, build_layout
+from drawgraph import GraphRenderer
 
 
 class Display(object):
@@ -20,24 +20,20 @@
 
 class GraphDisplay(Display):
     STATUSBARFONT = os.path.join(autopath.this_dir, 'VeraMoBd.ttf')
-    SCALE = 60
     ANIM_STEP = 0.07
 
-    def __init__(self, translator, functions=None):
+    def __init__(self, layout):
         super(GraphDisplay, self).__init__()
-        self.translator = translator
-        self.annotator = translator.annotator
         self.font = pygame.font.Font(self.STATUSBARFONT, 16)
-
-        self.variables_by_name = {}
-        if self.annotator:
-            for var in self.annotator.bindings:
-                self.variables_by_name[var.name] = var
-
-        functions = functions or self.translator.functions
-        graphs = [self.translator.getflowgraph(func) for func in functions]
-        layout = build_layout(graphs)
-        self.viewer = GraphRenderer(self.screen, layout, self.SCALE)
+        self.viewers_history = []
+        self.viewer = None
+        self.setlayout(layout)
+
+    def setlayout(self, layout):
+        if self.viewer:
+            self.viewers_history.append(self.viewer)
+        self.layout = layout
+        self.viewer = GraphRenderer(self.screen, layout)
         # center and scale to view the whole graph
         self.viewer.setoffset((self.viewer.width - self.width) // 2,
                               (self.viewer.height - self.height) // 2)
@@ -45,10 +41,24 @@
                 float(self.height-40) / self.viewer.height)
         if f < 1.0:
             self.viewer.shiftscale(f)
+        self.updated_viewer()
+
+    def updated_viewer(self):
         self.sethighlight()
         self.statusbarinfo = None
         self.must_redraw = True
-        self.setstatusbar('Click to move around, or drag mouse buttons (left to zoom, right to scroll)')
+        if self.viewers_history:
+            info = 'Press Left Arrow to go back to previous screen'
+        else:
+            info = ('Click to move around, or drag mouse buttons '
+                    '(left to zoom, right to scroll)')
+        self.setstatusbar(info)
+
+    def layout_back(self):
+        if self.viewers_history:
+            self.viewer = self.viewers_history.pop()
+            self.layout = self.viewer.graphlayout
+            self.updated_viewer()
 
     def setstatusbar(self, text, fgcolor=(255,255,80), bgcolor=(128,0,0)):
         info = (text, fgcolor, bgcolor)
@@ -86,16 +96,30 @@
 
     def notifymousepos(self, pos):
         word = self.viewer.at_position(pos)
-        if word in self.variables_by_name:
-            var = self.variables_by_name[word]
-            s_value = self.annotator.binding(var)
-            info = '%s: %s' % (var.name, s_value)
+        if word in self.layout.links:
+            info = self.layout.links[word]
             self.setstatusbar(info)
             self.sethighlight(word)
 
+    def notifyclick(self, pos):
+        word = self.viewer.at_position(pos)
+        if word in self.layout.links:
+            newlayout = self.layout.followlink(word)
+            if newlayout is not None:
+                self.setlayout(newlayout)
+                return
+        node = self.viewer.node_at_position(pos)
+        if node:
+            self.look_at_node(node)
+        else:
+            edge = self.viewer.edge_at_position(pos)
+            if edge:
+                if not self.look_at_node(edge.head):
+                    self.look_at_node(edge.tail)
+
     def sethighlight(self, word=None):
         self.viewer.highlightwords = {}
-        for name in self.variables_by_name:
+        for name in self.layout.links:
             self.viewer.highlightwords[name] = ((128,0,0), None)
         if word:
             self.viewer.highlightwords[word] = ((255,255,80), (128,0,0))
@@ -129,7 +153,7 @@
             # if the target is far off the window, reduce scale along the way
             tx, ty = self.viewer.map(cx2, cy2)
             offview = max(-tx, -ty, tx-self.width, ty-self.height)
-            middlescale = endscale - 0.06 * offview
+            middlescale = endscale * (0.999 ** offview)
             if offview > 150 and middlescale < startscale:
                 bumpscale = 4.0 * (middlescale - 0.5*(startscale+endscale))
             else:
@@ -182,16 +206,12 @@
                 pygame.event.set_grab(False)
                 if click_time is not None and abs(time.time() - click_time) < 1:
                     # click (no significant dragging)
-                    node = self.viewer.node_at_position(click_origin)
-                    if node:
-                        self.look_at_node(node)
-                    else:
-                        edge = self.viewer.edge_at_position(click_origin)
-                        if edge:
-                            if not self.look_at_node(edge.head):
-                                self.look_at_node(edge.tail)
+                    self.notifyclick(click_origin)
                 click_time = None
                 self.notifymousepos(event.pos)
+            if event.type == KEYDOWN:
+                if event.key in [K_p, K_LEFT, K_BACKSPACE]:
+                    self.layout_back()
             if event.type == VIDEORESIZE:
                 # short-circuit if there are more resize events pending
                 if pygame.event.peek([VIDEORESIZE]):
@@ -203,14 +223,3 @@
         # cannot safely close and re-open the display, depending on
         # Pygame version and platform.
         pygame.display.set_mode((self.width,1))
-
-
-if __name__ == '__main__':
-    from pypy.translator.translator import Translator
-    from pypy.translator.test import snippet
-    
-    t = Translator(snippet.powerset)
-    #t.simplify()
-    a = t.annotate([int])
-    a.simplify()
-    GraphDisplay(t).run()

Deleted: /pypy/trunk/src/pypy/translator/tool/pygame/graphviewer.py
==============================================================================
--- /pypy/trunk/src/pypy/translator/tool/pygame/graphviewer.py	Sun Aug 15 23:27:22 2004
+++ (empty file)
@@ -1,216 +0,0 @@
-from __future__ import generators
-import autopath
-import sys, os, re, time
-import pygame
-from pygame.locals import *
-from drawgraph import GraphRenderer, build_layout
-
-
-class Display(object):
-    
-    def __init__(self, (w,h)=(800,740)):
-        pygame.init()
-        self.resize((w,h))
-
-    def resize(self, (w,h)):
-        self.width = w
-        self.height = h
-        self.screen = pygame.display.set_mode((w, h), HWSURFACE|RESIZABLE)
-
-
-class GraphDisplay(Display):
-    STATUSBARFONT = os.path.join(autopath.this_dir, 'VeraMoBd.ttf')
-    SCALE = 60
-    ANIM_STEP = 0.07
-
-    def __init__(self, translator, functions=None):
-        super(GraphDisplay, self).__init__()
-        self.translator = translator
-        self.annotator = translator.annotator
-        self.font = pygame.font.Font(self.STATUSBARFONT, 16)
-
-        self.variables_by_name = {}
-        if self.annotator:
-            for var in self.annotator.bindings:
-                self.variables_by_name[var.name] = var
-
-        functions = functions or self.translator.functions
-        graphs = [self.translator.getflowgraph(func) for func in functions]
-        layout = build_layout(graphs)
-        self.viewer = GraphRenderer(self.screen, layout, self.SCALE)
-        # center and scale to view the whole graph
-        self.viewer.setoffset((self.viewer.width - self.width) // 2,
-                              (self.viewer.height - self.height) // 2)
-        f = min(float(self.width-40) / self.viewer.width,
-                float(self.height-40) / self.viewer.height)
-        if f < 1.0:
-            self.viewer.shiftscale(f)
-        self.sethighlight()
-        self.statusbarinfo = None
-        self.must_redraw = True
-        self.setstatusbar('Click to move around, or drag mouse buttons (left to zoom, right to scroll)')
-
-    def setstatusbar(self, text, fgcolor=(255,255,80), bgcolor=(128,0,0)):
-        info = (text, fgcolor, bgcolor)
-        if info != self.statusbarinfo:
-            self.statusbarinfo = info
-            self.must_redraw = True
-
-    def drawstatusbar(self):
-        text, fgcolor, bgcolor = self.statusbarinfo
-        words = text.split(' ')
-        lines = []
-        totalh = 0
-        while words:
-            line = words.pop(0)
-            img = self.font.render(line, 1, fgcolor)
-            while words:
-                longerline = line + ' ' + words[0]
-                longerimg = self.font.render(longerline, 1, fgcolor)
-                w, h = longerimg.get_size()
-                if w > self.width:
-                    break
-                words.pop(0)
-                line = longerline
-                img = longerimg
-            lines.append(img)
-            w, h = img.get_size()
-            totalh += h
-        
-        y = self.height - totalh
-        self.screen.fill(bgcolor, (0, y-16, self.width, totalh+16))
-        for img in lines:
-            w, h = img.get_size()
-            self.screen.blit(img, ((self.width-w)//2, y-8))
-            y += h
-
-    def notifymousepos(self, pos):
-        word = self.viewer.at_position(pos)
-        if word in self.variables_by_name:
-            var = self.variables_by_name[word]
-            s_value = self.annotator.binding(var)
-            info = '%s: %s' % (var.name, s_value)
-            self.setstatusbar(info)
-            self.sethighlight(word)
-
-    def sethighlight(self, word=None):
-        self.viewer.highlightwords = {}
-        for name in self.variables_by_name:
-            self.viewer.highlightwords[name] = ((128,0,0), None)
-        if word:
-            self.viewer.highlightwords[word] = ((255,255,80), (128,0,0))
-
-    def animation(self, expectedtime=0.6):
-        start = time.time()
-        step = 0.0
-        n = 0
-        while True:
-            step += self.ANIM_STEP
-            if step >= expectedtime:
-                break
-            yield step / expectedtime
-            n += 1
-            now = time.time()
-            frametime = (now-start) / n
-            self.ANIM_STEP = self.ANIM_STEP * 0.9 + frametime * 0.1
-        yield 1.0
-
-    def look_at_node(self, node):
-        """Shift the node in view."""
-        endscale = min(float(self.width-40) / node.w,
-                       float(self.height-40) / node.h,
-                       75)
-        startscale = self.viewer.scale
-        cx1, cy1 = self.viewer.getcenter()
-        cx2, cy2 = node.x, node.y
-        moving = (abs(startscale-endscale) + abs(cx1-cx2) + abs(cy1-cy2)
-                  > 0.4)
-        if moving:
-            # if the target is far off the window, reduce scale along the way
-            tx, ty = self.viewer.map(cx2, cy2)
-            offview = max(-tx, -ty, tx-self.width, ty-self.height)
-            middlescale = endscale - 0.06 * offview
-            if offview > 150 and middlescale < startscale:
-                bumpscale = 4.0 * (middlescale - 0.5*(startscale+endscale))
-            else:
-                bumpscale = 0.0
-            self.statusbarinfo = None
-            self.sethighlight(None)
-            for t in self.animation():
-                self.viewer.setscale(startscale*(1-t) + endscale*t +
-                                     bumpscale*t*(1-t))
-                self.viewer.setcenter(cx1*(1-t) + cx2*t, cy1*(1-t) + cy2*t)
-                self.viewer.render()
-                pygame.display.flip()
-        return moving
-
-    def run(self):
-        dragging = click_origin = click_time = None
-        while 1:
-            if self.must_redraw:
-                self.viewer.render()
-                if self.statusbarinfo:
-                    self.drawstatusbar()
-                pygame.display.flip()
-                self.must_redraw = False
-            
-            event = pygame.event.wait()
-            if event.type == MOUSEMOTION:
-                # short-circuit if there are more motion events pending
-                if pygame.event.peek([MOUSEMOTION]):
-                    continue
-                if dragging:
-                    if (abs(event.pos[0] - click_origin[0]) +
-                        abs(event.pos[1] - click_origin[1])) > 12:
-                        click_time = None
-                    dx = event.pos[0] - dragging[0]
-                    dy = event.pos[1] - dragging[1]
-                    if event.buttons[0]:   # left mouse button
-                        self.viewer.shiftscale(1.003 ** dy)
-                    else:
-                        self.viewer.shiftoffset(-2*dx, -2*dy)
-                    dragging = event.pos
-                    self.must_redraw = True
-                else:
-                    self.notifymousepos(event.pos)
-            if event.type == MOUSEBUTTONDOWN:
-                dragging = click_origin = event.pos
-                click_time = time.time()
-                pygame.event.set_grab(True)
-            if event.type == MOUSEBUTTONUP:
-                dragging = None
-                pygame.event.set_grab(False)
-                if click_time is not None and abs(time.time() - click_time) < 1:
-                    # click (no significant dragging)
-                    node = self.viewer.node_at_position(click_origin)
-                    if node:
-                        self.look_at_node(node)
-                    else:
-                        edge = self.viewer.edge_at_position(click_origin)
-                        if edge:
-                            if not self.look_at_node(edge.head):
-                                self.look_at_node(edge.tail)
-                click_time = None
-                self.notifymousepos(event.pos)
-            if event.type == VIDEORESIZE:
-                # short-circuit if there are more resize events pending
-                if pygame.event.peek([VIDEORESIZE]):
-                    continue
-                self.resize(event.size)
-                self.must_redraw = True
-            if event.type == QUIT:
-                break
-        # cannot safely close and re-open the display, depending on
-        # Pygame version and platform.
-        pygame.display.set_mode((self.width,1))
-
-
-if __name__ == '__main__':
-    from pypy.translator.translator import Translator
-    from pypy.translator.test import snippet
-    
-    t = Translator(snippet.powerset)
-    #t.simplify()
-    a = t.annotate([int])
-    a.simplify()
-    GraphDisplay(t).run()

Modified: pypy/trunk/src/pypy/translator/translator.py
==============================================================================
--- pypy/trunk/src/pypy/translator/translator.py	(original)
+++ pypy/trunk/src/pypy/translator/translator.py	Sun Aug 15 23:27:22 2004
@@ -54,9 +54,10 @@
         self.annotator = None
         self.flowgraphs = {}  # {function: graph}
         self.functions = []   # the keys of self.flowgraphs, in creation order
+        self.callgraph = {}   # {opaque_tag: (caller, callee)}
         self.getflowgraph()
 
-    def getflowgraph(self, func=None):
+    def getflowgraph(self, func=None, called_by=None, call_tag=None):
         """Get the flow graph for a function (default: the entry point)."""
         func = func or self.entrypoint
         try:
@@ -84,6 +85,8 @@
                 graph.source = inspect.getsource(func)
             except IOError:
                 pass  # e.g. when func is defined interactively
+        if called_by:
+            self.callgraph[called_by, func, call_tag] = called_by, func
         return graph
 
     def gv(self, func=None):
@@ -127,7 +130,7 @@
         if self.annotator is None:
             self.annotator = RPythonAnnotator(self)
         graph = self.getflowgraph(func)
-        self.annotator.build_types(graph, input_args_types)
+        self.annotator.build_types(graph, input_args_types, func)
         return self.annotator
 
     def source(self, func=None):
@@ -182,7 +185,7 @@
         g = gencls(graph)
         g.by_the_way_the_function_was = func   # XXX
         if input_arg_types is not None:
-            ann.build_types(graph, input_arg_types)
+            ann.build_types(graph, input_arg_types, func)
         if ann is not None:
             g.setannotator(ann)
         return g.emitcode()



More information about the Pypy-commit mailing list