525 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			525 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			Python
		
	
	
	
 | 
						|
""" Generating ReST output (raw, not python)
 | 
						|
out of data that we know about function calls
 | 
						|
"""
 | 
						|
 | 
						|
import py
 | 
						|
import sys
 | 
						|
import re
 | 
						|
 | 
						|
from py.__.apigen.tracer.docstorage import DocStorageAccessor
 | 
						|
from py.__.rest.rst import * # XXX Maybe we should list it here
 | 
						|
from py.__.apigen.tracer import model
 | 
						|
from py.__.rest.transform import RestTransformer
 | 
						|
 | 
						|
def split_of_last_part(name):
 | 
						|
    name = name.split(".")
 | 
						|
    return ".".join(name[:-1]), name[-1]
 | 
						|
 | 
						|
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 getlinkobj(self, obj, name):
 | 
						|
        return None
 | 
						|
    
 | 
						|
    def getlink(self, filename, lineno, funcname):
 | 
						|
        raise NotImplementedError("Abstract link writer")
 | 
						|
    
 | 
						|
    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
 | 
						|
    
 | 
						|
class ViewVC(AbstractLinkWriter):
 | 
						|
    """ Link writer for ViewVC version control viewer
 | 
						|
    """
 | 
						|
    def __init__(self, basepath):
 | 
						|
        # XXX: should try to guess from a working copy of svn
 | 
						|
        self.basepath = basepath
 | 
						|
    
 | 
						|
    def getlink(self, filename, lineno, funcname):
 | 
						|
        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]
 | 
						|
        sep = py.std.os.sep
 | 
						|
        if sep != '/':
 | 
						|
            relname = relname.replace(sep, '/')
 | 
						|
        return ('%s:%s' % (filename, lineno),
 | 
						|
                self.basepath + relname[1:] + '?view=markup')
 | 
						|
    
 | 
						|
class SourceView(AbstractLinkWriter):
 | 
						|
    def __init__(self, baseurl):
 | 
						|
        self.baseurl = baseurl
 | 
						|
        if self.baseurl.endswith("/"):
 | 
						|
            self.baseurl = baseurl[:-1]
 | 
						|
 | 
						|
    def getlink(self, filename, lineno, funcname):
 | 
						|
        if filename.endswith('.pyc'):
 | 
						|
            filename = filename[:-1]
 | 
						|
        if filename is None:
 | 
						|
            return "<UNKNOWN>:%s" % funcname,""
 | 
						|
        pkgpath = self.getpkgpath(filename)
 | 
						|
        if not filename.startswith(str(pkgpath)):
 | 
						|
            # let's leave it
 | 
						|
            return "<UNKNOWN>:%s" % funcname,""
 | 
						|
        
 | 
						|
        relname = filename[len(str(pkgpath)):]
 | 
						|
        if relname.endswith('.pyc'):
 | 
						|
            relname = relname[:-1]
 | 
						|
        sep = py.std.os.sep
 | 
						|
        if sep != '/':
 | 
						|
            relname = relname.replace(sep, '/')
 | 
						|
        return "%s:%s" % (relname, funcname),\
 | 
						|
            "%s%s#%s" % (self.baseurl, relname, funcname)
 | 
						|
 | 
						|
    def getlinkobj(self, name, obj):
 | 
						|
        try:
 | 
						|
            filename = sys.modules[obj.__module__].__file__
 | 
						|
            return self.getlink(filename, 0, name)
 | 
						|
        except AttributeError:
 | 
						|
            return None
 | 
						|
 | 
						|
class DirectPaste(AbstractLinkWriter):
 | 
						|
    """ No-link writer (inliner)
 | 
						|
    """
 | 
						|
    def getlink(self, filename, lineno, funcname):
 | 
						|
        return ('%s:%s' % (filename, lineno), "")
 | 
						|
 | 
						|
class DirectFS(AbstractLinkWriter):
 | 
						|
    """ Creates links to the files on the file system (for local docs)
 | 
						|
    """
 | 
						|
    def getlink(self, filename, lineno, funcname):
 | 
						|
        return ('%s:%s' % (filename, lineno), 'file://%s' % (filename,))
 | 
						|
 | 
						|
