[pypy-svn] r9724 - pypy/dist/pypy/tool
pedronis at codespeak.net
pedronis at codespeak.net
Thu Mar 10 14:02:36 CET 2005
Author: pedronis
Date: Thu Mar 10 14:02:36 2005
New Revision: 9724
Added:
pypy/dist/pypy/tool/delta.css (contents, props changed)
pypy/dist/pypy/tool/delta.js (contents, props changed)
pypy/dist/pypy/tool/delta.py (contents, props changed)
Log:
tool to generate summary pages about implementation status of (builtin) modules, classes and types.
We propably need to pick a canonical set of modules we really care about, and then publish the results
consistently.
Added: pypy/dist/pypy/tool/delta.css
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/tool/delta.css Thu Mar 10 14:02:36 2005
@@ -0,0 +1,17 @@
+body { font-family: arial, helvetica, sans-serif; }
+
+.title {font-size: 2em; font-weight: bolder }
+
+.even { background: #d0d0d0; }
+.odd { background: white; }
+
+table { border-spacing: 0pt }
+
+a { color: inherit; }
+a:hover { background: yellow }
+a:visited:hover { background: inherit }
+
+.missing { color: red }
+.faked { color: red; font-style: italic }
+.somemissing { color: maroon }
+.only_in { font-style: italic }
\ No newline at end of file
Added: pypy/dist/pypy/tool/delta.js
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/tool/delta.js Thu Mar 10 14:02:36 2005
@@ -0,0 +1,15 @@
+
+
+toggle_list = ['alpha', 'grandmissing', 'incompleteness'];
+TABLE_HIDE = "display: none"
+TABLE_SHOW = "display: table"
+
+function toggle(on) {
+ for (i in toggle_list) {
+ x = toggle_list[i];
+ if (x!=on) {
+ document.getElementById(x).setAttribute("style", TABLE_HIDE);
+ }
+ };
+ document.getElementById(on).setAttribute("style", TABLE_SHOW);
+}
\ No newline at end of file
Added: pypy/dist/pypy/tool/delta.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/tool/delta.py Thu Mar 10 14:02:36 2005
@@ -0,0 +1,712 @@
+import autopath
+from pypy.interpreter.error import OperationError
+
+from py.xml import html
+
+class html_plus(html):
+ __tagspec__ = html.__tagspec__.copy()
+
+ __tagspec__['button'] = 1
+ __tagspec__['script'] = 1
+
+html = html_plus
+
+import os, sys
+from sets import Set
+
+W = 125
+H = 12
+
+RED = "#ff0000"
+WHITE = "#ffffff"
+BLACK = "#000000"
+GREY = "#808080"
+
+def incompleteness_bar(DIR, incompleteness):
+ from PIL import Image, ImageDraw, ImageColor
+
+ red = ImageColor.getrgb(RED)
+ white = ImageColor.getrgb(WHITE)
+ black = ImageColor.getrgb(BLACK)
+ grey = ImageColor.getrgb(GREY)
+
+ if incompleteness == -1.0:
+ inc = -1
+ name = "bar_none.png"
+ else:
+ inc = round((W-1) * incompleteness)
+ if inc == 0 and incompleteness != 0:
+ inc == 1
+ name = "bar_%d.png" % inc
+
+ imgfname = os.path.join(DIR,'images',name)
+
+ if not os.path.exists(imgfname):
+ img = Image.new("RGB",(W,H), red)
+ draw = ImageDraw.Draw(img)
+
+ if inc == -1:
+ draw.rectangle([0,0,W-1,H-1], outline=black, fill=white)
+ else:
+ if W-1-inc != 0:
+ draw.rectangle([0,0,W-1-inc,H-1],outline=grey, fill=grey)
+
+ IMGDIR = os.path.join(DIR,'images')
+
+ if not os.path.isdir(IMGDIR):
+ os.mkdir(IMGDIR)
+
+ img.save(imgfname,optimize=True)
+
+ return html.img(src=os.path.join('images', name),alt="incompleteness=%.2f" % incompleteness)
+
+
+NOTFOUND = object()
+
+class Explore:
+
+ def __init__(self, label):
+ self.label = label
+ self.type_names = {}
+
+ def get_module(self, name):
+ pass
+
+ def names(self, obj):
+ if obj is NOTFOUND:
+ return []
+ return self.donames(obj)
+
+
+ def donames(self, obj):
+ pass
+
+ def findattr(self, obj, name):
+ if obj is NOTFOUND:
+ return NOTFOUND
+ return self.dofindattr(obj, name)
+
+ def dofindattr(self, obj, name):
+ pass
+
+ def get_kind(self, obj):
+ if obj is NOTFOUND:
+ return '-'
+ if self.is_class(obj):
+ return 'C'
+ elif self.findattr(obj, '__call__') is not NOTFOUND:
+ return '()'
+ else:
+ return '_'
+
+ def is_class(self, obj):
+ if obj is NOTFOUND:
+ return False
+ return self.dois_class(obj)
+
+ def is_faked(self, obj):
+ return False
+
+ def get_mro(self, obj):
+ if obj is NOTFOUND:
+ return []
+ return self.doget_mro(obj)
+
+ def doget_mro(self, obj):
+ pass
+
+ #def get_type(self, obj):
+ # pass
+ #
+ #def assign_class_name(obj, name):
+ # if obj in self.type_names:
+ # return
+ # self.type_names[obj] = name
+ #
+ #def get_lazy_class_name(obj):
+ # return lambda: self.type_names[obj]
+
+
+class HostExplore(Explore):
+
+ def __init__(self):
+ import sys
+ Explore.__init__(self, "CPython %s" % sys.version)
+
+ def get_module(self, name):
+ try:
+ return __import__(name,{},{},['*'])
+ except ImportError:
+ return NOTFOUND
+
+ def donames(self, obj):
+ return obj.__dict__.keys()
+
+ def dofindattr(self, obj, name):
+ return getattr(obj, name, NOTFOUND)
+
+ def dois_class(self, obj):
+ import types
+ return type(obj) in (type, types.ClassType)
+
+ def doget_mro(self, obj):
+ import inspect
+ return inspect.getmro(obj)
+
+ #def get_type(self, obj):
+ # return type(obj)
+
+
+def abstract_mro(obj, get_bases, acc=None):
+ if acc is None:
+ acc = []
+ if obj in acc:
+ return acc
+ acc.append(obj)
+ for base in get_bases(obj):
+ abstract_mro(base, get_bases, acc)
+ return acc
+
+class ObjSpaceExplore(Explore):
+
+ def __init__(self, space):
+ Explore.__init__(self, "PyPy/%s" % space.__class__.__name__)
+ self.space = space
+
+ def get_module(self, name):
+ space = self.space
+ w = space.wrap
+ try:
+ return space.builtin.call('__import__', w(name),w({}),w({}),w(['*']))
+ except OperationError:
+ return NOTFOUND
+
+ def donames(self, obj):
+ space = self.space
+ return space.unwrap(space.call_method(space.getattr(obj, space.wrap('__dict__')), 'keys'))
+
+ def dofindattr(self, obj, name):
+ space = self.space
+ try:
+ return space.getattr(obj, space.wrap(name))
+ except OperationError:
+ return NOTFOUND
+
+ def is_faked(self, obj):
+ if hasattr(obj, 'instancetypedef'):
+ return hasattr(obj.instancetypedef, 'fakedcpytype')
+ else:
+ return self.is_faked(self.space.type(obj))
+
+ def dois_class(self, obj):
+ #space = self.space
+ #w_t = space.type(obj)
+ #if space.is_w(w_t, space.w_type) or space.is_w(w_t, space.w_classobj):
+ # return True
+ return self.findattr(obj, '__bases__') is not NOTFOUND
+
+ def doget_mro(self, obj):
+ space = self.space
+ try:
+ return space.unpackiterable(space.getattr(obj, space.wrap('__mro__')))
+ except OperationError:
+ def get_bases(obj):
+ return space.unpackiterable(space.getattr(obj, space.wrap('__bases__')))
+
+ return abstract_mro(obj, get_bases)
+
+ #def get_type(self, obj):
+ # return type(obj)
+
+
+class Status:
+ def __init__(self, msg, detail_missing, class_, incompleteness):
+ self.msg = msg
+ self.detail_missing = detail_missing
+ self.class_ = class_
+ self.incompleteness = incompleteness
+
+class Entry:
+
+ def __init__(self, name):
+ self.name = name
+ self.shortname = name
+ self.status = 'PRESENT'
+
+ def __repr__(self):
+ return "<%s %s>" % (self.__class__.__name__, self.name)
+
+ def __str__(self):
+ return self.name
+
+ def set_status(self, obj1, obj2, expl1):
+ if obj1 is NOTFOUND:
+ assert obj2 is not NOTFOUND, "whoopsy %s" % self.name
+ self.status = 'MISSING'
+ self.specifically = 'missing'
+ elif expl1.is_faked(obj1):
+ assert obj2 is not NOTFOUND, "whoopsy %s" % self.name
+ self.status = 'MISSING'
+ self.specifically = 'faked'
+ elif obj2 is NOTFOUND:
+ self.status = expl1
+
+ def only_in(self, parent):
+ if parent is not None and parent.status != 'PRESENT':
+ return ""
+ return 'only in %s' % self.status.label
+
+ def status_wrt(self, parent=None):
+ detail_missing = 0
+ incompleteness = 0.0
+ if self.status == 'MISSING':
+ class_= msg = self.specifically
+ detail_missing = 1
+ incompleteness = 1.0
+ elif self.status == 'PRESENT':
+ msg = ''
+ class_ = None
+ else:
+ msg = self.only_in(parent)
+ if msg:
+ class_ = "only_in"
+ else:
+ class_ = None
+ incompleteness = -1.0
+ return Status(msg, detail_missing, class_, incompleteness)
+
+ def attach(self, parent, annot=None, name=None):
+ if self.status == 'MISSING':
+ parent.total += 1
+ parent.missing += 1
+ elif self.status == 'PRESENT':
+ parent.total += 1
+
+ if annot is None:
+ parent.add_row(self, [], name=name, parent=parent)
+ else:
+ parent.add_row(self, [annot], name=name, parent=parent)
+
+ def grandadd(self, parent):
+ if self.status == 'MISSING':
+ parent.grandtotal += 1
+ parent.grandmissing += 1
+ elif self.status == 'PRESENT':
+ parent.grandtotal += 1
+
+ def handle(self):
+ return self.shortname, # 1-tuple!
+
+ def link(self, name):
+ h = self.handle()
+ if len(h) == 1:
+ h = h[0]
+ if name is None:
+ return h
+ if name.endswith(h):
+ return name
+ return "%s [%s]" % (name, h)
+ else:
+ h, dest = h
+ if name is None:
+ return html.a(h, href=dest)
+ if name.endswith(h):
+ return html.a(name, href=dest)
+ return html.span(name, " [",
+ html.a(h, href=dest), "]")
+
+
+
+reports = []
+
+class Report(Entry):
+
+ notes = None
+
+ def __init__(self, name, title=None, fname=None, notes=None):
+ if title is None:
+ title = name
+ Entry.__init__(self, name)
+
+ self.title = title
+
+ self.rows = []
+
+ reports.append(self)
+
+ self.total = 0
+ self.missing = 0
+
+ self.grandtotal = 0
+ self.grandmissing = 0
+
+ self._fname = fname
+
+ if notes is not None:
+ self.notes = notes
+
+ def add_row(self, entry, rest, name=None, parent=None):
+ self.rows.append((name, entry, rest, parent))
+
+ def missing_stats(self, missing, total):
+ return "%s/%s missing (%.1f%%)" % (missing, total, float(missing)/total*100)
+
+ def status_wrt(self, parent=None):
+ detail_missing = 0
+ incompleteness = 0.0
+
+ if self.status == 'MISSING':
+ msg = "%s (%s)" % (self.specifically, self.total)
+ detail_missing = self.total
+ if self.grandtotal:
+ msg = "%s or in detail (%s)" % (msg, self.grandtotal)
+ detail_missing = self.grandtotal
+ return Status(msg, detail_missing, class_=self.specifically, incompleteness=1.0)
+ elif self.status == 'PRESENT':
+ if self.missing == 0 and self.grandmissing == 0:
+ return Status(msg="complete", detail_missing=detail_missing, class_=None,
+ incompleteness=incompleteness)
+ if self.missing == 0:
+ msg = "all present but"
+ incompleteness = 1.0
+ else:
+ msg = self.missing_stats(self.missing, self.total)
+ detail_missing = self.missing
+ incompleteness = float(self.missing) / self.total
+
+ if self.grandtotal:
+ msg = "%s in detail %s" % (msg,
+ self.missing_stats(self.grandmissing, self.grandtotal))
+ detail_missing = self.grandmissing
+ incompleteness = (incompleteness + float(self.grandmissing)/self.grandtotal)/2
+ return Status(msg, detail_missing, class_='somemissing',
+ incompleteness=incompleteness)
+ else:
+ msg = self.only_in(parent)
+ if msg:
+ class_ = "only_in"
+ else:
+ class_ = None
+ return Status(msg, detail_missing=0, class_=class_,
+ incompleteness=-1.0)
+
+ def fname(self):
+ fname = self._fname
+ if fname is None:
+ fname = self.name
+ return fname+'.html'
+
+ def handle(self):
+ return self.shortname, self.fname()
+
+ def fill_table(self, dir, tbl, rows):
+ def set_class(class_):
+ if class_ is None:
+ return {}
+ else:
+ return {'class': class_}
+
+ i = 0
+ for name, entry, rest, st in rows:
+ tr_class = i%2 == 0 and "even" or "odd"
+ rest = rest + [st.msg, incompleteness_bar(dir, st.incompleteness)]
+ tbl.append(html.tr(
+ html.td(entry.link(name), **set_class(st.class_)),
+ *map(html.td,rest), **{'class': tr_class})
+ )
+ i += 1
+
+ def html(self, dir):
+ title = self.title
+
+ def set_class(class_):
+ if class_ is None:
+ return {}
+ else:
+ return {'class': class_}
+
+ if self.notes is not None:
+ notes = html.p(self.notes)
+ else:
+ notes = html.p()
+
+ st = self.status_wrt()
+
+ msg = st.msg
+ class_ = st.class_
+ bar = incompleteness_bar(dir, st.incompleteness)
+
+ HEADER = '''<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'''
+
+ HIDE = 'display: none'
+ SHOW = 'display: table'
+
+ alpha_table = html.table(id="alpha", style=SHOW)
+ grandmissing_table = html.table(id="grandmissing", style=HIDE)
+ incompleteness_table = html.table(id="incompleteness", style=HIDE)
+
+ toggle = html.p("sort:",
+ html.button("alphabetical", type="button", onclick="toggle('alpha')"),'|',
+ html.button("incompleteness", type="button", onclick="toggle('incompleteness')"),'|',
+ html.button("# overall missing", type="button", onclick="toggle('grandmissing')"))
+
+ page = html.html(
+ html.head(html.title(title),
+ html.link(href="delta.css", rel="stylesheet", type="text/css"),
+ html.script(type="text/javascript",src="delta.js")),
+ html.body(self.navig(),
+ html.p(msg, bar, **set_class(class_)),
+ toggle,
+ alpha_table,
+ grandmissing_table,
+ incompleteness_table,
+ notes), xmlns="http://www.w3.org/1999/xhtml")
+
+ rows = []
+ for name, entry, rest, parent in self.rows:
+ st = entry.status_wrt(parent)
+ rows.append((name, entry, rest, st))
+
+ self.fill_table(dir, alpha_table, rows)
+
+ rows.sort(lambda (name1, ent1, r1, st1),(name2, ent2, r2, st2): -cmp(st1.detail_missing, st2.detail_missing))
+ self.fill_table(dir, grandmissing_table, rows)
+
+ rows.sort(lambda (name1, ent1, r1, st1),(name2, ent2, r2, st2): -cmp(st1.incompleteness, st2.incompleteness))
+ self.fill_table(dir, incompleteness_table, rows)
+
+ f = open(os.path.join(dir, self.fname()),'w')
+
+ f.write(HEADER+unicode(page).encode("utf8"))
+
+ f.close()
+
+ def navig(self):
+ return html.h1(self.title)
+
+class ClassReport(Report):
+
+ def navig(self):
+ return html.p(html.span(self.title,**{'class': 'title'}),
+ "|",mods_report.link(None),"|",cls_report.link(None))
+
+ def grandadd(self, parent):
+ if self.status == 'MISSING':
+ parent.grandtotal += self.total
+ parent.grandmissing += self.missing
+ elif self.status == 'PRESENT':
+ parent.grandtotal += self.total
+
+class ModuleReport(Report):
+
+ def navig(self):
+ return html.p(html.span(self.title,**{'class': 'title'}),
+ "|",mods_report.link(None),"|",cls_report.link(None))
+
+ notes = ("(): callable, C: type/class; detail counts module functions, attrs and "
+ "contained class/type methods and attrs")
+
+ def grandadd(self, parent):
+ if self.status == 'MISSING':
+ parent.grandtotal += self.grandtotal
+ parent.grandmissing += self.grandmissing
+ elif self.status == 'PRESENT':
+ parent.grandtotal += self.grandtotal
+
+
+def delta(expl1, expl2, modnames):
+
+ rep = Report('Modules', fname="modules-index",
+ notes = "detail counts module functions, attrs and "
+ "contained class/type methods and attrs")
+ def navig():
+ return html.p(html.span('Modules',**{'class': 'title'}),
+ "|",cls_report.link(None))
+
+ rep.navig = navig
+
+ for modname in modnames:
+
+ mod1 = expl1.get_module(modname)
+ mod2 = expl2.get_module(modname)
+
+ mod_rep = mod_delta(modname, expl1, mod1, expl2, mod2)
+
+ mod_rep.attach(rep)
+ mod_rep.grandadd(rep)
+
+ return rep
+
+
+def mod_delta(modname, expl1, mod1, expl2, mod2):
+ print "; mod %s" % modname
+ rep = ModuleReport(modname)
+
+ rep.set_status(mod1, mod2, expl1)
+
+ names = Set()
+
+ names.update(expl1.names(mod1))
+ names.update(expl2.names(mod2))
+
+ names = list(names)
+ names.sort()
+
+ for name in names:
+ obj1 = expl1.findattr(mod1, name)
+ obj2 = expl2.findattr(mod2, name)
+
+ if expl1.is_class(obj1) or expl2.is_class(obj2):
+ entry = cls_delta("%s.%s" % (modname, name), expl1, obj1, expl2, obj2)
+ else:
+ entry = Entry(name)
+ entry.set_status(obj1, obj2, expl1)
+
+ kind1 = expl1.get_kind(obj1)
+ kind2 = expl2.get_kind(obj2)
+
+ if kind1 == '-':
+ kindinf = kind2
+ elif kind2 == '-':
+ kindinf = kind1
+ else:
+ if kind1 == kind2:
+ kindinf = kind1
+ else:
+ if kind1 == '_': kind1 = '?'
+ if kind2 == '_': kind2 = '?'
+ kindinf = "%s/%s" % (kind1, kind2)
+
+ if kindinf == '_':
+ kindinf = ''
+
+ entry.attach(rep, kindinf, name=name)
+ entry.grandadd(rep)
+
+ return rep
+
+
+cls_delta_cache = {}
+
+def cls_delta(clsname, expl1, cls1, expl2, cls2):
+ cache = cls_delta_cache
+ print "; cls %s" % clsname
+ try:
+ rep = cache[(cls1, cls2)]
+ return rep
+ except KeyError:
+ pass
+
+ rep = ClassReport(clsname)
+ rep.shortname = clsname.split('.')[1]
+ rep.set_status(cls1, cls2, expl1)
+
+ cls1_is_not_a_class = False
+
+ if not expl1.is_class(cls1):
+ if cls1 is not NOTFOUND:
+ cls1 = NOTFOUND
+ cls1_is_not_a_class = True
+
+ if not expl2.is_class(cls2):
+ cls2 = NOTFOUND
+ assert not cls1_is_not_a_class
+
+ names = Set()
+
+ for cls in expl1.get_mro(cls1):
+ names.update(expl1.names(cls))
+
+ for cls in expl2.get_mro(cls2):
+ names.update(expl2.names(cls))
+
+ names = list(names)
+ names.sort()
+
+ for name in names:
+ obj1 = expl1.findattr(cls1, name)
+ obj2 = expl2.findattr(cls2, name)
+
+ if obj1 is NOTFOUND and obj2 is NOTFOUND:
+ continue # spurious :(
+
+ entry = Entry(name)
+ if cls1_is_not_a_class:
+ entry.status = expl2
+ else:
+ entry.set_status(obj1, obj2, expl1)
+
+ entry.attach(rep)
+
+ cache[(cls1, cls2)] = rep
+
+ return rep
+
+def cls_delta_rep():
+ reps = cls_delta_cache.values()
+ cls_rep = Report('Types/Classes', fname="types-index",
+ notes = "detail counts class/type methods and attrs")
+
+ def navig():
+ return html.p(mods_report.link(None),
+ "|",html.span('Types/Classes',**{'class': 'title'}))
+
+ cls_rep.navig = navig
+
+
+ reps.sort(lambda rep1,rep2: cmp(rep1.name, rep2.name))
+
+ for rep in reps:
+ cls_rep.add_row(rep, [], name=rep.name)
+ cls_rep.total += 1
+ cls_rep.missing += rep.status == 'MISSING'
+ rep.grandadd(cls_rep)
+
+ return cls_rep
+
+#__________________________________________
+
+host_explore = HostExplore()
+
+os_layer = []
+for modname in ['posix', 'nt', 'os2', 'mac', 'ce', 'riscos']:
+ if host_explore.get_module(modname) is not NOTFOUND:
+ os_layer.append(modname)
+
+
+TO_CHECK = list(Set(['types', '__builtin__', 'sys']).union(Set(sys.builtin_module_names)).
+ union(Set([
+ 'math', '_codecs', 'array',
+ '_random', '_sre', 'time', '_socket', 'errno',
+ 'marshal', 'binascii', 'parser']+os_layer)))
+TO_CHECK.remove('__main__')
+TO_CHECK.remove('xxsubtype')
+TO_CHECK.sort()
+
+
+if __name__ == '__main__':
+ if len(sys.argv) == 1:
+ print "usage: delta.py <dest-dir>"
+ print "Then copy delta.css, delta.js to dest-dir if they are not already there"
+ sys.exit(0)
+
+ DIR = sys.argv[1]
+
+
+ from pypy.objspace.std.objspace import StdObjSpace
+
+ space = StdObjSpace()
+
+ mods_report = delta(ObjSpaceExplore(space), host_explore, TO_CHECK)
+ cls_report = cls_delta_rep()
+
+ reports.insert(1,reports.pop())
+
+ reports.reverse()
+
+ if not os.path.isdir(DIR):
+ os.mkdir(DIR)
+
+ for rep in reports:
+ rep.html(DIR)
More information about the Pypy-commit
mailing list