| Index: third_party/logilab/astroid/utils.py
 | 
| ===================================================================
 | 
| --- third_party/logilab/astroid/utils.py	(revision 0)
 | 
| +++ third_party/logilab/astroid/utils.py	(working copy)
 | 
| @@ -0,0 +1,238 @@
 | 
| +# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 | 
| +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 | 
| +#
 | 
| +# This file is part of astroid.
 | 
| +#
 | 
| +# astroid is free software: you can redistribute it and/or modify it
 | 
| +# under the terms of the GNU Lesser General Public License as published by the
 | 
| +# Free Software Foundation, either version 2.1 of the License, or (at your
 | 
| +# option) any later version.
 | 
| +#
 | 
| +# astroid is distributed in the hope that it will be useful, but
 | 
| +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 | 
| +# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
 | 
| +# for more details.
 | 
| +#
 | 
| +# You should have received a copy of the GNU Lesser General Public License along
 | 
| +# with astroid. If not, see <http://www.gnu.org/licenses/>.
 | 
| +"""this module contains some utilities to navigate in the tree or to
 | 
| +extract information from it
 | 
| +"""
 | 
| +
 | 
| +__docformat__ = "restructuredtext en"
 | 
| +
 | 
| +from astroid.exceptions import AstroidBuildingException
 | 
| +from astroid.builder import parse
 | 
| +
 | 
| +
 | 
| +class ASTWalker(object):
 | 
| +    """a walker visiting a tree in preorder, calling on the handler:
 | 
| +
 | 
| +    * visit_<class name> on entering a node, where class name is the class of
 | 
| +    the node in lower case
 | 
| +
 | 
| +    * leave_<class name> on leaving a node, where class name is the class of
 | 
| +    the node in lower case
 | 
| +    """
 | 
| +
 | 
| +    def __init__(self, handler):
 | 
| +        self.handler = handler
 | 
| +        self._cache = {}
 | 
| +
 | 
| +    def walk(self, node, _done=None):
 | 
| +        """walk on the tree from <node>, getting callbacks from handler"""
 | 
| +        if _done is None:
 | 
| +            _done = set()
 | 
| +        if node in _done:
 | 
| +            raise AssertionError((id(node), node, node.parent))
 | 
| +        _done.add(node)
 | 
| +        self.visit(node)
 | 
| +        for child_node in node.get_children():
 | 
| +            self.handler.set_context(node, child_node)
 | 
| +            assert child_node is not node
 | 
| +            self.walk(child_node, _done)
 | 
| +        self.leave(node)
 | 
| +        assert node.parent is not node
 | 
| +
 | 
| +    def get_callbacks(self, node):
 | 
| +        """get callbacks from handler for the visited node"""
 | 
| +        klass = node.__class__
 | 
| +        methods = self._cache.get(klass)
 | 
| +        if methods is None:
 | 
| +            handler = self.handler
 | 
| +            kid = klass.__name__.lower()
 | 
| +            e_method = getattr(handler, 'visit_%s' % kid,
 | 
| +                               getattr(handler, 'visit_default', None))
 | 
| +            l_method = getattr(handler, 'leave_%s' % kid,
 | 
| +                               getattr(handler, 'leave_default', None))
 | 
| +            self._cache[klass] = (e_method, l_method)
 | 
| +        else:
 | 
| +            e_method, l_method = methods
 | 
| +        return e_method, l_method
 | 
| +
 | 
| +    def visit(self, node):
 | 
| +        """walk on the tree from <node>, getting callbacks from handler"""
 | 
| +        method = self.get_callbacks(node)[0]
 | 
| +        if method is not None:
 | 
| +            method(node)
 | 
| +
 | 
| +    def leave(self, node):
 | 
| +        """walk on the tree from <node>, getting callbacks from handler"""
 | 
| +        method = self.get_callbacks(node)[1]
 | 
| +        if method is not None:
 | 
| +            method(node)
 | 
| +
 | 
| +
 | 
| +class LocalsVisitor(ASTWalker):
 | 
| +    """visit a project by traversing the locals dictionary"""
 | 
| +    def __init__(self):
 | 
| +        ASTWalker.__init__(self, self)
 | 
| +        self._visited = {}
 | 
| +
 | 
| +    def visit(self, node):
 | 
| +        """launch the visit starting from the given node"""
 | 
| +        if node in self._visited:
 | 
| +            return
 | 
| +        self._visited[node] = 1 # FIXME: use set ?
 | 
| +        methods = self.get_callbacks(node)
 | 
| +        if methods[0] is not None:
 | 
| +            methods[0](node)
 | 
| +        if 'locals' in node.__dict__: # skip Instance and other proxy
 | 
| +            for local_node in node.values():
 | 
| +                self.visit(local_node)
 | 
| +        if methods[1] is not None:
 | 
| +            return methods[1](node)
 | 
| +
 | 
| +
 | 
| +def _check_children(node):
 | 
| +    """a helper function to check children - parent relations"""
 | 
| +    for child in node.get_children():
 | 
| +        ok = False
 | 
| +        if child is None:
 | 
| +            print "Hm, child of %s is None" % node
 | 
| +            continue
 | 
| +        if not hasattr(child, 'parent'):
 | 
| +            print " ERROR: %s has child %s %x with no parent" % (
 | 
| +                node, child, id(child))
 | 
| +        elif not child.parent:
 | 
