~gabriel1984sibiu/agros2d/agros2d

« back to all changes in this revision

Viewing changes to resources/python/logilab/astng/utils.py

  • Committer: Grevutiu Gabriel
  • Date: 2014-11-15 19:05:36 UTC
  • Revision ID: gabriel1984sibiu@gmail.com-20141115190536-1d4q8ez0f8b89ktj
originalĀ upstreamĀ code

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 
2
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 
3
#
 
4
# This file is part of logilab-astng.
 
5
#
 
6
# logilab-astng is free software: you can redistribute it and/or modify it
 
7
# under the terms of the GNU Lesser General Public License as published by the
 
8
# Free Software Foundation, either version 2.1 of the License, or (at your
 
9
# option) any later version.
 
10
#
 
11
# logilab-astng is distributed in the hope that it will be useful, but
 
12
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 
13
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
 
14
# for more details.
 
15
#
 
16
# You should have received a copy of the GNU Lesser General Public License along
 
17
# with logilab-astng. If not, see <http://www.gnu.org/licenses/>.
 
18
"""this module contains some utilities to navigate in the tree or to
 
19
extract information from it
 
20
"""
 
21
 
 
22
__docformat__ = "restructuredtext en"
 
23
 
 
24
from logilab.astng.exceptions import ASTNGBuildingException
 
25
from logilab.astng.builder import parse
 
26
 
 
27
 
 
28
class ASTWalker:
 
29
    """a walker visiting a tree in preorder, calling on the handler:
 
30
 
 
31
    * visit_<class name> on entering a node, where class name is the class of
 
32
    the node in lower case
 
33
 
 
34
    * leave_<class name> on leaving a node, where class name is the class of
 
35
    the node in lower case
 
36
    """
 
37
 
 
38
    def __init__(self, handler):
 
39
        self.handler = handler
 
40
        self._cache = {}
 
41
 
 
42
    def walk(self, node, _done=None):
 
43
        """walk on the tree from <node>, getting callbacks from handler"""
 
44
        if _done is None:
 
45
            _done = set()
 
46
        if node in _done:
 
47
            raise AssertionError((id(node), node, node.parent))
 
48
        _done.add(node)
 
49
        self.visit(node)
 
50
        for child_node in node.get_children():
 
51
            self.handler.set_context(node, child_node)
 
52
            assert child_node is not node
 
53
            self.walk(child_node, _done)
 
54
        self.leave(node)
 
55
        assert node.parent is not node
 
56
 
 
57
    def get_callbacks(self, node):
 
58
        """get callbacks from handler for the visited node"""
 
59
        klass = node.__class__
 
60
        methods = self._cache.get(klass)
 
61
        if methods is None:
 
62
            handler = self.handler
 
63
            kid = klass.__name__.lower()
 
64
            e_method = getattr(handler, 'visit_%s' % kid,
 
65
                               getattr(handler, 'visit_default', None))
 
66
            l_method = getattr(handler, 'leave_%s' % kid,
 
67
                               getattr(handler, 'leave_default', None))
 
68
            self._cache[klass] = (e_method, l_method)
 
69
        else:
 
70
            e_method, l_method = methods
 
71
        return e_method, l_method
 
72
 
 
73
    def visit(self, node):
 
74
        """walk on the tree from <node>, getting callbacks from handler"""
 
75
        method = self.get_callbacks(node)[0]
 
76
        if method is not None:
 
77
            method(node)
 
78
 
 
79
    def leave(self, node):
 
80
        """walk on the tree from <node>, getting callbacks from handler"""
 
81
        method = self.get_callbacks(node)[1]
 
82
        if method is not None:
 
83
            method(node)
 
84
 
 
85
 
 
86
class LocalsVisitor(ASTWalker):
 
87
    """visit a project by traversing the locals dictionary"""
 
88
    def __init__(self):
 
89
        ASTWalker.__init__(self, self)
 
90
        self._visited = {}
 
91
 
 
92
    def visit(self, node):
 
93
        """launch the visit starting from the given node"""
 
94
        if node in self._visited:
 
95
            return
 
96
        self._visited[node] = 1 # FIXME: use set ?
 
97
        methods = self.get_callbacks(node)
 
98
        if methods[0] is not None:
 
99
            methods[0](node)
 
100
        if 'locals' in node.__dict__: # skip Instance and other proxy
 
101
            for name, local_node in node.items():
 
102
                self.visit(local_node)
 
103
        if methods[1] is not None:
 
104
            return methods[1](node)
 
105
 
 
106
 
 
107
def _check_children(node):
 
108
    """a helper function to check children - parent relations"""
 
109
    for child in node.get_children():
 
110
        ok = False
 
111
        if child is None:
 
112
            print "Hm, child of %s is None" % node
 
113
            continue
 
114
        if not hasattr(child, 'parent'):
 
