144 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			144 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Python
		
	
	
	
 | 
						|
""" source browser using compiler module
 | 
						|
 | 
						|
WARNING!!!
 | 
						|
 | 
						|
This is very simple and very silly attempt to make so.
 | 
						|
 | 
						|
"""
 | 
						|
 | 
						|
from compiler import parse, ast
 | 
						|
import py
 | 
						|
 | 
						|
from py.__.path.common import PathBase
 | 
						|
 | 
						|
blockers = [ast.Function, ast.Class]
 | 
						|
 | 
						|
class BaseElem(object):
 | 
						|
    def listnames(self):
 | 
						|
        if getattr(self, 'parent', None):
 | 
						|
            return self.parent.listnames() + '.' + self.name
 | 
						|
        return self.name
 | 
						|
 | 
						|
class Module(BaseElem):
 | 
						|
    def __init__(self, path, _dict):
 | 
						|
        self.path = path
 | 
						|
        self.dict = _dict
 | 
						|
    
 | 
						|
    def __getattr__(self, attr):
 | 
						|
        try:
 | 
						|
            return self.dict[attr]
 | 
						|
        except KeyError:
 | 
						|
            raise AttributeError(attr)
 | 
						|
    
 | 
						|
    def get_children(self):
 | 
						|
        values = self.dict.values()
 | 
						|
        all = values[:]
 | 
						|
        for v in values:
 | 
						|
            all += v.get_children()
 | 
						|
        return all
 | 
						|
 | 
						|
def get_endline(start, lst):
 | 
						|
    l = lst[::-1]
 | 
						|
    for i in l:
 | 
						|
        if i.lineno:
 | 
						|
            return i.lineno
 | 
						|
        end_ch = get_endline(None, i.getChildNodes())
 | 
						|
        if end_ch:
 | 
						|
            return end_ch
 | 
						|
    return start
 | 
						|
 | 
						|
class Function(BaseElem):
 | 
						|
    def __init__(self, name, parent, firstlineno, endlineno):
 | 
						|
        self.firstlineno = firstlineno
 | 
						|
        self.endlineno = endlineno
 | 
						|
        self.name = name
 | 
						|
        self.parent = parent
 | 
						|
 | 
						|
    def get_children(self):
 | 
						|
        return []
 | 
						|
 | 
						|
class Method(BaseElem):
 | 
						|
    def __init__(self, name, parent, firstlineno, endlineno):
 | 
						|
        self.name = name
 | 
						|
        self.firstlineno = firstlineno
 | 
						|
        self.endlineno = endlineno
 | 
						|
        self.parent = parent
 | 
						|
 | 
						|
def function_from_ast(ast, cls_ast, cls=Function):
 | 
						|
    startline = ast.lineno
 | 
						|
    endline = get_endline(startline, ast.getChildNodes())
 | 
						|
    assert endline
 | 
						|
    return cls(ast.name, cls_ast, startline, endline)
 | 
						|
 | 
						|
def class_from_ast(cls_ast):
 | 
						|
    bases = [i.name for i in cls_ast.bases if isinstance(i, ast.Name)]
 | 
						|
    # XXX
 | 
						|
    methods = {}
 | 
						|
    startline = cls_ast.lineno
 | 
						|
    name = cls_ast.name
 | 
						|
    endline = get_endline(startline, cls_ast.getChildNodes())
 | 
						|
    cls = Class(name, startline, endline, bases, [])
 | 
						|
    cls.methods = dict([(i.name, function_from_ast(i, cls, Method)) for i in \
 | 
						|
        cls_ast.code.nodes if isinstance(i, ast.Function)])
 | 
						|
    return cls
 | 
						|
 | 
						|
class Class(BaseElem):
 | 
						|
    def __init__(self, name, firstlineno, endlineno, bases, methods):
 | 
						|
        self.bases = bases
 | 
						|
        self.firstlineno = firstlineno
 | 
						|
        self.endlineno = endlineno
 | 
						|
        self.name = name
 | 
						|
        self.methods = methods
 | 
						|
 | 
						|
    def __getattr__(self, attr):
 | 
						|
        try:
 | 
						|
            return self.methods[attr]
 | 
						|
        except KeyError:
 | 
						|
            raise AttributeError(attr)
 | 
						|
    
 | 
						|
    def get_children(self):
 | 
						|
        return self.methods.values()
 | 
						|
 | 
						|
def dir_nodes(st):
 | 
						|
    """ List all the subnodes, which are not blockers
 | 
						|
    """
 | 
						|
    res = []
 | 
						|
    for i in st.getChildNodes():
 | 
						|
        res.append(i)
 | 
						|
        if not i.__class__ in blockers:
 | 
						|
            res += dir_nodes(i)
 | 
						|
    return res
 | 
						|
 | 
						|
def update_mod_dict(imp_mod, mod_dict):
 | 
						|
    # make sure that things that are in mod_dict, and not in imp_mod,
 | 
						|
    # are not shown
 | 
						|
    for key, value in mod_dict.items():
 | 
						|
        if not hasattr(imp_mod, key):
 | 
						|
            del mod_dict[key]
 | 
						|
 | 
						|
def parse_path(path):
 | 
						|
    if not isinstance(path, PathBase):
 | 
						|
        path = py.path.local(path)
 | 
						|
    buf = path.open().read()
 | 
						|
    st = parse(buf)
 | 
						|
    # first go - we get all functions and classes defined on top-level
 | 
						|
    nodes = dir_nodes(st)
 | 
						|
    function_ast = [i for i in nodes if isinstance(i, ast.Function)]
 | 
						|
    classes_ast = [i for i in nodes if isinstance(i, ast.Class)]
 | 
						|
    mod_dict = dict([(i.name, function_from_ast(i, None)) for i in function_ast]
 | 
						|
       + [(i.name, class_from_ast(i)) for i in classes_ast])
 | 
						|
    # we check all the elements, if they're really there
 | 
						|
    try:
 | 
						|
        mod = path.pyimport()
 | 
						|
    except (KeyboardInterrupt, SystemExit):
 | 
						|
        raise
 | 
						|
    except:  # catch all other import problems generically
 | 
						|
        # XXX some import problem: we probably should not
 | 
						|
        # pretend to have an empty module 
 | 
						|
        pass
 | 
						|
    else:
 | 
						|
        update_mod_dict(mod, mod_dict)
 | 
						|
    return Module(path, mod_dict)
 | 
						|
 |