~ubuntu-branches/ubuntu/trusty/spyder/trusty-proposed

« back to all changes in this revision

Viewing changes to spyderlib/utils/classparser.py

  • Committer: Bazaar Package Importer
  • Author(s): Ludovic Aubry
  • Date: 2010-06-28 23:43:02 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20100628234302-3xnz0gcu0w83282r
Tags: 1.1.1-1
* New upstream release
* New maintainer address (Closes: #586833)
* Build with python 2.6 (Closes: #586824)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- coding: utf-8 -*-
 
2
"""
 
3
Python class/function parser
 
4
 
 
5
Derived from "Demo/parser/example.py" from Python distribution
 
6
 
 
7
******************************** WARNING ***************************************
 
8
    This module is not used anymore in Spyder since v1.1.0.
 
9
    However, it will still be part of spyderlib module for a little while -
 
10
    we never know, it could be useful...
 
11
********************************************************************************
 
12
"""
 
13
 
 
14
import os
 
15
import parser
 
16
import symbol
 
17
from types import ListType, TupleType
 
18
 
 
19
 
 
20
def get_info(fileName):
 
21
    source = open(fileName, "U").read() + "\n"
 
22
    basename = os.path.basename(os.path.splitext(fileName)[0])
 
23
    ast = parser.suite(source)
 
24
    return ModuleInfo(ast.totuple(line_info=True), basename)
 
25
 
 
26
def get_classes(filename):
 
27
    """
 
28
    Return classes (with methods) and functions of module *filename*:
 
29
    [ (class1_lineno, class1_name, [ (method1_lineno, method1_name), ]),
 
30
      (func1_lineno, func1_name, None),]
 
31
    """
 
32
    moduleinfo = get_info(filename)
 
33
    classes = []
 
34
    for classname in moduleinfo.get_class_names():
 
35
        clinfo = moduleinfo.get_class_info(classname)
 
36
        methods = []
 
37
        for methodname in clinfo.get_method_names():
 
38
            minfo = clinfo.get_method_info(methodname)
 
39
            methods.append( (minfo.get_lineno(), methodname) )
 
40
        methods.sort()
 
41
        classes.append( (clinfo.get_lineno(), classname, methods) )
 
42
    for funcname in moduleinfo.get_function_names():
 
43
        finfo = moduleinfo.get_function_info(funcname)
 
44
        classes.append( (finfo.get_lineno(), funcname, None) )
 
45
    classes.sort()
 
46
    return classes
 
47
 
 
48
 
 
49
class SuiteInfoBase:
 
50
    _name = ''
 
51
    _lineno = -1
 
52
    #  This pattern identifies compound statements, allowing them to be readily
 
53
    #  differentiated from simple statements.
 
54
    #
 
55
    COMPOUND_STMT_PATTERN = (
 
56
        symbol.stmt,
 
57
        (symbol.compound_stmt, ['compound'])
 
58
        )
 
59
 
 
60
    def __init__(self, tree = None):
 
61
        self._class_info = {}
 
62
        self._function_info = {}
 
63
        if tree:
 
64
            self._extract_info(tree)
 
65
 
 
66
    def _extract_info(self, tree):
 
67
        # extract docstring
 
68
        # discover inner definitions
 
69
        for node in tree[1:]:
 
70
            found, vars = match(self.COMPOUND_STMT_PATTERN, node)
 
71
            if found:
 
72
                cstmt = vars['compound']
 
73
                if cstmt[0] == symbol.funcdef:
 
74
                    func_info = FunctionInfo(cstmt)
 
75
                    self._function_info[func_info._name] = func_info
 
76
                elif cstmt[0] == symbol.classdef:
 
77
                    class_info = ClassInfo(cstmt)
 
78
                    self._class_info[class_info._name] = class_info
 
79
 
 
80
    def get_name(self):
 
81
        return self._name
 
82
        
 
83
    def get_lineno(self):
 
84
        return self._lineno
 
85
 
 
86
    def get_class_names(self):
 
87
        return self._class_info.keys()
 
88
 
 
89
    def get_class_info(self, name):
 
90
        return self._class_info[name]
 
91
 
 
92
    def __getitem__(self, name):
 
93
        try:
 
94
            return self._class_info[name]
 
95
        except KeyError:
 
96
            return self._function_info[name]
 
97
 
 
98
 
 
99
class SuiteFuncInfo:
 
100
    #  Mixin class providing access to function names and info.
 
101
 
 
102
    def get_function_names(self):
 
103
        return self._function_info.keys()
 
104
 
 
105
    def get_function_info(self, name):
 
106
        return self._function_info[name]
 
107
 
 
108
 
 
109
class FunctionInfo(SuiteInfoBase, SuiteFuncInfo):
 
110
    def __init__(self, tree = None):
 
111
        index = 2
 
112
        prefix = ''
 
113
        if tree[1][0] == symbol.decorators:
 
114
            index += 1
 
115
            prefix = '@'
 
116
        self._name = prefix + tree[index][1]
 
117
        self._lineno = tree[index][2]
 
118
        SuiteInfoBase.__init__(self, tree and tree[-1] or None)
 
119
 
 
120
 
 
121
class ClassInfo(SuiteInfoBase):
 
122
    def __init__(self, tree = None):
 
123
        self._name = tree[2][1]
 
124
        self._lineno = tree[2][2]
 
125
        SuiteInfoBase.__init__(self, tree and tree[-1] or None)
 
126
 
 
127
    def get_method_names(self):
 
128
        return self._function_info.keys()
 
129
 
 
130
    def get_method_info(self, name):
 
131
        return self._function_info[name]
 
132
 
 
133
 
 
134
class ModuleInfo(SuiteInfoBase, SuiteFuncInfo):
 
135
    def __init__(self, tree = None, name = "<string>"):
 
136
        self._name = name
 
137
        self._lineno = 0
 
138
        if tree[0] == symbol.encoding_decl:
 
139
            self._encoding = tree[2]
 
140
            tree = tree[1]
 
141
        else:
 
142
            self._encoding = 'ascii'
 
143
        SuiteInfoBase.__init__(self, tree)
 
144
 
 
145
 
 
146
def match(pattern, data, vars=None):
 
147
    """Match `data' to `pattern', with variable extraction.
 
148
 
 
149
    pattern
 
150
        Pattern to match against, possibly containing variables.
 
151
 
 
152
    data
 
153
        Data to be checked and against which variables are extracted.
 
154
 
 
155
    vars
 
156
        Dictionary of variables which have already been found.  If not
 
157
        provided, an empty dictionary is created.
 
158
 
 
159
    The `pattern' value may contain variables of the form ['varname'] which
 
160
    are allowed to match anything.  The value that is matched is returned as
 
161
    part of a dictionary which maps 'varname' to the matched value.  'varname'
 
162
    is not required to be a string object, but using strings makes patterns
 
163
    and the code which uses them more readable.
 
164
 
 
165
    This function returns two values: a boolean indicating whether a match
 
166
    was found and a dictionary mapping variable names to their associated
 
167
    values.
 
168
    """
 
169
    if vars is None:
 
170
        vars = {}
 
171
    if type(pattern) is ListType:       # 'variables' are ['varname']
 
172
        vars[pattern[0]] = data
 
173
        return 1, vars
 
174
    if type(pattern) is not TupleType:
 
175
        return (pattern == data), vars
 
176
    if len(data) != len(pattern):
 
177
        return 0, vars
 
178
    for pattern, data in map(None, pattern, data):
 
179
        same, vars = match(pattern, data, vars)
 
180
        if not same:
 
181
            break
 
182
    return same, vars
 
183
 
 
184
 
 
185
if __name__ == '__main__':
 
186
    import sys, time
 
187
    t0 = time.time()
 
188
    classes = get_classes(sys.argv[1])
 
189
    print "Elapsed time: %s ms" % round((time.time()-t0)*1000)
 
190
#    from pprint import pprint
 
191
#    pprint(classes)