File: browser.py

package info (click to toggle)
codespeak-lib 0.9.1-3
  • links: PTS
  • area: main
  • in suites: lenny
  • size: 3,212 kB
  • ctags: 5,409
  • sloc: python: 33,390; ansic: 961; xml: 582; makefile: 90
file content (143 lines) | stat: -rw-r--r-- 4,096 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143

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