| +            print " ERROR: %s has child %s %x with parent %r" % (
 | 
| +                node, child, id(child), child.parent)
 | 
| +        elif child.parent is not node:
 | 
| +            print " ERROR: %s %x has child %s %x with wrong parent %s" % (
 | 
| +                node, id(node), child, id(child), child.parent)
 | 
| +        else:
 | 
| +            ok = True
 | 
| +        if not ok:
 | 
| +            print "lines;", node.lineno, child.lineno
 | 
| +            print "of module", node.root(), node.root().name
 | 
| +            raise AstroidBuildingException
 | 
| +        _check_children(child)
 | 
| +
 | 
| +
 | 
| +class TreeTester(object):
 | 
| +    '''A helper class to see _ast tree and compare with astroid tree
 | 
| +
 | 
| +    indent: string for tree indent representation
 | 
| +    lineno: bool to tell if we should print the line numbers
 | 
| +
 | 
| +    >>> tester = TreeTester('print')
 | 
| +    >>> print tester.native_tree_repr()
 | 
| +
 | 
| +    <Module>
 | 
| +    .   body = [
 | 
| +    .   <Print>
 | 
| +    .   .   nl = True
 | 
| +    .   ]
 | 
| +    >>> print tester.astroid_tree_repr()
 | 
| +    Module()
 | 
| +        body = [
 | 
| +        Print()
 | 
| +            dest =
 | 
| +            values = [
 | 
| +            ]
 | 
| +        ]
 | 
| +    '''
 | 
| +
 | 
| +    indent = '.   '
 | 
| +    lineno = False
 | 
| +
 | 
| +    def __init__(self, sourcecode):
 | 
| +        self._string = ''
 | 
| +        self.sourcecode = sourcecode
 | 
| +        self._ast_node = None
 | 
| +        self.build_ast()
 | 
| +
 | 
| +    def build_ast(self):
 | 
| +        """build the _ast tree from the source code"""
 | 
| +        self._ast_node = parse(self.sourcecode)
 | 
| +
 | 
| +    def native_tree_repr(self, node=None, indent=''):
 | 
| +        """get a nice representation of the _ast tree"""
 | 
| +        self._string = ''
 | 
| +        if node is None:
 | 
| +            node = self._ast_node
 | 
| +        self._native_repr_tree(node, indent)
 | 
| +        return self._string
 | 
| +
 | 
| +
 | 
| +    def _native_repr_tree(self, node, indent, _done=None):
 | 
| +        """recursive method for the native tree representation"""
 | 
| +        from _ast import Load as _Load, Store as _Store, Del as _Del
 | 
| +        from _ast import AST as Node
 | 
| +        if _done is None:
 | 
| +            _done = set()
 | 
| +        if node in _done:
 | 
| +            self._string += '\nloop in tree: %r (%s)' % (
 | 
| +                node, getattr(node, 'lineno', None))
 | 
| +            return
 | 
| +        _done.add(node)
 | 
| +        self._string += '\n' + indent +  '<%s>' % node.__class__.__name__
 | 
| +        indent += self.indent
 | 
| +        if not hasattr(node, '__dict__'):
 | 
| +            self._string += '\n' + self.indent + " ** node has no __dict__ " + str(node)
 | 
| +            return
 | 
| +        node_dict = node.__dict__
 | 
| +        if hasattr(node, '_attributes'):
 | 
| +            for a in node._attributes:
 | 
| +                attr = node_dict[a]
 | 
| +                if attr is None:
 | 
| +                    continue
 | 
| +                if a in ("lineno", "col_offset") and not self.lineno:
 | 
| +                    continue
 | 
| +                self._string += '\n' +  indent + a + " = " + repr(attr)
 | 
| +        for field in node._fields or ():
 | 
| +            attr = node_dict[field]
 | 
| +            if attr is None:
 | 
| +                continue
 | 
| +            if isinstance(attr, list):
 | 
| +                if not attr:
 | 
| +                    continue
 | 
| +                self._string += '\n' + indent + field + ' = ['
 | 
| +                for elt in attr:
 | 
| +                    self._native_repr_tree(elt, indent, _done)
 | 
| +                self._string += '\n' + indent + ']'
 | 
| +                continue
 | 
| +            if isinstance(attr, (_Load, _Store, _Del)):
 | 
| +                continue
 | 
| +            if isinstance(attr, Node):
 | 
| +                self._string += '\n' + indent + field + " = "
 | 
| +                self._native_repr_tree(attr, indent, _done)
 | 
| +            else:
 | 
| +                self._string += '\n' + indent + field + " = " + repr(attr)
 | 
| +
 | 
| +
 | 
| +    def build_astroid_tree(self):
 | 
| +        """build astroid tree from the _ast tree
 | 
| +        """
 | 
| +        from astroid.builder import AstroidBuilder
 | 
| +        tree = AstroidBuilder().string_build(self.sourcecode)
 | 
| +        return tree
 | 
| +
 | 
| +    def astroid_tree_repr(self, ids=False):
 | 
| +        """build the astroid tree and return a nice tree representation"""
 | 
| +        mod = self.build_astroid_tree()
 | 
| +        return mod.repr_tree(ids)
 | 
| +
 | 
| +
 | 
| +__all__ = ('LocalsVisitor', 'ASTWalker',)
 | 
| +
 | 
| 
 |