[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