class PipeWriter(object):
 | 
						|
    def __init__(self, output=sys.stdout):
 | 
						|
        self.output = output
 | 
						|
    
 | 
						|
    def write_section(self, name, rest):
 | 
						|
        text = "Contents of file %s.txt:" % (name,)
 | 
						|
        self.output.write(text + "\n")
 | 
						|
        self.output.write("=" * len(text) + "\n")
 | 
						|
        self.output.write("\n")
 | 
						|
        self.output.write(rest.text() + "\n")
 | 
						|
 | 
						|
    def getlink(self, type, targetname, targetfilename):
 | 
						|
        return '%s.txt' % (targetfilename,)
 | 
						|
 | 
						|
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_section(self, name, rest):
 | 
						|
        filename = '%s.txt' % (name,)
 | 
						|
        self.directory.ensure(filename).write(rest.text())
 | 
						|
 | 
						|
    def getlink(self, type, targetname, targetfilename):
 | 
						|
        # we assume the result will get converted to HTML...
 | 
						|
        return '%s.html' % (targetfilename,)
 | 
						|
 | 
						|
class FileWriter(object):
 | 
						|
    def __init__(self, fpath):
 | 
						|
        self.fpath = fpath
 | 
						|
        self.fp = fpath.open('w+')
 | 
						|
        self._defined_targets = []
 | 
						|
 | 
						|
    def write_section(self, name, rest):
 | 
						|
        self.fp.write(rest.text())
 | 
						|
        self.fp.flush()
 | 
						|
 | 
						|
    def getlink(self, type, targetname, targetbasename):
 | 
						|
        # XXX problem: because of docutils' named anchor generation scheme,
 | 
						|
        # a method Foo.__init__ would clash with Foo.init (underscores are 
 | 
						|
        # removed)
 | 
						|
        if targetname in self._defined_targets:
 | 
						|
            return None
 | 
						|
        self._defined_targets.append(targetname)
 | 
						|
        targetname = targetname.lower().replace('.', '-').replace('_', '-')
 | 
						|
        while '--' in targetname:
 | 
						|
            targetname = targetname.replace('--', '-')
 | 
						|
        if targetname.startswith('-'):
 | 
						|
            targetname = targetname[1:]
 | 
						|
        if targetname.endswith('-'):
 | 
						|
            targetname = targetname[:-1]
 | 
						|
        return '#%s-%s' % (type, targetname)
 | 
						|
 | 
						|
class HTMLDirWriter(object):
 | 
						|
    def __init__(self, indexhandler, filehandler, directory=None):
 | 
						|
        self.indexhandler = indexhandler
 | 
						|
        self.filehandler = filehandler
 | 
						|
        if directory is None:
 | 
						|
            self.directory = py.test.ensuretemp('dirwriter')
 | 
						|
        else:
 | 
						|
            self.directory = py.path.local(directory)
 | 
						|
 | 
						|
    def write_section(self, name, rest):
 | 
						|
        if name == 'index':
 | 
						|
            handler = self.indexhandler
 | 
						|
        else:
 | 
						|
            handler = self.filehandler
 | 
						|
        h = handler(name)
 | 
						|
        t = RestTransformer(rest)
 | 
						|
        t.parse(h)
 | 
						|
        self.directory.ensure('%s.html' % (name,)).write(h.html)
 | 
						|
 | 
						|
    def getlink(self, type, targetname, targetfilename):
 | 
						|
        return '%s.html' % (targetfilename,)
 | 
						|
 | 
						|