115
            print " ERROR: %s has child %s %x with no parent" % (node, child, id(child))
 
116
        elif not child.parent:
 
117
            print " ERROR: %s has child %s %x with parent %r" % (node, child, id(child), child.parent)
 
118
        elif child.parent is not node:
 
119
            print " ERROR: %s %x has child %s %x with wrong parent %s" % (node,
 
120
                                      id(node), child, id(child), child.parent)
 
121
        else:
 
122
            ok = True
 
123
        if not ok:
 
124
            print "lines;", node.lineno, child.lineno
 
125
            print "of module", node.root(), node.root().name
 
126
            raise ASTNGBuildingException
 
127
        _check_children(child)
 
128
 
 
129
 
 
130
class TreeTester(object):
 
131
    '''A helper class to see _ast tree and compare with astng tree
 
132
 
 
133
    indent: string for tree indent representation
 
134
    lineno: bool to tell if we should print the line numbers
 
135
 
 
136
    >>> tester = TreeTester('print')
 
137
    >>> print tester.native_tree_repr()
 
138
 
 
139
    <Module>
 
140
    .   body = [
 
141
    .   <Print>
 
142
    .   .   nl = True
 
143
    .   ]
 
144
    >>> print tester.astng_tree_repr()
 
145
    Module()
 
146
        body = [
 
147
        Print()
 
148
            dest = 
 
149
            values = [
 
150
            ]
 
151
        ]
 
152
    '''
 
153
 
 
154
    indent = '.   '
 
155
    lineno = False
 
156
 
 
157
    def __init__(self, sourcecode):
 
158
        self._string = ''
 
159
        self.sourcecode = sourcecode
 
160
        self._ast_node = None
 
161
        self.build_ast()
 
162
 
 
163
    def build_ast(self):
 
164
        """build the _ast tree from the source code"""
 
165
        self._ast_node = parse(self.sourcecode)
 
166
 
 
167
    def native_tree_repr(self, node=None, indent=''):
 
168
        """get a nice representation of the _ast tree"""
 
169
        self._string = ''
 
170
        if node is None:
 
171
            node = self._ast_node
 
172
        self._native_repr_tree(node, indent)
 
173
        return self._string
 
174
 
 
175
 
 
176
    def _native_repr_tree(self, node, indent, _done=None):
 
177
        """recursive method for the native tree representation"""
 
178
        from _ast import Load as _Load, Store as _Store, Del as _Del
 
179
        from _ast import AST as Node
 
180
        if _done is None:
 
181
            _done = set()
 
182
        if node in _done:
 
183
            self._string += '\nloop in tree: %r (%s)' % (node,
 
184
                                            getattr(node, 'lineno', None))
 
185
            return
 
186
        _done.add(node)
 
187
        self._string += '\n' + indent +  '<%s>' % node.__class__.__name__
 
188
        indent += self.indent
 
189
        if not hasattr(node, '__dict__'):
 
190
            self._string += '\n' + self.indent + " ** node has no __dict__ " + str(node)
 
191
            return
 
192
        node_dict = node.__dict__
 
193
        if hasattr(node, '_attributes'):
 
194
            for a in node._attributes:
 
195
                attr = node_dict[a]
 
196
                if attr is None:
 
197
                    continue
 
198
                if a in ("lineno", "col_offset") and not self.lineno:
 
199
                    continue
 
200
                self._string +='\n' +  indent + a + " = " + repr(attr)
 
201
        for field in node._fields or ():
 
202
            attr = node_dict[field]
 
203
            if attr is None:
 
204
                continue
 
205
            if isinstance(attr, list):
 
206
                if not attr:
 
207
                    continue
 
208
                self._string += '\n' + indent + field + ' = ['
 
209
                for elt in attr:
 
210
                    self._native_repr_tree(elt, indent, _done)
 
211
                self._string += '\n' + indent + ']'
 
212
                continue
 
213
            if isinstance(attr, (_Load, _Store, _Del)):
 
214
                continue
 
215
            if isinstance(attr, Node):
 
216
                self._string += '\n' + indent + field + " = "
 
217
                self._native_repr_tree(attr, indent, _done)
 
218
            else:
 
219
                self._string += '\n' + indent + field + " = " + repr(attr)
 
220
 
 
221
 
 
222
    def build_astng_tree(self):
 
223
        """build astng tree from the _ast tree
 
224
        """
 
225
        from logilab.astng.builder import ASTNGBuilder
 
226
        tree = ASTNGBuilder().string_build(self.sourcecode)
 
227
        return tree
 
228
 
 
229
    def astng_tree_repr(self, ids=False):
 
230
        """build the astng tree and return a nice tree representation"""
 
231
        mod = self.build_astng_tree()
 
232
        return mod.repr_tree(ids)
 
233
 
 
234
 
 
235
__all__ = ('LocalsVisitor', 'ASTWalker',)
 
236