[py-svn] r33283 - in py/dist/py: code rst rst/testing test/rsession test/rsession/testing test/tracer test/tracer/testing
fijal at codespeak.net
fijal at codespeak.net
Sat Oct 14 14:00:51 CEST 2006
Author: fijal
Date: Sat Oct 14 13:58:29 2006
New Revision: 33283
Added:
py/dist/py/test/rsession/testing/test_reporter.py (contents, props changed)
py/dist/py/test/tracer/ (props changed)
py/dist/py/test/tracer/__init__.py (contents, props changed)
py/dist/py/test/tracer/description.py (contents, props changed)
py/dist/py/test/tracer/docstorage.py (contents, props changed)
py/dist/py/test/tracer/genrest.py (contents, props changed)
py/dist/py/test/tracer/magic.py (contents, props changed)
py/dist/py/test/tracer/testing/ (props changed)
py/dist/py/test/tracer/testing/__init__.py (contents, props changed)
py/dist/py/test/tracer/testing/runtest.py (contents, props changed)
py/dist/py/test/tracer/testing/test_docgen.py (contents, props changed)
py/dist/py/test/tracer/testing/test_magic.py (contents, props changed)
py/dist/py/test/tracer/testing/test_rest.py (contents, props changed)
py/dist/py/test/tracer/tracer.py (contents, props changed)
Modified:
py/dist/py/code/code.py
py/dist/py/code/frame.py
py/dist/py/rst/rst.py
py/dist/py/rst/testing/test_rst.py
py/dist/py/test/rsession/rsession.py
Log:
Missing branch merge checkin.
Modified: py/dist/py/code/code.py
==============================================================================
--- py/dist/py/code/code.py (original)
+++ py/dist/py/code/code.py Sat Oct 14 13:58:29 2006
@@ -67,4 +67,13 @@
return py.code.Source(self.path.read(mode="rU"))
fullsource = property(fullsource, None, None,
"full source containing this code object")
+
+ def source(self):
+ # return source only for that part of code
+ import inspect
+ return py.code.Source(inspect.getsource(self.raw))
+ def getargs(self):
+ # handfull shortcut for getting args
+ raw = self.raw
+ return raw.co_varnames[:raw.co_argcount]
Modified: py/dist/py/code/frame.py
==============================================================================
--- py/dist/py/code/frame.py (original)
+++ py/dist/py/code/frame.py Sat Oct 14 13:58:29 2006
@@ -10,6 +10,7 @@
self.lineno = frame.f_lineno - 1
self.f_globals = frame.f_globals
self.f_locals = frame.f_locals
+ self.raw = frame
def statement(self):
return self.code.fullsource.getstatement(self.lineno)
@@ -32,4 +33,8 @@
def is_true(self, object):
return object
-
+ def getargs(self):
+ retval = []
+ for arg in self.code.getargs():
+ retval.append((arg, self.f_locals[arg]))
+ return retval
Modified: py/dist/py/rst/rst.py
==============================================================================
--- py/dist/py/rst/rst.py (original)
+++ py/dist/py/rst/rst.py Sat Oct 14 13:58:29 2006
@@ -85,7 +85,7 @@
link_texts = []
for link, target in self.links.iteritems():
link_texts.append(".. _`%s`: %s" % (link, target))
- return "\n".join(link_texts) + "\n"
+ return "\n" + "\n".join(link_texts) + "\n"
def text(self):
outcome = []
@@ -124,6 +124,7 @@
def grab(buf):
outcome.append(self.indent + self.sep.join(buf))
+ texts.reverse()
while texts:
next = texts[-1]
if lgt + len(self.sep) + len(next) <= self.width or not buf:
@@ -135,7 +136,6 @@
lgt = len(self.indent)
buf = []
grab(buf)
- outcome.reverse()
return "\n".join(outcome)
class SubParagraph(Paragraph):
@@ -151,14 +151,14 @@
all_txts = all_txt.split("\n")
return "\n".join([self.indent + i for i in all_txts])
-class Title(AbstractNode):
+class Title(Paragraph):
parentclass = Rest
belowchar = ""
abovechar = ""
previous_paragraph = None
def text(self):
- txt = AbstractNode.text(self)
+ txt = Paragraph.text(self)
lines = []
if self.abovechar:
lines.append(self.abovechar * len(txt))
@@ -185,6 +185,16 @@
start = "*"
end = "*"
+class ListItem(Paragraph):
+ item_char = "*"
+
+ def text(self):
+ self.indent = self.indent + " "
+ txt = Paragraph.text(self)
+ txt = self.item_char + txt[1:]
+ del self.indent
+ return txt
+
class Link(AbstractText):
start = '`'
end = '`_'
@@ -206,3 +216,7 @@
while next.parent is not None:
next = next.parent
return next
+
+class Quote(AbstractText):
+ start = '``'
+ end = '``'
Modified: py/dist/py/rst/testing/test_rst.py
==============================================================================
--- py/dist/py/rst/testing/test_rst.py (original)
+++ py/dist/py/rst/testing/test_rst.py Sat Oct 14 13:58:29 2006
@@ -35,3 +35,13 @@
def test_link():
expected = "`some link`_\n.. _`some link`: http://codespeak.net\n"
txt = Rest(Paragraph(Link("some link", "http://codespeak.net"))).text()
+
+def test_text():
+ expected = "This is a test!\n"
+ txt = Rest(Paragraph("This is a test!")).text()
+ assert txt == expected
+
+def test_list():
+ expected = "* a\n\n* b\n"
+ txt = Rest(ListItem("a"), ListItem("b")).text()
+ assert txt == expected
Modified: py/dist/py/test/rsession/rsession.py
==============================================================================
--- py/dist/py/test/rsession/rsession.py (original)
+++ py/dist/py/test/rsession/rsession.py Sat Oct 14 13:58:29 2006
@@ -15,7 +15,7 @@
setup_slave, MasterNode, dispatch_loop
from py.__.test.rsession.hostmanage import init_hosts, teardown_hosts
-from py.__.test.terminal.out import getout
+from py.__.test.terminal.out import getout
class RemoteOptions(object):
def __init__(self, d):
@@ -27,8 +27,8 @@
remote_options = RemoteOptions({'we_are_remote':False})
class Reporter(object):
- def __init__(self, config, hosts):
- self.config = config
+ def __init__(self, config, hosts):
+ self.config = config
self.failed_tests_outcome = []
self.skipped_tests_outcome = []
self.out = getout(py.std.sys.stdout)
Added: py/dist/py/test/rsession/testing/test_reporter.py
==============================================================================
--- (empty file)
+++ py/dist/py/test/rsession/testing/test_reporter.py Sat Oct 14 13:58:29 2006
@@ -0,0 +1,38 @@
+
+""" reporter tests. This is crippled test, because we just
+test if it *works*, not if the output produced is what we really like
+"""
+
+import py
+from py.__.test.rsession.rsession import Reporter
+from py.__.test.rsession.report import ReceivedItemOutcome
+from py.__.test.rsession.outcome import ReprOutcome, Outcome
+from py.__.test.rsession.testing.test_slave import funcpass_spec
+
+def setup_module(mod):
+ mod.pkgdir = py.path.local(py.__file__).dirpath()
+
+class TestReporter(object):
+ def test_report_received_item_outcome(self):
+ config, args = py.test.Config.parse(["some_sub"])
+ r = Reporter(config, ["host"])
+ # possible outcomes
+ try:
+ 1/0
+ except:
+ exc = py.code.ExceptionInfo()
+
+ outcomes = [Outcome(()),
+ Outcome(skipped=True),
+ Outcome(excinfo=exc),
+ Outcome()]
+
+ outcomes = [ReprOutcome(outcome.make_repr()) for outcome in outcomes]
+ outcomes[3].signal = 11
+ outcomes[0].passed = False
+
+ # we just go...
+ rootcol = py.test.collect.Directory(pkgdir.dirpath())
+ item = rootcol.getitembynames(funcpass_spec)
+ for outcome in outcomes:
+ r.report(ReceivedItemOutcome(None, item, outcome))
Added: py/dist/py/test/tracer/__init__.py
==============================================================================
Added: py/dist/py/test/tracer/description.py
==============================================================================
--- (empty file)
+++ py/dist/py/test/tracer/description.py Sat Oct 14 13:58:29 2006
@@ -0,0 +1,156 @@
+
+try:
+ from pypy.annotation import model
+except ImportError:
+ pass
+
+import types
+
+class CallSite(object):
+ def __init__(self, filename, lineno):
+ self.filename = filename
+ self.lineno = lineno
+
+ def get_tuple(self):
+ return self.filename, self.lineno
+
+ def __hash__(self):
+ return hash((self.filename, self.lineno))
+
+ def __eq__(self, other):
+ return (self.filename, self.lineno) == (other.filename, other.lineno)
+
+ def __ne__(self, other):
+ return not self == other
+
+ def __cmp__(self, other):
+ if self.filename < other.filename:
+ return -1
+ if self.filename > other.filename:
+ return 1
+ if self.lineno < other.lineno:
+ return -1
+ if self.lineno > other.lineno:
+ return 1
+ return 0
+
+class NonHashableObject(object):
+ def __init__(self, cls):
+ self.cls = cls
+
+ def __hash__(self):
+ raise NotImplementedError("Object of type %s are unhashable" % self.cls)
+
+class Desc(object):
+ def __init__(self, name, pyobj, **kwargs):
+ self.pyobj = pyobj
+ self.name = name
+ if type(self) is Desc:
+ # do not override property...
+ self.code = NonHashableObject(self.__class__) # dummy think that makes code unhashable
+ # we make new base class instead of using pypy's one because
+ # of type restrictions of pypy descs
+
+ def __hash__(self):
+ return hash(self.code)
+
+ def __eq__(self, other):
+ if isinstance(other, Desc):
+ return self.code is other.code
+ if isinstance(other, types.CodeType):
+ return self.code is other
+ return False
+
+ def __ne__(self, other):
+ return not self == other
+ # This set of functions will not work on Desc, because we need to
+ # define code somehow
+
+class FunctionDesc(Desc):
+ def __init__(self, *args, **kwargs):
+ super(FunctionDesc, self).__init__(*args, **kwargs)
+ self.inputcells = [model.s_ImpossibleValue for i in xrange(self.\
+ code.co_argcount)]
+ self.call_sites = {}
+ self.keep_frames = kwargs.get('keep_frames', False)
+ self.frame_copier = kwargs.get('frame_copier', lambda x:x)
+ self.retval = model.s_ImpossibleValue
+
+ def consider_call(self, inputcells):
+ for cell_num, cell in enumerate(inputcells):
+ self.inputcells[cell_num] = model.unionof(cell, self.inputcells[cell_num])
+
+ def consider_call_site(self, frame):
+ cs = CallSite(frame.code.raw.co_filename, frame.lineno+1)
+ if self.keep_frames:
+ if cs in self.call_sites:
+ self.call_sites[cs].append(self.frame_copier(frame))
+ else:
+ self.call_sites[cs] = [self.frame_copier(frame)]
+ else:
+ # frame copier makes no sense if we want to keep only one
+ self.call_sites[cs] = frame
+
+ def get_call_sites(self):
+ # convinient accessor for various data which we keep there
+ if not self.keep_frames:
+ return [(key, val) for key, val in self.call_sites.iteritems()]
+ else:
+ lst = []
+ for key, val in self.call_sites.iteritems():
+ for frame in val:
+ lst.append((key, frame))
+ return lst
+
+ def consider_return(self, arg):
+ self.retval = model.unionof(arg, self.retval)
+
+ def getcode(self):
+ return self.pyobj.func_code
+ code = property(getcode)
+
+class ClassDesc(Desc):
+ def __init__(self, *args, **kwargs):
+ super(ClassDesc, self).__init__(*args, **kwargs)
+ self.fields = {}
+ # we'll gather informations about methods and possibly
+ # other variables encountered here
+
+ def getcode(self):
+ return self.pyobj.__init__.im_func.func_code
+ code = property(getcode)
+
+ def consider_call(self, inputcells):
+ md = MethodDesc(self.name + '.__init__', self.pyobj.__init__)
+ self.fields['__init__'] = md
+ md.consider_call(inputcells)
+
+ def consider_return(self, arg):
+ pass # we *know* what return value we do have
+
+ def consider_call_site(self, frame):
+ self.fields['__init__'].consider_call_site(frame)
+
+ def add_method_desc(self, name, methoddesc):
+ self.fields[name] = methoddesc
+
+ def getfields(self):
+ # return fields of values that has been used
+ l = [i for i, v in self.fields.iteritems() if (not i.startswith('_') or\
+ i.startswith('__'))]
+ return l
+## def has_code(self, code):
+## # check __init__ method
+## return self.pyobj.__init__.im_func.func_code is code
+##
+## def consider_call(self, inputcells):
+## # special thing, make MethodDesc for __init__
+##
+##
+class MethodDesc(FunctionDesc):
+ # right now it's not different than method desc, only code is different
+ def getcode(self):
+ return self.pyobj.im_func.func_code
+ code = property(getcode)
+## def has_code(self, code):
+## return self.pyobj.im_func.func_code is code
Added: py/dist/py/test/tracer/docstorage.py
==============================================================================
--- (empty file)
+++ py/dist/py/test/tracer/docstorage.py Sat Oct 14 13:58:29 2006
@@ -0,0 +1,169 @@
+
+""" This module is keeping track about API informations as well as
+providing some interface to easily access stored data
+"""
+
+import py
+import sys
+import types
+
+from py.__.test.tracer.description import FunctionDesc, ClassDesc, MethodDesc, \
+ Desc
+
+try:
+ from pypy.annotation.policy import AnnotatorPolicy
+ from pypy.annotation.bookkeeper import Bookkeeper
+except ImportError:
+ py.test.skip("No PyPy")
+
+class DummyAnnotator(object):
+ policy = AnnotatorPolicy()
+
+class DocStorage(object):
+ """ Class storing info about API
+ """
+ def __init__(self):
+ self.bookkeeper = Bookkeeper(DummyAnnotator())
+ #self.call_stack = []
+ #self.frames = []
+
+ def consider_call(self, frame, caller_frame):
+ assert isinstance(frame, py.code.Frame)
+ desc = self.find_desc(frame.code)
+ if desc:
+ self.generalize_args(desc, frame)
+ desc.consider_call_site(caller_frame)
+
+ def generalize_args(self, desc, frame):
+ args = [arg for key, arg in frame.getargs()]
+ #self.call_stack.append((desc, args))
+ desc.consider_call([self.bookkeeper.immutablevalue(arg) for arg in args])
+
+ def generalize_retval(self, desc, arg):
+ desc.consider_return(self.bookkeeper.immutablevalue(arg))
+
+ def consider_return(self, frame, arg):
+ assert isinstance(frame, py.code.Frame)
+ desc = self.find_desc(frame.code)
+ if desc:
+ self.generalize_retval(desc, arg)
+
+ def find_desc(self, code):
+ return self.desc_cache.get(code.raw, None)
+ #for desc in self.descs.values():
+ # if desc.has_code(frame.code.raw):
+ # return desc
+ #return None
+
+ def make_cache(self):
+ self.desc_cache = {}
+ for key, desc in self.descs.iteritems():
+ self.desc_cache[desc] = desc
+
+ def from_dict(self, _dict, keep_frames = False):
+ self.descs = {}
+ for key, val in _dict.iteritems():
+ to_key, to_val = self.make_desc(key, val)
+ self.descs[to_key] = to_val
+ self.make_cache()
+ return self
+
+ def add_desc(self, name, value, **kwargs):
+ key = name
+ count = 1
+ while key in self.descs:
+ key = "%s_%d" % (name, count)
+ count += 1
+ key, desc = self.make_desc(key, value, **kwargs)
+ self.descs[key] = desc
+ self.desc_cache[desc] = desc
+ return desc
+
+ def make_desc(self, key, value, **kwargs):
+ if isinstance(value, types.FunctionType):
+ desc = FunctionDesc(key, value, **kwargs)
+ elif isinstance(value, (types.ObjectType, types.ClassType)):
+ desc = ClassDesc(key, value, **kwargs)
+ for name in dir(value):
+ field = getattr(value, name)
+ if isinstance(field, types.MethodType):
+ real_name = key + '.' + name
+ md = MethodDesc(real_name, field)
+ self.descs[real_name] = md
+ desc.add_method_desc(name, md)
+ # Some other fields as well?
+ elif isinstance(value, types.MethodType):
+ desc = MethodDesc(key, value, **kwargs)
+ else:
+ desc = Desc(value)
+ return (key, desc) # How to do it better? I want a desc to be a key
+ # value, but I cannot get full object if I do a lookup
+
+ def from_pkg(self, module):
+ self.module = module
+ self.from_dict(module.__package__.__dict__)
+ return self
+
+ def from_module(self, func):
+ raise NotImplementedError("From module")
+
+class DocStorageAccessor(object):
+ """ Set of helper functions to access DocStorage, separated in different
+ class to keep abstraction
+ """
+ def __init__(self, ds):
+ self.ds = ds
+
+ def _get_names(self, filter):
+ return [i for i, desc in self.ds.descs.iteritems() if filter(i, desc)]
+
+ def get_function_names(self):
+ return self._get_names(lambda i, desc: type(desc) is FunctionDesc)
+
+ def get_class_names(self):
+ return self._get_names(lambda i, desc: isinstance(desc, ClassDesc))
+
+ #def get_function(self, name):
+ # return self.ds.descs[name].pyobj
+
+ def get_function_doc(self, name):
+ return self.ds.descs[name].pyobj.__doc__ or "*Not documented*"
+
+ def get_function_definition(self, name):
+ desc = self.ds.descs[name]
+ assert isinstance(desc, FunctionDesc)
+ code = py.code.Code(desc.code)
+ return code.fullsource[code.firstlineno]
+
+ def get_function_signature(self, name):
+ desc = self.ds.descs[name]
+ return desc.inputcells, desc.retval
+
+ def get_function_callpoints(self, name):
+ # return list of tuple (filename, fileline, frame)
+ return self.ds.descs[name].get_call_sites()
+
+ def get_module_name(self):
+ if hasattr(self.ds, 'module'):
+ return self.ds.module.__name__
+ return "Unknown module"
+
+ def get_class_methods(self, name):
+ desc = self.ds.descs[name]
+ assert isinstance(desc, ClassDesc)
+ return desc.getfields()
+
+ #def get_object_info(self, key):
+ #
+
+ def get_module_info(self):
+ module = getattr(self.ds, 'module', None)
+ if module is None:
+ return "Lack of module info"
+ try:
+ retval = module.__doc__ or "*undocumented*"
+ retval = module.__package__.description
+ retval = module.__package__.long_description
+ except AttributeError:
+ pass
+ return retval
Added: py/dist/py/test/tracer/genrest.py
==============================================================================
--- (empty file)
+++ py/dist/py/test/tracer/genrest.py Sat Oct 14 13:58:29 2006
@@ -0,0 +1,208 @@
+
+""" Generating ReST output (raw, not python)
+out of data that we know about function calls
+"""
+
+import py
+import sys
+import re
+
+from py.__.test.tracer.docstorage import DocStorageAccessor
+from py.__.rst.rst import * # XXX Maybe we should list it here
+
+class AbstractLinkWriter(object):
+ """ Class implementing writing links to source code.
+ There should exist various classes for that, different for Trac,
+ different for CVSView, etc.
+ """
+ def getlink(self, filename, lineno):
+ raise NotImplementedError("Abstract link writer")
+
+class ViewVC(AbstractLinkWriter):
+ """ Link writer for ViewVC version control viewer
+ """
+ def __init__(self, basepath):
+ self.basepath = basepath # XXX: should try to guess from working
+ # copy of svn
+
+ def getpkgpath(self, filename):
+ # XXX: very simple thing
+ path = py.path.local(filename).dirpath()
+ while 1:
+ try:
+ path.join('__init__.py').stat()
+ path = path.dirpath()
+ except py.error.ENOENT:
+ return path
+
+ def getlink(self, filename, lineno):
+ path = str(self.getpkgpath(filename))
+ assert filename.startswith(path), "%s does not belong to package %s" %\
+ (filename, path)
+ relname = filename[len(path):]
+ if relname.endswith('.pyc'):
+ relname = relname[:-1]
+ return ('%s:%s' % (filename, lineno), self.basepath + relname[1:] + '?view=markup')
+
+class DirectPaste(AbstractLinkWriter):
+ """ No-link writer (inliner)
+ """
+ def getlink(self, filename, lineno):
+ return (py.path.local(filename).open().readlines()[lineno-1], "")
+
+class PipeWriter(object):
+ def __init__(self, output=sys.stdout):
+ self.output = output
+
+ def write_file(self, filename, data):
+ text = "Written file: %s" % filename
+ self.output.write(text + "\n")
+ self.output.write("=" * len(text) + "\n")
+ self.output.write("\n")
+ self.output.write(data + "\n")
+
+class DirWriter(object):
+ def __init__(self, directory=None):
+ if directory is None:
+ self.directory = py.test.ensuretemp("rstoutput")
+ else:
+ self.directory = py.path.local(directory)
+
+ def write_file(self, filename, data):
+ self.directory.ensure(filename).write(data)
+
+class RestGen(object):
+ def __init__(self, ds, linkgen, writer=PipeWriter()):
+ self.dsa = DocStorageAccessor(ds)
+ self.linkgen = linkgen
+ self.writer = writer
+
+ def write(self):
+ # first write down a file with all the exported interfaces,
+ # sorted by type
+ self.write_interface("index.txt")
+
+ def write_interface(self, filename):
+ lst = [Title("Module: %s" % self.dsa.get_module_name(), belowchar="="),
+ Paragraph(self.dsa.get_module_info()),
+ Title("Exported functions:", belowchar="-")]
+ self.write_function_list(lst)
+ lst.append(Title("Exported classes:", belowchar="-"))
+ self.write_class_list(lst)
+ self.writer.write_file(filename, Rest(*lst).text())
+
+ def write_function_list(self, lst):
+ for name in self.dsa.get_function_names():
+ # XXX: should be .html here?
+ lst.append(ListItem(Text("Function: "), Link(name, "function_%s.html" % name)))
+ self.write_function('function_' + name + '.txt', name)
+
+ def write_class_list(self, lst):
+ for name in self.dsa.get_class_names():
+ lst.append(ListItem(Text("Class: "), Link(name, "class_%s.html" % name)))
+ self.write_class('class_' + name + '.txt', name)
+
+ def write_class(self, filename, class_name):
+ lst = [Title("Class: %s" % class_name, belowchar='-')]
+
+ # write down exported methods
+ lst.append(Title("Exported methods:", belowchar="^"))
+ for method in self.dsa.get_class_methods(class_name):
+ fname = "method_%s" % (class_name + \
+ "_" + method)
+ lst.append(ListItem(Link(method, fname+'.html')))
+ self.write_function(fname+'.txt', class_name + "." + method)
+
+ self.writer.write_file(filename, Rest(*lst).text())
+
+ def write_function(self, filename, fun_name):
+ lst = [Title("Function: %s" % fun_name, belowchar="-"),
+ Paragraph(self.dsa.get_function_doc(fun_name)),
+ BlockQuote(self.dsa.get_function_definition(fun_name))]
+
+ lst.append(Paragraph("Function source: XXX (to write a link here)"))
+
+ args, retval = self.dsa.get_function_signature(fun_name)
+ # XXX: we just do "knowntype" here, but we should
+ # present some more informative way, maybe even provide a link
+ # for the type declaration (if this is known)
+ arg_str = "(%s)" % (",".join([str(i.knowntype) for i in args]))
+ ret_str = str(retval.knowntype)
+ arg_quote = Paragraph("Function type:", Quote(arg_str), '->', Quote(ret_str))
+ lst.append(arg_quote)
+
+ # call sites..
+ call_site_title = Title("Call sites:", belowchar="-")
+ lst.append(call_site_title)
+ call_sites = lst
+
+ for call_site, frame in self.dsa.get_function_callpoints(fun_name):
+ link_str = "File %s:%s" % (call_site.filename,
+ call_site.lineno)
+ #_, link_target = self.linkgen.getlink(call_site.filename, call_site.lineno)
+ #if link_target: # otherwise it's just inline text
+ # call_sites.append(Paragraph(Link(link_str, link_target)))
+ #else:
+ # call_sites.append(Paragraph(link_str))
+ #call_sites.append(BlockQuote(call_site.source))
+ # XXX: For now, we just paste here the filename of that
+ call_sites.append(Paragraph(link_str))
+ source = frame.code.source()
+ lines = []
+ for num, line in enumerate(source):
+ if num == call_site.lineno - frame.code.firstlineno - 1:
+ m = re.match("^( *)(.*)", line)
+ lines.append(">" + "-"*len(m.group(1)) + m.group(2))
+ else:
+ lines.append(" " + line)
+ call_sites.append(BlockQuote("\n".join(lines)))
+
+ self.writer.write_file(filename, Rest(*lst).text())
+##class RestGen(object):
+## def __init__(self, ds, linkgen, output=sys.stdout):
+## self.dsa = DocStorageAccessor(ds)
+## self.linkgen = linkgen
+## self.output = output
+##
+## def add(self, item):
+## self.list.append(item)
+##
+## def write(self):
+## self.list = []
+## self.add(Title("Module: %s" % self.dsa.get_module_name(), belowchar="="))
+## self.add(Paragraph(Text(self.dsa.get_module_info())))
+## self.write_functions()
+## self.write_classes()
+##
+## def write_functions(self):
+## self.add(Title("Exported functions:", belowchar="-"))
+## for key in self.dsa.get_function_names():
+## title = Title("%s description:" % key, belowchar="^")
+## docstr = Paragraph(self.dsa.get_function_doc(key))
+## args = self.dsa.get_function_args(key)
+## arg_str = "(%s)" % (",".join([str(i) for i in args]))
+## arg_quote = BlockQuote("Function type: %s" % arg_str)
+##
+## call_site_title = Title("Call sites:", belowchar="-")
+## call_sites = []
+##
+## for call_site in self.dsa.get_function_callpoints(key):
+## link_str = "File %s:%s" % (call_site.filename,
+## call_site.lineno)
+## link_target = self.linkgen.getlink(call_site.filename, call_site.lineno)
+## if link_target: # otherwise it's just inline text
+## call_sites.append(Paragraph(Link(link_str, link_target)))
+## else:
+## call_sites.append(Paragraph(link_str))
+## call_sites.append(BlockQuote(call_site.source))
+##
+## for item in [title, docstr, arg_quote, call_site_title] + call_sites:
+## self.add(item)
+##
+## #for call_site in self.dsa.get_function_callpoints(key):
+## # self.writeline("File %s:%s\n%s" % call_site.get_tuple())
+## self.output.write(Rest(*self.list).text())
+##
+## def write_classes(self):
+## self.add(Title("Exported classes:", belowchar="-"))
+
Added: py/dist/py/test/tracer/magic.py
==============================================================================
--- (empty file)
+++ py/dist/py/test/tracer/magic.py Sat Oct 14 13:58:29 2006
@@ -0,0 +1,63 @@
+
+""" magic - some operations which helps to extend PDB with some magic data.
+Actually there is only explicit tracking of data, might be extended to
+automatic at some point.
+"""
+
+# some magic stuff to have singleton of DocStorage, but initialised explicitely
+
+import weakref
+
+import py
+from py.__.test.tracer.docstorage import DocStorage
+from py.__.test.tracer.tracer import Tracer
+import sys
+
+class DocStorageKeeper(object):
+ doc_storage = DocStorage()
+ doc_storage.tracer = Tracer(doc_storage)
+ doc_storage.from_dict({})
+
+ def set_storage(cl, ds):
+ cl.doc_storage = ds
+ cl.doc_storage.tracer = Tracer(ds)
+ set_storage = classmethod(set_storage)
+
+def get_storage():
+ return DocStorageKeeper.doc_storage
+
+def stack_copier(frame):
+ # copy all stack, not only frame
+ num = 0
+ gather = False
+ stack = []
+ try:
+ while 1:
+ if gather:
+ stack.append(py.code.Frame(sys._getframe(num)))
+ else:
+ if sys._getframe(num) is frame.raw:
+ gather = True
+ num += 1
+ except ValueError:
+ pass
+ return stack
+
+def trace(keep_frames=False, frame_copier=lambda x:x):
+ def decorator(fun):
+ ds = get_storage()
+ # in case we do not have this function inside doc storage, we
+ # want to have it
+ desc = ds.find_desc(py.code.Code(fun.func_code))
+ if desc is None:
+ desc = ds.add_desc(fun.func_name, fun, keep_frames=keep_frames,
+ frame_copier=frame_copier)
+
+ def wrapper(*args, **kwargs):
+ ds.tracer.start_tracing()
+ retval = fun(*args, **kwargs)
+ ds.tracer.end_tracing()
+ return retval
+
+ return wrapper
+ return decorator
Added: py/dist/py/test/tracer/testing/__init__.py
==============================================================================
Added: py/dist/py/test/tracer/testing/runtest.py
==============================================================================
--- (empty file)
+++ py/dist/py/test/tracer/testing/runtest.py Sat Oct 14 13:58:29 2006
@@ -0,0 +1,5 @@
+
+def cut_pyc(f_name):
+ if f_name.endswith('.pyc'):
+ return f_name[:-1]
+ return f_name
Added: py/dist/py/test/tracer/testing/test_docgen.py
==============================================================================
--- (empty file)
+++ py/dist/py/test/tracer/testing/test_docgen.py Sat Oct 14 13:58:29 2006
@@ -0,0 +1,120 @@
+
+""" test doc generation
+"""
+
+import py
+import sys
+
+try:
+ from py.__.test.tracer.tracer import DocStorage, Tracer
+ from py.__.test.tracer.testing.runtest import cut_pyc
+ from py.__.test.tracer.description import FunctionDesc
+ from pypy.annotation import model
+except ImportError, s:
+ py.test.skip("Cannot import: %s" % str(s))
+
+#def setup_module(mod):
+# data_path = py.path.local(mod.__file__).dirpath().join("data")
+# sys.path.insert(0, str(data_path))
+
+
+def fun(a, b, c):
+ "Some docstring"
+ return "d"
+
+def test_basic():
+ descs = {"fun":fun}
+ ds = DocStorage().from_dict(descs)
+ t = Tracer(ds)
+ t.start_tracing()
+ fun(1, ("g", 3), 8)
+ fun(2., ("a", 1.), "a")
+ t.end_tracing()
+ desc = ds.descs['fun']
+ inputcells = desc.inputcells
+ assert len(inputcells) == 3
+ assert isinstance(inputcells[0], model.SomeFloat)
+ assert isinstance(inputcells[1], model.SomeTuple)
+ assert isinstance(inputcells[2], model.SomeObject)
+ assert isinstance(desc.retval, model.SomeChar)
+ cs = sorted(desc.call_sites.keys())
+ assert len(cs) == 2
+ f_name = cut_pyc(__file__)
+ assert cs[0].filename == f_name
+ assert cs[0].lineno == test_basic.func_code.co_firstlineno + 5
+ assert cs[1].filename == f_name
+ assert cs[1].lineno == test_basic.func_code.co_firstlineno + 6
+
+class SomeClass(object):
+ """ Class docstring
+ """
+ def __init__(self, b="blah"):
+ pass
+
+ def exposed_method(self, a, b, c):
+ """ method docstring
+ """
+ return self._hidden_method()
+
+ def _hidden_method(self):
+ """ should not appear
+ """
+ return "z"
+
+def test_class():
+ descs = {'SomeClass':SomeClass}
+ ds = DocStorage().from_dict(descs)
+ t = Tracer(ds)
+ t.start_tracing()
+ s = SomeClass()
+ s.exposed_method(1, 2., [1,2,3])
+ t.end_tracing()
+ desc = ds.descs['SomeClass']
+ inputcells = desc.fields['__init__'].inputcells
+ assert len(inputcells) == 2
+ assert isinstance(inputcells[0], model.SomeInstance)
+ assert inputcells[0].classdef.classdesc.pyobj is SomeClass
+ assert isinstance(inputcells[1], model.SomeString)
+ f_name = __file__
+ if f_name.endswith('.pyc'):
+ f_name = f_name[:-1]
+ cs = sorted(desc.fields['__init__'].call_sites.keys())
+ assert len(cs) == 1
+ assert cs[0].filename == f_name
+ assert cs[0].lineno == test_class.func_code.co_firstlineno + 5
+ # method check
+ assert sorted(desc.getfields()) == ['__init__', 'exposed_method']
+ inputcells = desc.fields['exposed_method'].inputcells
+ assert len(inputcells) == 4
+ assert isinstance(inputcells[0], model.SomeInstance)
+ assert inputcells[0].classdef.classdesc.pyobj is SomeClass
+ assert isinstance(inputcells[1], model.SomeInteger)
+ assert isinstance(inputcells[2], model.SomeFloat)
+ assert isinstance(inputcells[3], model.SomeList)
+ assert isinstance(desc.fields['exposed_method'].retval, model.SomeChar)
+
+def other_fun():
+ pass
+
+def test_add_desc():
+ ds = DocStorage().from_dict({})
+ ds.add_desc("one", fun)
+ ds.add_desc("one", other_fun)
+ assert sorted(ds.descs.keys()) == ["one", "one_1"]
+ assert isinstance(ds.descs["one"], FunctionDesc)
+ assert isinstance(ds.descs["one_1"], FunctionDesc)
+ assert ds.descs["one"].pyobj is fun
+ assert ds.descs["one_1"].pyobj is other_fun
+ assert ds.desc_cache[ds.descs["one"]] is ds.descs["one"]
+ assert ds.desc_cache[ds.descs["one_1"]] is ds.descs["one_1"]
+
+def test_while_call():
+ ds = DocStorage().from_dict({"other_fun":other_fun})
+ t = Tracer(ds)
+ t.start_tracing()
+ for x in xrange(8):
+ other_fun()
+ t.end_tracing()
+ desc = ds.descs["other_fun"]
+ assert len(desc.call_sites.keys()) == 1
+ assert isinstance(desc.call_sites.values()[0], py.code.Frame)
Added: py/dist/py/test/tracer/testing/test_magic.py
==============================================================================
--- (empty file)
+++ py/dist/py/test/tracer/testing/test_magic.py Sat Oct 14 13:58:29 2006
@@ -0,0 +1,45 @@
+
+""" test magic abilities of tracer
+"""
+
+from py.__.test.tracer.magic import trace, get_storage, stack_copier, \
+ DocStorageKeeper
+from py.__.test.tracer.docstorage import DocStorage
+from pypy.annotation import model
+
+#def setup_function(f):
+# DocStorageKeeper.set_storage(DocStorage().from_dict({}))
+
+def fun(a, b, c):
+ return "a"
+fun = trace()(fun)
+
+def test_magic():
+ fun(1, 2, 3)
+
+ ds = get_storage()
+ assert 'fun' in ds.descs
+ assert len(ds.descs.keys()) == 2
+ desc = ds.descs['fun']
+ inputcells = desc.inputcells
+ assert isinstance(inputcells[0], model.SomeInteger)
+ assert isinstance(inputcells[1], model.SomeInteger)
+ assert isinstance(inputcells[2], model.SomeInteger)
+ assert isinstance(desc.retval, model.SomeChar)
+
+def g(x):
+ return f(x)
+
+def f(x):
+ return x + 3
+f = trace(keep_frames=True, frame_copier=stack_copier)(f)
+
+def test_fancy_copier():
+ g(1)
+
+ ds = get_storage()
+ assert 'f' in ds.descs
+ desc = ds.descs['f']
+ stack = desc.call_sites.values()[0][0]
+ assert str(stack[0].statement) == ' return f(x)'
+ assert str(stack[1].statement) == ' g(1)'
Added: py/dist/py/test/tracer/testing/test_rest.py
==============================================================================
--- (empty file)
+++ py/dist/py/test/tracer/testing/test_rest.py Sat Oct 14 13:58:29 2006
@@ -0,0 +1,86 @@
+
+""" tests document generation
+"""
+
+import py
+
+try:
+ from py.__.test.tracer.genrest import ViewVC, RestGen, PipeWriter, DirWriter,\
+ DirectPaste
+ from py.__.test.tracer.tracer import DocStorage, Tracer
+
+ from StringIO import StringIO
+ from py.__.test.tracer.testing.runtest import cut_pyc
+except ImportError, s:
+ py.test.skip("Cannot import: %s" % str(s))
+
+def test_links():
+ vcview = ViewVC("http://codespeak.net/viewvc/")
+ _, linkname = vcview.getlink(cut_pyc(__file__), 0)
+ assert linkname == 'http://codespeak.net/viewvc/py/test/tracer/testing/test_rest.py?view=markup'
+
+class SomeClass(object):
+ def __init__(self, a):
+ self.a = a
+
+ def method(self, a, b, c):
+ return a + b + c
+
+def fun(a, b, c):
+ "Some docstring"
+ return "d"
+
+def test_dir_writer():
+ p = StringIO()
+ dir = py.test.ensuretemp("dirwriter")
+ w = DirWriter(dir)
+ w.write_file("one", "one data")
+ w.write_file("two", "two data")
+ assert dir.join("one").read() == "one data"
+ assert dir.join("two").read() == "two data"
+
+def test_direct_link():
+ assert DirectPaste().getlink(cut_pyc(__file__), 2)[0] == '""" tests document generation\n'
+ # C-c C-v ....
+
+class TestRest(object):
+ def check_rest(self, tmpdir):
+ from py.__.misc import rest
+ for path in tmpdir.listdir('*.txt'):
+ rest.process(path)
+
+ def test_generation_simple_api(self):
+ descs = {'SomeClass':SomeClass, 'fun':fun}
+ ds = DocStorage().from_dict(descs)
+ lg = DirectPaste()
+ t = Tracer(ds)
+ t.start_tracing()
+ s1 = SomeClass("a")
+ fun(1, 2, s1)
+ s2 = SomeClass("b")
+ s2.method(1,2,3)
+ fun(1, 3, s2)
+ t.end_tracing()
+ tmpdir = py.test.ensuretemp("test_generation")
+ r = RestGen(ds, lg, DirWriter(tmpdir))
+ r.write()
+ # now we check out...
+ self.check_rest(tmpdir)
+
+##def test_generation():
+## py.test.skip("WIP")
+## descs = {"fun":fun}
+## ds = DocStorage().from_dict(descs)
+## lg = ViewVC("http://codespeak.net/viewvc/")
+## t = Tracer(ds)
+## t.start_tracing()
+## fun(1, ("g", 3), 8)
+## fun(2., ("a", 1.), "a")
+## t.end_tracing()
+## s = StringIO()
+## r = RestGen(ds, lg, output=s)
+## r.write()
+## # we cannot check the source, cause the rapidly changing
+## # visual effect will break it, so we'll make assertion later
+## # XXX: do that
+## assert s.getvalue()
Added: py/dist/py/test/tracer/tracer.py
==============================================================================
--- (empty file)
+++ py/dist/py/test/tracer/tracer.py Sat Oct 14 13:58:29 2006
@@ -0,0 +1,47 @@
+
+""" simple tracer for API generation
+"""
+
+import py
+import sys
+import types
+
+# We have to steal as much as possible from union of types
+# from pypy's code.
+# BLAH BLAH BLAH
+
+from py.__.test.tracer.description import FunctionDesc
+from py.__.test.tracer.docstorage import DocStorage
+
+class UnionError(Exception):
+ pass
+
+class Tracer(object):
+ """ Basic tracer object, used for gathering additional info
+ about API functions
+ """
+ def __init__(self, docstorage):
+ self.docstorage = docstorage
+ self.tracing = False
+
+ def _tracer(self, frame, event, arg):
+
+ # perform actuall tracing
+ frame = py.code.Frame(frame)
+ if event == 'call':
+ assert arg is None
+ self.docstorage.consider_call(frame, py.code.Frame(sys._getframe(2)))
+ elif event == 'return':
+ self.docstorage.consider_return(frame, arg)
+
+ return self._tracer
+
+ def start_tracing(self):
+ if self.tracing:
+ return
+ self.tracing = True
+ sys.settrace(self._tracer)
+
+ def end_tracing(self):
+ self.tracing = False
+ sys.settrace(None)
More information about the pytest-commit
mailing list