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)
 | |
| 
 |