1
# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
2
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
4
# This file is part of logilab-astng.
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.
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
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
22
__docformat__ = "restructuredtext en"
24
from logilab.astng.exceptions import ASTNGBuildingException
25
from logilab.astng.builder import parse
29
"""a walker visiting a tree in preorder, calling on the handler:
31
* visit_<class name> on entering a node, where class name is the class of
32
the node in lower case
34
* leave_<class name> on leaving a node, where class name is the class of
35
the node in lower case
38
def __init__(self, handler):
39
self.handler = handler
42
def walk(self, node, _done=None):
43
"""walk on the tree from <node>, getting callbacks from handler"""
47
raise AssertionError((id(node), node, node.parent))
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)
55
assert node.parent is not node
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)
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)
70
e_method, l_method = methods
71
return e_method, l_method
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:
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:
86
class LocalsVisitor(ASTWalker):
87
"""visit a project by traversing the locals dictionary"""
89
ASTWalker.__init__(self, self)
92
def visit(self, node):
93
"""launch the visit starting from the given node"""
94
if node in self._visited:
96
self._visited[node] = 1 # FIXME: use set ?
97
methods = self.get_callbacks(node)
98
if methods[0] is not None:
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)
107
def _check_children(node):
108
"""a helper function to check children - parent relations"""
109
for child in node.get_children():
112
print "Hm, child of %s is None" % node
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)
124
print "lines;", node.lineno, child.lineno
125
print "of module", node.root(), node.root().name
126
raise ASTNGBuildingException
127
_check_children(child)
130
class TreeTester(object):
131
'''A helper class to see _ast tree and compare with astng tree
133
indent: string for tree indent representation
134
lineno: bool to tell if we should print the line numbers
136
>>> tester = TreeTester('print')
137
>>> print tester.native_tree_repr()
144
>>> print tester.astng_tree_repr()
157
def __init__(self, sourcecode):
159
self.sourcecode = sourcecode
160
self._ast_node = None
164
"""build the _ast tree from the source code"""
165
self._ast_node = parse(self.sourcecode)
167
def native_tree_repr(self, node=None, indent=''):
168
"""get a nice representation of the _ast tree"""
171
node = self._ast_node
172
self._native_repr_tree(node, indent)
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
183
self._string += '\nloop in tree: %r (%s)' % (node,
184
getattr(node, 'lineno', None))
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)
192
node_dict = node.__dict__
193
if hasattr(node, '_attributes'):
194
for a in node._attributes:
198
if a in ("lineno", "col_offset") and not self.lineno:
200
self._string +='\n' + indent + a + " = " + repr(attr)
201
for field in node._fields or ():
202
attr = node_dict[field]
205
if isinstance(attr, list):
208
self._string += '\n' + indent + field + ' = ['
210
self._native_repr_tree(elt, indent, _done)
211
self._string += '\n' + indent + ']'
213
if isinstance(attr, (_Load, _Store, _Del)):
215
if isinstance(attr, Node):
216
self._string += '\n' + indent + field + " = "
217
self._native_repr_tree(attr, indent, _done)
219
self._string += '\n' + indent + field + " = " + repr(attr)
222
def build_astng_tree(self):
223
"""build astng tree from the _ast tree
225
from logilab.astng.builder import ASTNGBuilder
226
tree = ASTNGBuilder().string_build(self.sourcecode)
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)
235
__all__ = ('LocalsVisitor', 'ASTWalker',)