class RestGen(object):
 | 
						|
    def __init__(self, dsa, linkgen, writer=PipeWriter()):
 | 
						|
        #assert isinstance(linkgen, DirectPaste), (
 | 
						|
        #                        "Cannot use different linkgen by now")
 | 
						|
        self.dsa = dsa
 | 
						|
        self.linkgen = linkgen
 | 
						|
        self.writer = writer
 | 
						|
        self.tracebacks = {}
 | 
						|
 | 
						|
    def write(self):
 | 
						|
        """write the data to the writer"""
 | 
						|
        modlist = self.get_module_list()
 | 
						|
        classlist = self.get_class_list(module='')
 | 
						|
        funclist = self.get_function_list()
 | 
						|
        modlist.insert(0, ['', classlist, funclist])
 | 
						|
 | 
						|
        indexrest = self.build_index([t[0] for t in modlist])
 | 
						|
        self.writer.write_section('index', Rest(*indexrest))
 | 
						|
        
 | 
						|
        self.build_modrest(modlist)
 | 
						|
        
 | 
						|
    def build_modrest(self, modlist):
 | 
						|
        modrest = self.build_modules(modlist)
 | 
						|
        for name, rest, classlist, funclist in modrest:
 | 
						|
            mname = name
 | 
						|
            if mname == '':
 | 
						|
                mname = self.dsa.get_module_name()
 | 
						|
            self.writer.write_section('module_%s' % (mname,),
 | 
						|
                                      Rest(*rest))
 | 
						|
            for cname, crest, cfunclist in classlist:
 | 
						|
                self.writer.write_section('class_%s' % (cname,),
 | 
						|
                                          Rest(*crest))
 | 
						|
                for fname, frest, tbdata in cfunclist:
 | 
						|
                    self.writer.write_section('method_%s' % (fname,),
 | 
						|
                                              Rest(*frest))
 | 
						|
                    for tbname, tbrest in tbdata:
 | 
						|
                        self.writer.write_section('traceback_%s' % (tbname,),
 | 
						|
                                                  Rest(*tbrest))
 | 
						|
            for fname, frest, tbdata in funclist:
 | 
						|
                self.writer.write_section('function_%s' % (fname,),
 | 
						|
                                          Rest(*frest))
 | 
						|
                for tbname, tbrest in tbdata:
 | 
						|
                    self.writer.write_section('traceback_%s' % (tbname,),
 | 
						|
                                              Rest(*tbrest))
 | 
						|
    
 | 
						|
    def build_classrest(self, classlist):
 | 
						|
        classrest = self.build_classes(classlist)
 | 
						|
        for cname, rest, cfunclist in classrest:
 | 
						|
            self.writer.write_section('class_%s' % (cname,),
 | 
						|
                                      Rest(*rest))
 | 
						|
            for fname, rest in cfunclist:
 | 
						|
                self.writer.write_section('method_%s' % (fname,),
 | 
						|
                                          Rest(*rest))
 | 
						|
 | 
						|
    def build_funcrest(self, funclist):
 | 
						|
        funcrest = self.build_functions(funclist)
 | 
						|
        for fname, rest, tbdata in funcrest:
 | 
						|
            self.writer.write_section('function_%s' % (fname,),
 | 
						|
                                      Rest(*rest))
 | 
						|
            for tbname, tbrest in tbdata:
 | 
						|
                self.writer.write_section('traceback_%s' % (tbname,),
 | 
						|
                                          Rest(*tbrest))
 | 
						|
 | 
						|
    def build_index(self, modules):
 | 
						|
        rest = [Title('index', abovechar='=', belowchar='=')]
 | 
						|
        rest.append(Title('exported modules:', belowchar='='))
 | 
						|
        for module in modules:
 | 
						|
            mtitle = module
 | 
						|
            if module == '':
 | 
						|
                module = self.dsa.get_module_name()
 | 
						|
                mtitle = '%s (top-level)' % (module,)
 | 
						|
            linktarget = self.writer.getlink('module', module,
 | 
						|
                                             'module_%s' % (module,))
 | 
						|
            rest.append(ListItem(Link(mtitle, linktarget)))
 | 
						|
        return rest
 | 
						|
 | 
						|
    def build_modules(self, modules):
 | 
						|
        ret = []
 | 
						|
        for module, classes, functions in modules:
 | 
						|
            mname = module
 | 
						|
            if mname == '':
 | 
						|
                mname = self.dsa.get_module_name()
 | 
						|
            rest = [Title('module: %s' % (mname,), abovechar='=',
 | 
						|
                                                   belowchar='='),
 | 
						|
                    Title('index:', belowchar='=')]
 | 
						|
            if classes:
 | 
						|
                rest.append(Title('classes:', belowchar='^'))
 | 
						|
                for cls, bases, cfunclist in classes:
 | 
						|
                    linktarget = self.writer.getlink('class', cls,
 | 
						|
                        'class_%s' % (cls,))
 | 
						|
                    rest.append(ListItem(Link(cls, linktarget)))
 | 
						|
            classrest = self.build_classes(classes)
 | 
						|
            if functions:
 | 
						|
                rest.append(Title('functions:', belowchar='^'))
 | 
						|
                for func in functions:
 | 
						|
                    if module:
 | 
						|
                        func = '%s.%s' % (module, func)
 | 
						|
                    linktarget = self.writer.getlink('function',
 | 
						|
                                                     func,
 | 
						|
                                                     'function_%s' % (func,))
 | 
						|
                    rest.append(ListItem(Link(func, linktarget)))
 | 
						|
            funcrest = self.build_functions(functions, module, False)
 | 
						|
            ret.append((module, rest, classrest, funcrest))
 | 
						|
        return ret
 | 
						|
    
 | 
						|
    def build_classes(self, classes):
 | 
						|
        ret = []
 | 
						|
        for cls, bases, functions in classes:
 | 
						|
            rest = [Title('class: %s' % (cls,), belowchar='='),
 | 
						|
                    LiteralBlock(self.dsa.get_doc(cls))]
 | 
						|
            # link to source
 | 
						|
            link_to_class = self.linkgen.getlinkobj(cls, self.dsa.get_obj(cls))
 | 
						|
            if link_to_class:
 | 
						|
                rest.append(Paragraph(Text("source: "), Link(*link_to_class)))
 | 
						|
        
 | 
						|
            if bases:
 | 
						|
                rest.append(Title('base classes:', belowchar='^')),
 | 
						|
                for base in bases:
 | 
						|
                    rest.append(ListItem(self.make_class_link(base)))
 | 
						|
            if functions:
 | 
						|
                rest.append(Title('functions:', belowchar='^'))
 | 
						|
                for (func, origin) in functions:
 | 
						|
                    linktarget = self.writer.getlink('method',
 | 
						|
                                                     '%s.%s' % (cls, func),
 | 
						|
                                                     'method_%s.%s' % (cls,
 | 
						|
                                                                       func))
 | 
						|
                    rest.append(ListItem(Link('%s.%s' % (cls, func),
 | 
						|
                                              linktarget)))
 | 
						|
            funcrest = self.build_functions(functions, cls, True)
 | 
						|
            ret.append((cls, rest, funcrest))
 | 
						|
        return ret
 | 
						|
    
 | 
						|
    def build_functions(self, functions, parent='', methods=False):
 | 
						|
        ret = []
 | 
						|
        for function in functions:
 | 
						|
            origin = None
 | 
						|
            if methods:
 | 
						|
                function, origin = function
 | 
						|
            if parent:
 | 
						|
                function = '%s.%s' % (parent, function)
 | 
						|
            rest, tbrest = self.write_function(function, origin=origin,
 | 
						|
                                               ismethod=methods)
 | 
						|
            ret.append((function, rest, tbrest))
 | 
						|
        return ret
 | 
						|
 | 
						|
    def get_module_list(self):
 | 
						|
        visited = []
 | 
						|
        ret = []
 | 
						|
        for name in self.dsa.get_class_names():
 | 
						|
            if '.' in name:
 | 
						|
                module, classname = split_of_last_part(name)
 | 
						|
                if module in visited:
 | 
						|
                    continue
 | 
						|
                visited.append(module)
 | 
						|
                ret.append((module, self.get_class_list(module),
 | 
						|
                                    self.get_function_list(module)))
 | 
						|
        return ret
 | 
						|
 | 
						|
    def get_class_list(self, module):
 | 
						|
        ret = []
 | 
						|
        for name in self.dsa.get_class_names():
 | 
						|
            classname = name
 | 
						|
            if '.' in name:
 | 
						|
                classmodule, classname = split_of_last_part(name)
 | 
						|
                if classmodule != module:
 | 
						|
                    continue
 | 
						|
            elif module != '':
 | 
						|
                continue
 | 
						|
            bases = self.dsa.get_possible_base_classes(name)
 | 
						|
            ret.append((name, bases, self.get_method_list(name)))
 | 
						|
        return ret
 | 
						|
 | 
						|
    def get_function_list(self, module=''):
 | 
						|
        ret = []
 | 
						|
        for name in self.dsa.get_function_names():
 | 
						|
            funcname = name
 | 
						|
            if '.' in name:
 | 
						|
                funcpath, funcname = split_of_last_part(name)
 | 
						|
                if funcpath != module:
 | 
						|
                    continue
 | 
						|
            elif module != '':
 | 
						|
                continue
 | 
						|
            ret.append(funcname)
 | 
						|
        return ret
 | 
						|
 | 
						|
    def get_method_list(self, classname):
 | 
						|
        methodnames = self.dsa.get_class_methods(classname)
 | 
						|
        return [(mn, self.dsa.get_method_origin('%s.%s' % (classname, mn)))
 | 
						|
                for mn in methodnames]
 | 
						|
 | 
						|
    def process_type_link(self, _type):
 | 
						|
        # now we do simple type dispatching and provide a link in this case
 | 
						|
        lst = []
 | 
						|
        data = self.dsa.get_type_desc(_type)
 | 
						|
        if not data:
 | 
						|
            for i in _type.striter():
 | 
						|
                if isinstance(i, str):
 | 
						|
                    lst.append(i)
 | 
						|
                else:
 | 
						|
                    lst += self.process_type_link(i)
 | 
						|
            return lst
 | 
						|
        name, _desc_type, is_degenerated = data
 | 
						|
        if not is_degenerated:
 | 
						|
            linktarget = self.writer.getlink(_desc_type, name,
 | 
						|
                                             '%s_%s' % (_desc_type, name))
 | 
						|
            lst.append(Link(str(_type), linktarget))
 | 
						|
        else:
 | 
						|
            # we should provide here some way of linking to sourcegen directly
 | 
						|
            lst.append(name)
 | 
						|
        return lst
 | 
						|
 | 
						|
    def write_function(self, functionname, origin=None, ismethod=False,
 | 
						|
                       belowchar='-'):
 | 
						|
        # XXX I think the docstring should either be split on \n\n and cleaned
 | 
						|
        # from indentation, or treated as ReST too (although this is obviously
 | 
						|
        # dangerous for non-ReST docstrings)...
 | 
						|
        if ismethod:
 | 
						|
            title = Title('method: %s' % (functionname,), belowchar=belowchar)
 | 
						|
        else:
 | 
						|
            title = Title('function: %s' % (functionname,),
 | 
						|
                          belowchar=belowchar)
 | 
						|
        
 | 
						|
        lst = [title, LiteralBlock(self.dsa.get_doc(functionname)),
 | 
						|
               LiteralBlock(self.dsa.get_function_definition(functionname))]
 | 
						|
        link_to_function = self.linkgen.getlinkobj(functionname, self.dsa.get_obj(functionname))
 | 
						|
        if link_to_function:
 | 
						|
            lst.insert(1, Paragraph(Text("source: "), Link(*link_to_function)))
 | 
						|
        
 | 
						|
        opar = Paragraph(Strong('origin'), ":")
 | 
						|
        if origin:
 | 
						|
            opar.add(self.make_class_link(origin))
 | 
						|
        else:
 | 
						|
            opar.add(Text('<UNKNOWN>'))
 | 
						|
        lst.append(opar)
 | 
						|
 | 
						|
        lst.append(Paragraph(Strong("where"), ":"))
 | 
						|
        args, retval = self.dsa.get_function_signature(functionname)
 | 
						|
        for name, _type in args + [('return value', retval)]:
 | 
						|
            l = self.process_type_link(_type)
 | 
						|
            items = []
 | 
						|
            next = "%s :: " % name
 | 
						|
            for item in l:
 | 
						|
                if isinstance(item, str):
 | 
						|
                    next += item
 | 
						|
                else:
 | 
						|
                    if next:
 | 
						|
                        items.append(Text(next))
 | 
						|
                        next = ""
 | 
						|
                    items.append(item)
 | 
						|
            if next:
 | 
						|
                items.append(Text(next))
 | 
						|
            lst.append(ListItem(*items))
 | 
						|
        
 | 
						|
        local_changes = self.dsa.get_function_local_changes(functionname)
 | 
						|
        if local_changes:
 | 
						|
            lst.append(Paragraph(Strong('changes in __dict__ after execution'), ":"))
 | 
						|
            for k, changeset in local_changes.iteritems():
 | 
						|
                lst.append(ListItem('%s: %s' % (k, ', '.join(changeset))))
 | 
						|
        
 | 
						|
        exceptions = self.dsa.get_function_exceptions(functionname)
 | 
						|
        if exceptions:
 | 
						|
            lst.append(Paragraph(Strong('exceptions that might appear during '
 | 
						|
                                 'execution'), ":"))
 | 
						|
            for exc in exceptions:
 | 
						|
                lst.append(ListItem(exc))
 | 
						|
                # XXX: right now we leave it alone
 | 
						|
        
 | 
						|
        # XXX missing implementation of dsa.get_function_location()
 | 
						|
        #filename, lineno = self.dsa.get_function_location(functionname)
 | 
						|
        #linkname, linktarget = self.linkgen.getlink(filename, lineno)
 | 
						|
        #if linktarget:
 | 
						|
        #    lst.append(Paragraph("Function source: ",
 | 
						|
        #               Link(linkname, linktarget)))
 | 
						|
        #else:
 | 
						|
        source = self.dsa.get_function_source(functionname)
 | 
						|
        if source:
 | 
						|
            lst.append(Paragraph(Strong('function source'), ":"))
 | 
						|
            lst.append(LiteralBlock(source))
 | 
						|
        
 | 
						|
        # call sites..
 | 
						|
        call_sites = self.dsa.get_function_callpoints(functionname)
 | 
						|
        tbrest = []
 | 
						|
        if call_sites:
 | 
						|
            call_site_title = Title("call sites:", belowchar='+')
 | 
						|
            lst.append(call_site_title)
 | 
						|
            
 | 
						|
            # we have to think differently here. I would go for:
 | 
						|
            # 1. A quick'n'dirty statement where call has appeared first 
 | 
						|
            #    (topmost)
 | 
						|
            # 2. Link to short traceback
 | 
						|
            # 3. Link to long traceback
 | 
						|
            for call_site, _ in call_sites:
 | 
						|
                fdata, tbdata = self.call_site_link(functionname, call_site)
 | 
						|
                lst += fdata
 | 
						|
                tbrest.append(tbdata)
 | 
						|
        
 | 
						|
        return lst, tbrest
 | 
						|
 | 
						|
    def call_site_link(self, functionname, call_site):
 | 
						|
        tbid, tbrest = self.gen_traceback(functionname, call_site)
 | 
						|
        tbname = '%s.%s' % (functionname, tbid)
 | 
						|
        linktarget = self.writer.getlink('traceback',
 | 
						|
                                         tbname,
 | 
						|
                                         'traceback_%s' % (tbname,))
 | 
						|
        frest = [Paragraph("called in %s" % call_site[0].filename),
 | 
						|
                 Paragraph(Link("traceback %s" % (tbname,),
 | 
						|
                 linktarget))]
 | 
						|
        return frest, (tbname, tbrest)
 | 
						|
    
 | 
						|
    def gen_traceback(self, funcname, call_site):
 | 
						|
        tbid = len(self.tracebacks.setdefault(funcname, []))
 | 
						|
        self.tracebacks[funcname].append(call_site)
 | 
						|
        tbrest = [Title('traceback for %s' % (funcname,))]
 | 
						|
        for line in call_site:
 | 
						|
            lineno = line.lineno - line.firstlineno
 | 
						|
            linkname, linktarget = self.linkgen.getlink(line.filename,
 | 
						|
                                                        line.lineno + 1,
 | 
						|
                                                        funcname)
 | 
						|
            if linktarget:
 | 
						|
                tbrest.append(Paragraph(Link(linkname, linktarget)))
 | 
						|
            else:
 | 
						|
                tbrest.append(Paragraph(linkname))
 | 
						|
            try:
 | 
						|
                source = line.source
 | 
						|
            except IOError:
 | 
						|
                source = "*cannot get source*"
 | 
						|
            mangled = []
 | 
						|
            for i, sline in enumerate(str(source).split('\n')):
 | 
						|
                if i == lineno:
 | 
						|
                    line = '-> %s' % (sline,)
 | 
						|
                else:
 | 
						|
                    line = '   %s' % (sline,)
 | 
						|
                mangled.append(line)
 | 
						|
            tbrest.append(LiteralBlock('\n'.join(mangled)))
 | 
						|
        return tbid, tbrest
 | 
						|
 | 
						|
    def make_class_link(self, desc):
 | 
						|
        if not desc or desc.is_degenerated:
 | 
						|
            # create dummy link here, or no link at all
 | 
						|
            return Strong(desc.name)
 | 
						|
        else:
 | 
						|
            linktarget = self.writer.getlink('class', desc.name,
 | 
						|
                'class_%s' % (desc.name,))
 | 
						|
            return Link(desc.name, linktarget)
 |