[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