PageRenderTime 85ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/logilab-astng-0.23.1/utils.py

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