Commits

vrmg committed 5ba6071 Merge

merged w/local

  • Participants
  • Parent commits 8f8896e, 31ca4e1

Comments (0)

Files changed (160)

File ftplugin/python/logilab/__init__.py

+"""generated file, don't modify or your data will be lost"""
+try:
+    __import__('pkg_resources').declare_namespace(__name__)
+except ImportError:
+    pass

File ftplugin/python/logilab/__init__.pyc

Binary file added.

File ftplugin/python/logilab/astng/__init__.py

+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+# copyright 2003-2010 Sylvain Thenault, all rights reserved.
+# contact mailto:thenault@gmail.com
+#
+# This file is part of logilab-astng.
+#
+# logilab-astng 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.
+#
+# logilab-astng 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 logilab-astng. If not, see <http://www.gnu.org/licenses/>.
+"""Python Abstract Syntax Tree New Generation
+
+The aim of this module is to provide a common base representation of
+python source code for projects such as pychecker, pyreverse,
+pylint... Well, actually the development of this library is essentially
+governed by pylint's needs.
+
+It extends class defined in the compiler.ast [1] module with some
+additional methods and attributes. Instance attributes are added by a
+builder object, which can either generate extended ast (let's call
+them astng ;) by visiting an existent ast tree or by inspecting living
+object. Methods are added by monkey patching ast classes.
+
+Main modules are:
+
+* nodes and scoped_nodes for more information about methods and
+  attributes added to different node classes
+
+* the manager contains a high level object to get astng trees from
+  source files and living objects. It maintains a cache of previously
+  constructed tree for quick access
+
+* builder contains the class responsible to build astng trees
+"""
+__doctype__ = "restructuredtext en"
+
+import sys
+if sys.version_info >= (3, 0):
+    BUILTINS_MODULE = 'builtins'
+else:
+    BUILTINS_MODULE = '__builtin__'
+
+# WARNING: internal imports order matters !
+
+# make all exception classes accessible from astng package
+from logilab.astng.exceptions import *
+
+# make all node classes accessible from astng package
+from logilab.astng.nodes import *
+
+# trigger extra monkey-patching
+from logilab.astng import inference
+
+# more stuff available
+from logilab.astng import raw_building
+from logilab.astng.bases import YES, Instance, BoundMethod, UnboundMethod
+from logilab.astng.node_classes import are_exclusive, unpack_infer
+from logilab.astng.scoped_nodes import builtin_lookup
+
+# make a manager instance (borg) as well as Project and Package classes
+# accessible from astng package
+from logilab.astng.manager import ASTNGManager, Project
+MANAGER = ASTNGManager()
+del ASTNGManager

File ftplugin/python/logilab/astng/__init__.pyc

Binary file added.

File ftplugin/python/logilab/astng/__pkginfo__.py

+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+# copyright 2003-2010 Sylvain Thenault, all rights reserved.
+# contact mailto:thenault@gmail.com
+#
+# This file is part of logilab-astng.
+#
+# logilab-astng 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.
+#
+# logilab-astng 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 logilab-astng. If not, see <http://www.gnu.org/licenses/>.
+"""logilab.astng packaging information"""
+
+distname = 'logilab-astng'
+
+modname = 'astng'
+subpackage_of = 'logilab'
+
+numversion = (0, 22, 0)
+version = '.'.join([str(num) for num in numversion])
+
+install_requires = ['logilab-common >= 0.53.0']
+
+license = 'LGPL'
+
+author = 'Logilab'
+author_email = 'python-projects@lists.logilab.org'
+mailinglist = "mailto://%s" % author_email
+web = "http://www.logilab.org/project/%s" % distname
+ftp = "ftp://ftp.logilab.org/pub/%s" % modname
+
+description = "rebuild a new abstract syntax tree from Python's ast"
+
+from os.path import join
+include_dirs = [join('test', 'regrtest_data'),
+                join('test', 'data'), join('test', 'data2')]

File ftplugin/python/logilab/astng/__pkginfo__.pyc

Binary file added.

File ftplugin/python/logilab/astng/as_string.py

+# This program 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 of the License, or (at your option) any later
+# version.
+#
+# This program 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
+# this program; if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+# copyright 2003-2010 Sylvain Thenault, all rights reserved.
+# contact mailto:thenault@gmail.com
+#
+# This file is part of logilab-astng.
+#
+# logilab-astng 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.
+#
+# logilab-astng 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 logilab-astng. If not, see <http://www.gnu.org/licenses/>.
+"""This module renders ASTNG nodes to string representation.
+It will probably not work on compiler.ast or _ast trees.
+"""
+import sys
+
+
+INDENT = '    ' # 4 spaces ; keep indentation variable
+
+
+def _import_string(names):
+    """return a list of (name, asname) formatted as a string"""
+    _names = []
+    for name, asname in names:
+        if asname is not None:
+            _names.append('%s as %s' % (name, asname))
+        else:
+            _names.append(name)
+    return  ', '.join(_names)
+
+
+class AsStringVisitor(object):
+    """Visitor to render an ASTNG node as string """
+
+    def __call__(self, node):
+        """Makes this visitor behave as a simple function"""
+        return node.accept(self)
+    
+    def _stmt_list(self, stmts):
+        """return a list of nodes to string"""
+        stmts = '\n'.join([nstr for nstr in [n.accept(self) for n in stmts] if nstr])
+        return INDENT + stmts.replace('\n', '\n'+INDENT)
+
+    
+    ## visit_<node> methods ###########################################
+
+    def visit_arguments(self, node):
+        """return an astng.Function node as string"""
+        return node.format_args()
+
+    def visit_assattr(self, node):
+        """return an astng.AssAttr node as string"""
+        return self.visit_getattr(node)
+
+    def visit_assert(self, node):
+        """return an astng.Assert node as string"""
+        if node.fail:
+            return 'assert %s, %s' % (node.test.accept(self),
+                                        node.fail.accept(self))
+        return 'assert %s' % node.test.accept(self)
+
+    def visit_assname(self, node):
+        """return an astng.AssName node as string"""
+        return node.name
+
+    def visit_assign(self, node):
+        """return an astng.Assign node as string"""
+        lhs = ' = '.join([n.accept(self) for n in node.targets])
+        return '%s = %s' % (lhs, node.value.accept(self))
+    
+    def visit_augassign(self, node):
+        """return an astng.AugAssign node as string"""
+        return '%s %s %s' % (node.target.accept(self), node.op, node.value.accept(self))
+    
+    def visit_backquote(self, node):
+        """return an astng.Backquote node as string"""
+        return '`%s`' % node.value.accept(self)
+    
+    def visit_binop(self, node):
+        """return an astng.BinOp node as string"""
+        return '(%s) %s (%s)' % (node.left.accept(self), node.op, node.right.accept(self))
+
+    def visit_boolop(self, node):
+        """return an astng.BoolOp node as string"""
+        return (' %s ' % node.op).join(['(%s)' % n.accept(self)
+                                            for n in node.values])
+    
+    def visit_break(self, node):
+        """return an astng.Break node as string"""
+        return 'break'
+    
+    def visit_callfunc(self, node):
+        """return an astng.CallFunc node as string"""
+        expr_str = node.func.accept(self)
+        args = [arg.accept(self) for arg in node.args]
+        if node.starargs:
+            args.append( '*' + node.starargs.accept(self))
+        if node.kwargs:
+            args.append( '**' + node.kwargs.accept(self))
+        return '%s(%s)' % (expr_str, ', '.join(args))
+    
+    def visit_class(self, node):
+        """return an astng.Class node as string"""
+        decorate = node.decorators and node.decorators.accept(self)  or ''
+        bases =  ', '.join([n.accept(self) for n in node.bases])
+        bases = bases and '(%s)' % bases or ''
+        docs = node.doc and '\n%s"""%s"""' % (INDENT, node.doc) or ''
+        return '\n\n%sclass %s%s:%s\n%s\n' % (decorate, node.name, bases, docs,
+                                            self._stmt_list( node.body))
+    
+    def visit_compare(self, node):
+        """return an astng.Compare node as string"""
+        rhs_str = ' '.join(['%s %s' % (op, expr.accept(self))
+                            for op, expr in node.ops])
+        return '%s %s' % (node.left.accept(self), rhs_str)
+
+    def visit_comprehension(self, node):
+        """return an astng.Comprehension node as string"""
+        ifs = ''.join([ ' if %s' % n.accept(self) for n in node.ifs])
+        return 'for %s in %s%s' % (node.target.accept(self),
+                                    node.iter.accept(self), ifs )
+
+    def visit_const(self, node):
+        """return an astng.Const node as string"""
+        return repr(node.value)
+    
+    def visit_continue(self, node):
+        """return an astng.Continue node as string"""
+        return 'continue'
+    
+    def visit_delete(self, node): # XXX check if correct
+        """return an astng.Delete node as string"""
+        return 'del %s' % ', '.join([child.accept(self) 
+                                for child in node.targets])
+
+    def visit_delattr(self, node):
+        """return an astng.DelAttr node as string"""
+        return self.visit_getattr(node)
+
+    def visit_delname(self, node):
+        """return an astng.DelName node as string"""
+        return node.name
+
+    def visit_decorators(self, node):
+        """return an astng.Decorators node as string"""
+        return '@%s\n' % '\n@'.join([item.accept(self) for item in node.nodes])
+
+    def visit_dict(self, node):
+        """return an astng.Dict node as string"""
+        return '{%s}' % ', '.join(['%s: %s' % (key.accept(self), 
+                            value.accept(self)) for key, value in node.items])
+
+    def visit_dictcomp(self, node):
+        """return an astng.DictComp node as string"""
+        return '{%s: %s %s}' % (node.key.accept(self), node.value.accept(self),
+                ' '.join([n.accept(self) for n in node.generators]))
+
+    def visit_discard(self, node):
+        """return an astng.Discard node as string"""
+        return node.value.accept(self)
+
+    def visit_emptynode(self, node):
+        """dummy method for visiting an Empty node"""
+        return ''
+
+    def visit_excepthandler(self, node):
+        if node.type:
+            if node.name:
+                excs = 'except %s, %s' % (node.type.accept(self),
+                                        node.name.accept(self))
+            else:
+                excs = 'except %s' % node.type.accept(self)
+        else:
+            excs = 'except'
+        return '%s:\n%s' % (excs, self._stmt_list(node.body))
+    
+    def visit_ellipsis(self, node):
+        """return an astng.Ellipsis node as string"""
+        return '...'
+    
+    def visit_empty(self, node):
+        """return an Empty node as string"""
+        return ''
+    
+    def visit_exec(self, node):
+        """return an astng.Exec node as string"""
+        if node.locals:
+            return 'exec %s in %s, %s' % (node.expr.accept(self),
+                                          node.locals.accept(self),
+                                          node.globals.accept(self))
+        if node.globals:
+            return 'exec %s in %s' % (node.expr.accept(self),
+                                      node.globals.accept(self))
+        return 'exec %s' % node.expr.accept(self)
+
+    def visit_extslice(self, node):
+        """return an astng.ExtSlice node as string"""
+        return ','.join( [dim.accept(self) for dim in node.dims] )
+
+    def visit_for(self, node):
+        """return an astng.For node as string"""
+        fors = 'for %s in %s:\n%s' % (node.target.accept(self),
+                                    node.iter.accept(self),
+                                    self._stmt_list( node.body))
+        if node.orelse:
+            fors = '%s\nelse:\n%s' % (fors, self._stmt_list(node.orelse))
+        return fors
+    
+    def visit_from(self, node):
+        """return an astng.From node as string"""
+        return 'from %s import %s' % ('.' * (node.level or 0) + node.modname,
+                                      _import_string(node.names))
+    
+    def visit_function(self, node):
+        """return an astng.Function node as string"""
+        decorate = node.decorators and node.decorators.accept(self)  or ''
+        docs = node.doc and '\n%s"""%s"""' % (INDENT, node.doc) or ''
+        return '\n%sdef %s(%s):%s\n%s' % (decorate, node.name, node.args.accept(self),
+                                        docs, self._stmt_list(node.body))
+    
+    def visit_genexpr(self, node):
+        """return an astng.GenExpr node as string"""
+        return '(%s %s)' % (node.elt.accept(self), ' '.join([n.accept(self)
+                                                    for n in node.generators]))
+    
+    def visit_getattr(self, node):
+        """return an astng.Getattr node as string"""
+        return '%s.%s' % (node.expr.accept(self), node.attrname)
+
+    def visit_global(self, node):
+        """return an astng.Global node as string"""
+        return 'global %s' % ', '.join(node.names)
+    
+    def visit_if(self, node):
+        """return an astng.If node as string"""
+        ifs = ['if %s:\n%s' % (node.test.accept(self), self._stmt_list(node.body))]
+        if node.orelse:# XXX use elif ???
+            ifs.append('else:\n%s' % self._stmt_list(node.orelse))
+        return '\n'.join(ifs)
+  
+    def visit_ifexp(self, node):
+        """return an astng.IfExp node as string"""
+        return '%s if %s else %s' % (node.body.accept(self),
+                node.test.accept(self), node.orelse.accept(self))
+
+    def visit_import(self, node):
+        """return an astng.Import node as string"""
+        return 'import %s' % _import_string(node.names)
+    
+    def visit_keyword(self, node):
+        """return an astng.Keyword node as string"""
+        return '%s=%s' % (node.arg, node.value.accept(self))
+    
+    def visit_lambda(self, node):
+        """return an astng.Lambda node as string"""
+        return 'lambda %s: %s' % (node.args.accept(self), node.body.accept(self))
+    
+    def visit_list(self, node):
+        """return an astng.List node as string"""
+        return '[%s]' % ', '.join([child.accept(self) for child in node.elts])
+    
+    def visit_listcomp(self, node):
+        """return an astng.ListComp node as string"""
+        return '[%s %s]' % (node.elt.accept(self), ' '.join([n.accept(self)
+                                                for n in node.generators]))
+
+    def visit_module(self, node):
+        """return an astng.Module node as string"""
+        docs = node.doc and '"""%s"""\n\n' % node.doc or ''
+        return docs + '\n'.join([n.accept(self) for n in node.body]) + '\n\n'
+    
+    def visit_name(self, node):
+        """return an astng.Name node as string"""
+        return node.name
+    
+    def visit_pass(self, node):
+        """return an astng.Pass node as string"""
+        return 'pass'
+    
+    def visit_print(self, node):
+        """return an astng.Print node as string"""
+        nodes = ', '.join([n.accept(self) for n in node.values])
+        if not node.nl:
+            nodes = '%s,' % nodes
+        if node.dest:
+            return 'print >> %s, %s' % (node.dest.accept(self), nodes)
+        return 'print %s' % nodes
+
+    def visit_raise(self, node):
+        """return an astng.Raise node as string"""
+        if node.exc:
+            if node.inst:
+                if node.tback:
+                    return 'raise %s, %s, %s' % (node.exc.accept(self),
+                                                node.inst.accept(self),
+                                                node.tback.accept(self))
+                return 'raise %s, %s' % (node.exc.accept(self),
+                                        node.inst.accept(self))
+            return 'raise %s' % node.exc.accept(self)
+        return 'raise'
+
+    def visit_return(self, node):
+        """return an astng.Return node as string"""
+        if node.value:
+            return 'return %s' % node.value.accept(self)
+        else:
+            return 'return'
+
+    def visit_index(self, node):
+        """return a astng.Index node as string"""
+        return node.value.accept(self)
+
+    def visit_set(self, node):
+        """return an astng.Set node as string"""
+        return '{%s}' % ', '.join([child.accept(self) for child in node.elts])
+
+    def visit_setcomp(self, node):
+        """return an astng.SetComp node as string"""
+        return '{%s %s}' % (node.elt.accept(self), ' '.join([n.accept(self)
+                                                for n in node.generators]))
+
+    def visit_slice(self, node):
+        """return a astng.Slice node as string"""
+        lower = node.lower and node.lower.accept(self) or ''
+        upper = node.upper and node.upper.accept(self) or ''
+        step = node.step and node.step.accept(self) or ''
+        if step:
+            return '%s:%s:%s' % (lower, upper, step)
+        return  '%s:%s' % (lower, upper)
+
+    def visit_subscript(self, node):
+        """return an astng.Subscript node as string"""
+        return '%s[%s]' % (node.value.accept(self), node.slice.accept(self))
+    
+    def visit_tryexcept(self, node):
+        """return an astng.TryExcept node as string"""
+        trys = ['try:\n%s' % self._stmt_list( node.body)]
+        for handler in node.handlers:
+            trys.append(handler.accept(self))
+        if node.orelse:
+            trys.append('else:\n%s' % self._stmt_list(node.orelse))
+        return '\n'.join(trys)
+    
+    def visit_tryfinally(self, node):
+        """return an astng.TryFinally node as string"""
+        return 'try:\n%s\nfinally:\n%s' % (self._stmt_list( node.body),
+                                        self._stmt_list(node.finalbody))
+    
+    def visit_tuple(self, node):
+        """return an astng.Tuple node as string"""
+        return '(%s)' % ', '.join([child.accept(self) for child in node.elts])
+    
+    def visit_unaryop(self, node):
+        """return an astng.UnaryOp node as string"""
+        if node.op == 'not':
+            operator = 'not '
+        else:
+            operator = node.op
+        return '%s%s' % (operator, node.operand.accept(self))
+    
+    def visit_while(self, node):
+        """return an astng.While node as string"""
+        whiles = 'while %s:\n%s' % (node.test.accept(self),
+                                    self._stmt_list(node.body))
+        if node.orelse:
+            whiles = '%s\nelse:\n%s' % (whiles, self._stmt_list(node.orelse))
+        return whiles
+    
+    def visit_with(self, node): # 'with' without 'as' is possible
+        """return an astng.With node as string"""
+        as_var = node.vars and " as (%s)" % (node.vars.accept(self)) or ""
+        withs = 'with (%s)%s:\n%s' % (node.expr.accept(self), as_var,
+                                        self._stmt_list( node.body))
+        return withs
+    
+    def visit_yield(self, node):
+        """yield an ast.Yield node as string"""
+        yi_val = node.value and (" " + node.value.accept(self)) or ""
+        return 'yield' + yi_val
+
+
+class AsStringVisitor3k(AsStringVisitor):
+    """AsStringVisitor3k overwrites some AsStringVisitor methods"""
+
+    def visit_excepthandler(self, node):
+        if node.type:
+            if node.name:
+                excs = 'except %s as %s' % (node.type.accept(self),
+                                        node.name.accept(self))
+            else:
+                excs = 'except %s' % node.type.accept(self)
+        else:
+            excs = 'except'
+        return '%s:\n%s' % (excs, self._stmt_list(node.body))
+
+    def visit_nonlocal(self, node):
+        """return an astng.Nonlocal node as string"""
+        return 'nonlocal %s' % ', '.join(node.names)
+
+    def visit_raise(self, node):
+        """return an astng.Raise node as string"""
+        if node.exc:
+            if node.cause:
+                return 'raise %s from %s' % (node.exc.accept(self),
+                                             node.cause.accept(self))
+            return 'raise %s' % node.exc.accept(self)
+        return 'raise'
+
+    def visit_starred(self, node):
+        """return Starred node as string"""
+        return "*" + node.value.accept(self)
+
+if sys.version_info >= (3, 0):
+    AsStringVisitor = AsStringVisitor3k
+
+# this visitor is stateless, thus it can be reused
+as_string = AsStringVisitor()
+

File ftplugin/python/logilab/astng/as_string.pyc

Binary file added.

File ftplugin/python/logilab/astng/bases.py

+# -*- coding: utf-8 -*-
+# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+# copyright 2003-2010 Sylvain Thenault, all rights reserved.
+# contact mailto:thenault@gmail.com
+#
+# This file is part of logilab-astng.
+#
+# logilab-astng 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.
+#
+# logilab-astng 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 logilab-astng. If not, see <http://www.gnu.org/licenses/>.
+"""This module contains base classes and functions for the nodes and some
+inference utils.
+"""
+
+__docformat__ = "restructuredtext en"
+
+
+from logilab.common.compat import builtins
+
+from logilab.astng import BUILTINS_MODULE
+from logilab.astng.exceptions import InferenceError, ASTNGError, \
+                                       NotFoundError, UnresolvableName
+from logilab.astng.as_string import as_string
+
+BUILTINS_NAME = builtins.__name__
+
+class Proxy(object):
+    """a simple proxy object"""
+    _proxied = None
+
+    def __init__(self, proxied=None):
+        if proxied is not None:
+            self._proxied = proxied
+
+    def __getattr__(self, name):
+        if name == '_proxied':
+            return getattr(self.__class__, '_proxied')
+        if name in self.__dict__:
+            return self.__dict__[name]
+        return getattr(self._proxied, name)
+
+    def infer(self, context=None):
+        yield self
+
+
+# Inference ##################################################################
+
+class InferenceContext(object):
+    __slots__ = ('path', 'lookupname', 'callcontext', 'boundnode')
+
+    def __init__(self, path=None):
+        if path is None:
+            self.path = set()
+        else:
+            self.path = path
+        self.lookupname = None
+        self.callcontext = None
+        self.boundnode = None
+
+    def push(self, node):
+        name = self.lookupname
+        if (node, name) in self.path:
+            raise StopIteration()
+        self.path.add( (node, name) )
+
+    def clone(self):
+        # XXX copy lookupname/callcontext ?
+        clone = InferenceContext(self.path)
+        clone.callcontext = self.callcontext
+        clone.boundnode = self.boundnode
+        return clone
+
+def copy_context(context):
+    if context is not None:
+        return context.clone()
+    else:
+        return InferenceContext()
+
+
+def _infer_stmts(stmts, context, frame=None):
+    """return an iterator on statements inferred by each statement in <stmts>
+    """
+    stmt = None
+    infered = False
+    if context is not None:
+        name = context.lookupname
+        context = context.clone()
+    else:
+        name = None
+        context = InferenceContext()
+    for stmt in stmts:
+        if stmt is YES:
+            yield stmt
+            infered = True
+            continue
+        context.lookupname = stmt._infer_name(frame, name)
+        try:
+            for infered in stmt.infer(context):
+                yield infered
+                infered = True
+        except UnresolvableName:
+            continue
+        except InferenceError:
+            yield YES
+            infered = True
+    if not infered:
+        raise InferenceError(str(stmt))
+
+
+# special inference objects (e.g. may be returned as nodes by .infer()) #######
+
+class _Yes(object):
+    """a yes object"""
+    def __repr__(self):
+        return 'YES'
+    def __getattribute__(self, name):
+        if name.startswith('__') and name.endswith('__'):
+            # to avoid inspection pb
+            return super(_Yes, self).__getattribute__(name)
+        return self
+    def __call__(self, *args, **kwargs):
+        return self
+
+
+YES = _Yes()
+
+
+class Instance(Proxy):
+    """a special node representing a class instance"""
+    def getattr(self, name, context=None, lookupclass=True):
+        try:
+            values = self._proxied.instance_attr(name, context)
+        except NotFoundError:
+            if name == '__class__':
+                return [self._proxied]
+            if lookupclass:
+                # class attributes not available through the instance
+                # unless they are explicitly defined
+                if name in ('__name__', '__bases__', '__mro__', '__subclasses__'):
+                    return self._proxied.local_attr(name)
+                return self._proxied.getattr(name, context)
+            raise NotFoundError(name)
+        # since we've no context information, return matching class members as
+        # well
+        if lookupclass:
+            try:
+                return values + self._proxied.getattr(name, context)
+            except NotFoundError:
+                pass
+        return values
+
+    def igetattr(self, name, context=None):
+        """inferred getattr"""
+        try:
+            # XXX frame should be self._proxied, or not ?
+            get_attr = self.getattr(name, context, lookupclass=False)
+            return _infer_stmts(self._wrap_attr(get_attr, context), context,
+                                frame=self)
+        except NotFoundError:
+            try:
+                # fallback to class'igetattr since it has some logic to handle
+                # descriptors
+                return self._wrap_attr(self._proxied.igetattr(name, context),
+                                       context)
+            except NotFoundError:
+                raise InferenceError(name)
+
+    def _wrap_attr(self, attrs, context=None):
+        """wrap bound methods of attrs in a InstanceMethod proxies"""
+        for attr in attrs:
+            if isinstance(attr, UnboundMethod):
+                if BUILTINS_NAME + '.property' in attr.decoratornames():
+                    for infered in attr.infer_call_result(self, context):
+                        yield infered
+                else:
+                    yield BoundMethod(attr, self)
+            else:
+                yield attr
+
+    def infer_call_result(self, caller, context=None):
+        """infer what a class instance is returning when called"""
+        infered = False
+        for node in self._proxied.igetattr('__call__', context):
+            for res in node.infer_call_result(caller, context):
+                infered = True
+                yield res
+        if not infered:
+            raise InferenceError()
+
+    def __repr__(self):
+        return '<Instance of %s.%s at 0x%s>' % (self._proxied.root().name,
+                                                self._proxied.name,
+                                                id(self))
+    def __str__(self):
+        return 'Instance of %s.%s' % (self._proxied.root().name,
+                                      self._proxied.name)
+
+    def callable(self):
+        try:
+            self._proxied.getattr('__call__')
+            return True
+        except NotFoundError:
+            return False
+
+    def pytype(self):
+        return self._proxied.qname()
+
+    def display_type(self):
+        return 'Instance of'
+
+
+class UnboundMethod(Proxy):
+    """a special node representing a method not bound to an instance"""
+    def __repr__(self):
+        frame = self._proxied.parent.frame()
+        return '<%s %s of %s at 0x%s' % (self.__class__.__name__,
+                                         self._proxied.name,
+                                         frame.qname(), id(self))
+
+    def is_bound(self):
+        return False
+
+    def getattr(self, name, context=None):
+        if name == 'im_func':
+            return [self._proxied]
+        return super(UnboundMethod, self).getattr(name, context)
+
+    def igetattr(self, name, context=None):
+        if name == 'im_func':
+            return iter((self._proxied,))
+        return super(UnboundMethod, self).igetattr(name, context)
+
+    def infer_call_result(self, caller, context):
+        # If we're unbound method __new__ of builtin object, the result is an
+        # instance of the class given as first argument.
+        if (self._proxied.name == '__new__' and
+                self._proxied.parent.frame().qname() == '%s.object' % BUILTINS_MODULE):
+            return (x is YES and x or Instance(x) for x in caller.args[0].infer())
+        return self._proxied.infer_call_result(caller, context)
+
+
+class BoundMethod(UnboundMethod):
+    """a special node representing a method bound to an instance"""
+    def __init__(self,  proxy, bound):
+        UnboundMethod.__init__(self, proxy)
+        self.bound = bound
+
+    def is_bound(self):
+        return True
+
+    def infer_call_result(self, caller, context):
+        context = context.clone()
+        context.boundnode = self.bound
+        return self._proxied.infer_call_result(caller, context)
+
+
+class Generator(Instance):
+    """a special node representing a generator"""
+    def callable(self):
+        return True
+
+    def pytype(self):
+        return '%s.generator' % BUILTINS_MODULE
+
+    def display_type(self):
+        return 'Generator'
+
+    def __repr__(self):
+        return '<Generator(%s) l.%s at 0x%s>' % (self._proxied.name, self.lineno, id(self))
+
+    def __str__(self):
+        return 'Generator(%s)' % (self._proxied.name)
+
+
+# decorators ##################################################################
+
+def path_wrapper(func):
+    """return the given infer function wrapped to handle the path"""
+    def wrapped(node, context=None, _func=func, **kwargs):
+        """wrapper function handling context"""
+        if context is None:
+            context = InferenceContext()
+        context.push(node)
+        yielded = set()
+        for res in _func(node, context, **kwargs):
+            # unproxy only true instance, not const, tuple, dict...
+            if res.__class__ is Instance:
+                ares = res._proxied
+            else:
+                ares = res
+            if not ares in yielded:
+                yield res
+                yielded.add(ares)
+    return wrapped
+
+def yes_if_nothing_infered(func):
+    def wrapper(*args, **kwargs):
+        infered = False
+        for node in func(*args, **kwargs):
+            infered = True
+            yield node
+        if not infered:
+            yield YES
+    return wrapper
+
+def raise_if_nothing_infered(func):
+    def wrapper(*args, **kwargs):
+        infered = False
+        for node in func(*args, **kwargs):
+            infered = True
+            yield node
+        if not infered:
+            raise InferenceError()
+    return wrapper
+
+
+# Node  ######################################################################
+
+class NodeNG(object):
+    """Base Class for all ASTNG node classes.
+
+    It represents a node of the new abstract syntax tree.
+    """
+    is_statement = False
+    optional_assign = False # True  for For (and for Comprehension if py <3.0)
+    is_function = False # True for Function nodes
+    # attributes below are set by the builder module or by raw factories
+    lineno = None
+    fromlineno = None
+    tolineno = None
+    col_offset = None
+    # parent node in the tree
+    parent = None
+    # attributes containing child node(s) redefined in most concrete classes:
+    _astng_fields = ()
+
+    def _repr_name(self):
+        """return self.name or self.attrname or '' for nice representation"""
+        return getattr(self, 'name', getattr(self, 'attrname', ''))
+
+    def __str__(self):
+        return '%s(%s)' % (self.__class__.__name__, self._repr_name())
+
+    def __repr__(self):
+        return '<%s(%s) l.%s [%s] at Ox%x>' % (self.__class__.__name__,
+                                           self._repr_name(),
+                                           self.fromlineno,
+                                           self.root().name,
+                                           id(self))
+
+
+    def accept(self, visitor):
+        klass = self.__class__.__name__
+        func = getattr(visitor, "visit_" + self.__class__.__name__.lower())
+        return func(self)
+
+    def get_children(self):
+        for field in self._astng_fields:
+            attr = getattr(self, field)
+            if attr is None:
+                continue
+            if isinstance(attr, (list, tuple)):
+                for elt in attr:
+                    yield elt
+            else:
+                yield attr
+
+    def last_child(self):
+        """an optimized version of list(get_children())[-1]"""
+        for field in self._astng_fields[::-1]:
+            attr = getattr(self, field)
+            if not attr: # None or empty listy / tuple
+                continue
+            if isinstance(attr, (list, tuple)):
+                return attr[-1]
+            else:
+                return attr
+        return None
+
+    def parent_of(self, node):
+        """return true if i'm a parent of the given node"""
+        parent = node.parent
+        while parent is not None:
+            if self is parent:
+                return True
+            parent = parent.parent
+        return False
+
+    def statement(self):
+        """return the first parent node marked as statement node"""
+        if self.is_statement:
+            return self
+        return self.parent.statement()
+
+    def frame(self):
+        """return the first parent frame node (i.e. Module, Function or Class)
+        """
+        return self.parent.frame()
+
+    def scope(self):
+        """return the first node defining a new scope (i.e. Module, Function,
+        Class, Lambda but also GenExpr)
+        """
+        return self.parent.scope()
+
+    def root(self):
+        """return the root node of the tree, (i.e. a Module)"""
+        if self.parent:
+            return self.parent.root()
+        return self
+
+    def child_sequence(self, child):
+        """search for the right sequence where the child lies in"""
+        for field in self._astng_fields:
+            node_or_sequence = getattr(self, field)
+            if node_or_sequence is child:
+                return [node_or_sequence]
+            # /!\ compiler.ast Nodes have an __iter__ walking over child nodes
+            if isinstance(node_or_sequence, (tuple, list)) and child in node_or_sequence:
+                return node_or_sequence
+        else:
+            msg = 'Could not found %s in %s\'s children'
+            raise ASTNGError(msg % (repr(child), repr(self)))
+
+    def locate_child(self, child):
+        """return a 2-uple (child attribute name, sequence or node)"""
+        for field in self._astng_fields:
+            node_or_sequence = getattr(self, field)
+            # /!\ compiler.ast Nodes have an __iter__ walking over child nodes
+            if child is node_or_sequence:
+                return field, child
+            if isinstance(node_or_sequence, (tuple, list)) and child in node_or_sequence:
+                return field, node_or_sequence
+        msg = 'Could not found %s in %s\'s children'
+        raise ASTNGError(msg % (repr(child), repr(self)))
+    # FIXME : should we merge child_sequence and locate_child ? locate_child
+    # is only used in are_exclusive, child_sequence one time in pylint.
+
+    def next_sibling(self):
+        """return the next sibling statement"""
+        return self.parent.next_sibling()
+
+    def previous_sibling(self):
+        """return the previous sibling statement"""
+        return self.parent.previous_sibling()
+
+    def nearest(self, nodes):
+        """return the node which is the nearest before this one in the
+        given list of nodes
+        """
+        myroot = self.root()
+        mylineno = self.fromlineno
+        nearest = None, 0
+        for node in nodes:
+            assert node.root() is myroot, \
+                   'nodes %s and %s are not from the same module' % (self, node)
+            lineno = node.fromlineno
+            if node.fromlineno > mylineno:
+                break
+            if lineno > nearest[1]:
+                nearest = node, lineno
+        # FIXME: raise an exception if nearest is None ?
+        return nearest[0]
+
+    def set_line_info(self, lastchild):
+        if self.lineno is None:
+            self.fromlineno = self._fixed_source_line()
+        else:
+            self.fromlineno = self.lineno
+        if lastchild is None:
+            self.tolineno = self.fromlineno
+        else:
+            self.tolineno = lastchild.tolineno
+        return
+        # TODO / FIXME:
+        assert self.fromlineno is not None, self
+        assert self.tolineno is not None, self
+
+    def _fixed_source_line(self):
+        """return the line number where the given node appears
+
+        we need this method since not all nodes have the lineno attribute
+        correctly set...
+        """
+        line = self.lineno
+        _node = self
+        try:
+            while line is None:
+                _node = _node.get_children().next()
+                line = _node.lineno
+        except StopIteration:
+            _node = self.parent
+            while _node and line is None:
+                line = _node.lineno
+                _node = _node.parent
+        return line
+
+    def block_range(self, lineno):
+        """handle block line numbers range for non block opening statements
+        """
+        return lineno, self.tolineno
+
+    def set_local(self, name, stmt):
+        """delegate to a scoped parent handling a locals dictionary"""
+        self.parent.set_local(name, stmt)
+
+    def nodes_of_class(self, klass, skip_klass=None):
+        """return an iterator on nodes which are instance of the given class(es)
+
+        klass may be a class object or a tuple of class objects
+        """
+        if isinstance(self, klass):
+            yield self
+        for child_node in self.get_children():
+            if skip_klass is not None and isinstance(child_node, skip_klass):
+                continue
+            for matching in child_node.nodes_of_class(klass, skip_klass):
+                yield matching
+
+    def _infer_name(self, frame, name):
+        # overridden for From, Import, Global, TryExcept and Arguments
+        return None
+
+    def infer(self, context=None):
+        """we don't know how to resolve a statement by default"""
+        # this method is overridden by most concrete classes
+        raise InferenceError(self.__class__.__name__)
+
+    def infered(self):
+        '''return list of infered values for a more simple inference usage'''
+        return list(self.infer())
+
+    def instanciate_class(self):
+        """instanciate a node if it is a Class node, else return self"""
+        return self
+
+    def has_base(self, node):
+        return False
+
+    def callable(self):
+        return False
+
+    def eq(self, value):
+        return False
+
+    def as_string(self):
+        return as_string(self)
+
+    def repr_tree(self, ids=False):
+        """print a nice astng tree representation.
+
+        :param ids: if true, we also print the ids (usefull for debugging)"""
+        result = []
+        _repr_tree(self, result, ids=ids)
+        return "\n".join(result)
+
+
+class Statement(NodeNG):
+    """Statement node adding a few attributes"""
+    is_statement = True
+
+    def next_sibling(self):
+        """return the next sibling statement"""
+        stmts = self.parent.child_sequence(self)
+        index = stmts.index(self)
+        try:
+            return stmts[index +1]
+        except IndexError:
+            pass
+
+    def previous_sibling(self):
+        """return the previous sibling statement"""
+        stmts = self.parent.child_sequence(self)
+        index = stmts.index(self)
+        if index >= 1:
+            return stmts[index -1]
+
+INDENT = "    "
+
+def _repr_tree(node, result, indent='', _done=None, ids=False):
+    """built a tree representation of a node as a list of lines"""
+    if _done is None:
+        _done = set()
+    if not hasattr(node, '_astng_fields'): # not a astng node
+        return
+    if node in _done:
+        result.append( indent + 'loop in tree: %s' % node )
+        return
+    _done.add(node)
+    node_str = str(node)
+    if ids:
+        node_str += '  . \t%x' % id(node)
+    result.append( indent + node_str )
+    indent += INDENT
+    for field in node._astng_fields:
+        value = getattr(node, field)
+        if isinstance(value, (list, tuple) ):
+            result.append(  indent + field + " = [" )
+            for child in value:
+                if isinstance(child, (list, tuple) ):
+                    # special case for Dict # FIXME
+                    _repr_tree(child[0], result, indent, _done, ids)
+                    _repr_tree(child[1], result, indent, _done, ids)
+                    result.append(indent + ',')
+                else:
+                    _repr_tree(child, result, indent, _done, ids)
+            result.append(  indent + "]" )
+        else:
+            result.append(  indent + field + " = " )
+            _repr_tree(value, result, indent, _done, ids)
+
+

File ftplugin/python/logilab/astng/bases.pyc

Binary file added.

File ftplugin/python/logilab/astng/builder.py

+# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+# copyright 2003-2010 Sylvain Thenault, all rights reserved.
+# contact mailto:thenault@gmail.com
+#
+# This file is part of logilab-astng.
+#
+# logilab-astng 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.
+#
+# logilab-astng 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 logilab-astng. If not, see <http://www.gnu.org/licenses/>.
+"""The ASTNGBuilder makes astng from living object and / or from compiler.ast
+
+With python >= 2.5, the internal _ast module is used instead
+
+The builder is not thread safe and can't be used to parse different sources
+at the same time.
+"""
+
+__docformat__ = "restructuredtext en"
+
+import sys, re
+from os.path import splitext, basename, dirname, exists, abspath
+
+from logilab.common.modutils import modpath_from_file
+
+from logilab.astng.exceptions import ASTNGBuildingException, InferenceError
+from logilab.astng.raw_building import InspectBuilder
+from logilab.astng.rebuilder import TreeRebuilder
+from logilab.astng.manager import ASTNGManager
+from logilab.astng.bases import YES, Instance
+
+from _ast import PyCF_ONLY_AST
+def parse(string):
+    return compile(string, "<string>", 'exec', PyCF_ONLY_AST)
+
+if sys.version_info >= (3, 0):
+    from tokenize import detect_encoding
+
+    def open_source_file(filename):
+        byte_stream = open(filename, 'bU')
+        encoding = detect_encoding(byte_stream.readline)[0]
+        stream = open(filename, 'U', encoding=encoding)
+        try:
+            data = stream.read()
+        except UnicodeError, uex: # wrong encodingg
+            # detect_encoding returns utf-8 if no encoding specified
+            msg = 'Wrong (%s) or no encoding specified' % encoding
+            raise ASTNGBuildingException(msg)
+        return stream, encoding, data
+
+else:
+    import re
+
+    _ENCODING_RGX = re.compile("\s*#+.*coding[:=]\s*([-\w.]+)")
+
+    def _guess_encoding(string):
+        """get encoding from a python file as string or return None if not found
+        """
+        # check for UTF-8 byte-order mark
+        if string.startswith('\xef\xbb\xbf'):
+            return 'UTF-8'
+        for line in string.split('\n', 2)[:2]:
+            # check for encoding declaration
+            match = _ENCODING_RGX.match(line)
+            if match is not None:
+                return match.group(1)
+
+    def open_source_file(filename):
+        """get data for parsing a file"""
+        stream = open(filename, 'U')
+        data = stream.read()
+        encoding = _guess_encoding(data)
+        return stream, encoding, data
+
+# ast NG builder ##############################################################
+
+MANAGER = ASTNGManager()
+
+class ASTNGBuilder(InspectBuilder):
+    """provide astng building methods"""
+    rebuilder = TreeRebuilder()
+
+    def __init__(self, manager=None):
+        self._manager = manager or MANAGER
+
+    def module_build(self, module, modname=None):
+        """build an astng from a living module instance
+        """
+        node = None
+        path = getattr(module, '__file__', None)
+        if path is not None:
+            path_, ext = splitext(module.__file__)
+            if ext in ('.py', '.pyc', '.pyo') and exists(path_ + '.py'):
+                node = self.file_build(path_ + '.py', modname)
+        if node is None:
+            # this is a built-in module
+            # get a partial representation by introspection
+            node = self.inspect_build(module, modname=modname, path=path)
+        return node
+
+    def file_build(self, path, modname=None):
+        """build astng from a source code file (i.e. from an ast)
+
+        path is expected to be a python source file
+        """
+        try:
+            stream, encoding, data = open_source_file(path)
+        except IOError, exc:
+            msg = 'Unable to load file %r (%s)' % (path, exc)
+            raise ASTNGBuildingException(msg)
+        except SyntaxError, exc: # py3k encoding specification error
+            raise ASTNGBuildingException(exc)
+        except LookupError, exc: # unknown encoding
+            raise ASTNGBuildingException(exc)
+        # get module name if necessary, *before modifying sys.path*
+        if modname is None:
+            try:
+                modname = '.'.join(modpath_from_file(path))
+            except ImportError:
+                modname = splitext(basename(path))[0]
+        # build astng representation
+        try:
+            sys.path.insert(0, dirname(path)) # XXX (syt) iirk
+            node = self.string_build(data, modname, path)
+        finally:
+            sys.path.pop(0)
+        node.file_encoding = encoding
+        node.file_stream = stream
+        return node
+
+    def string_build(self, data, modname='', path=None):
+        """build astng from source code string and return rebuilded astng"""
+        module = self._data_build(data, modname, path)
+        if self._manager is not None:
+            self._manager.astng_cache[module.name] = module
+        # post tree building steps after we stored the module in the cache:
+        for from_node in module._from_nodes:
+            self.add_from_names_to_locals(from_node)
+        # handle delayed assattr nodes
+        for delayed in module._delayed_assattr:
+            self.delayed_assattr(delayed)
+        return module
+
+    def _data_build(self, data, modname, path):
+        """build tree node from data and add some informations"""
+        # this method could be wrapped with a pickle/cache function
+        node = parse(data + '\n')
+        if path is not None:
+            node_file = abspath(path)
+        else:
+            node_file = '<?>'
+        if modname.endswith('.__init__'):
+            modname = modname[:-9]
+            package = True
+        else:
+            package = path and path.find('__init__.py') > -1 or False
+        self.rebuilder.init()
+        module = self.rebuilder.visit_module(node, modname, package)
+        module.file = module.path = node_file
+        module._from_nodes = self.rebuilder._from_nodes
+        module._delayed_assattr = self.rebuilder._delayed_assattr
+        return module
+
+    def add_from_names_to_locals(self, node):
+        """store imported names to the locals;
+        resort the locals if coming from a delayed node
+        """
+
+        _key_func = lambda node: node.fromlineno
+        def sort_locals(my_list):
+            my_list.sort(key=_key_func)
+        for (name, asname) in node.names:
+            if name == '*':
+                try:
+                    imported = node.root().import_module(node.modname)
+                except ASTNGBuildingException:
+                    continue
+                for name in imported.wildcard_import_names():
+                    node.parent.set_local(name, node)
+                    sort_locals(node.parent.scope().locals[name])
+            else:
+                node.parent.set_local(asname or name, node)
+                sort_locals(node.parent.scope().locals[asname or name])
+
+    def delayed_assattr(self, node):
+        """visit a AssAttr node -> add name to locals, handle members
+        definition
+        """
+        try:
+            frame = node.frame()
+            for infered in node.expr.infer():
+                if infered is YES:
+                    continue
+                try:
+                    if infered.__class__ is Instance:
+                        infered = infered._proxied
+                        iattrs = infered.instance_attrs
+                    elif isinstance(infered, Instance):
+                        # Const, Tuple, ... we may be wrong, may be not, but
+                        # anyway we don't want to pollute builtin's namespace
+                        continue
+                    elif infered.is_function:
+                        iattrs = infered.instance_attrs
+                    else:
+                        iattrs = infered.locals
+                except AttributeError:
+                    # XXX log error
+                    #import traceback
+                    #traceback.print_exc()
+                    continue
+                values = iattrs.setdefault(node.attrname, [])
+                if node in values:
+                    continue
+                # get assign in __init__ first XXX useful ?
+                if frame.name == '__init__' and values and not \
+                       values[0].frame().name == '__init__':
+                    values.insert(0, node)
+                else:
+                    values.append(node)
+        except InferenceError:
+            pass
+

File ftplugin/python/logilab/astng/builder.pyc

Binary file added.

File ftplugin/python/logilab/astng/exceptions.py

+# This program 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 of the License, or (at your option) any later
+# version.
+#
+# This program 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
+# this program; if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+# copyright 2003-2010 Sylvain Thenault, all rights reserved.
+# contact mailto:thenault@gmail.com
+#
+# This file is part of logilab-astng.
+#
+# logilab-astng 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.
+#
+# logilab-astng 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 logilab-astng. If not, see <http://www.gnu.org/licenses/>.
+"""this module contains exceptions used in the astng library
+
+"""
+
+__doctype__ = "restructuredtext en"
+
+class ASTNGError(Exception):
+    """base exception class for all astng related exceptions"""
+
+class ASTNGBuildingException(ASTNGError):
+    """exception class when we are unable to build an astng representation"""
+
+class ResolveError(ASTNGError):
+    """base class of astng resolution/inference error"""
+
+class NotFoundError(ResolveError):
+    """raised when we are unable to resolve a name"""
+
+class InferenceError(ResolveError):
+    """raised when we are unable to infer a node"""
+
+class UnresolvableName(InferenceError):
+    """raised when we are unable to resolve a name"""
+
+class NoDefault(ASTNGError):
+    """raised by function's `default_value` method when an argument has
+    no default value
+    """
+

File ftplugin/python/logilab/astng/exceptions.pyc

Binary file added.

File ftplugin/python/logilab/astng/inference.py

+# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+# copyright 2003-2010 Sylvain Thenault, all rights reserved.
+# contact mailto:thenault@gmail.com
+#
+# This file is part of logilab-astng.
+#
+# logilab-astng 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.
+#
+# logilab-astng 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 logilab-astng. If not, see <http://www.gnu.org/licenses/>.
+"""this module contains a set of functions to handle inference on astng trees
+"""
+
+__doctype__ = "restructuredtext en"
+
+from itertools import chain
+import sys
+
+from logilab.astng import nodes
+
+from logilab.astng.manager import ASTNGManager
+from logilab.astng.exceptions import (ASTNGBuildingException, ASTNGError,
+    InferenceError, NoDefault, NotFoundError, UnresolvableName)
+from logilab.astng.bases import YES, Instance, InferenceContext, Generator, \
+     _infer_stmts, copy_context, path_wrapper, raise_if_nothing_infered
+from logilab.astng.protocols import _arguments_infer_argname
+
+MANAGER = ASTNGManager()
+
+
+class CallContext:
+    """when inferring a function call, this class is used to remember values
+    given as argument
+    """
+    def __init__(self, args, starargs, dstarargs):
+        self.args = []
+        self.nargs = {}
+        for arg in args:
+            if isinstance(arg, nodes.Keyword):
+                self.nargs[arg.arg] = arg.value
+            else:
+                self.args.append(arg)
+        self.starargs = starargs
+        self.dstarargs = dstarargs
+
+    def infer_argument(self, funcnode, name, context):
+        """infer a function argument value according to the call context"""
+        # 1. search in named keywords
+        try:
+            return self.nargs[name].infer(context)
+        except KeyError:
+            # Function.args.args can be None in astng (means that we don't have
+            # information on argnames)
+            argindex = funcnode.args.find_argname(name)[0]
+            if argindex is not None:
+                # 2. first argument of instance/class method
+                if argindex == 0 and funcnode.type in ('method', 'classmethod'):
+                    if context.boundnode is not None:
+                        boundnode = context.boundnode
+                    else:
+                        # XXX can do better ?
+                        boundnode = funcnode.parent.frame()
+                    if funcnode.type == 'method':
+                        if not isinstance(boundnode, Instance):
+                            boundnode = Instance(boundnode)
+                        return iter((boundnode,))
+                    if funcnode.type == 'classmethod':
+                        return iter((boundnode,))
+                # 2. search arg index
+                try:
+                    return self.args[argindex].infer(context)
+                except IndexError:
+                    pass
+                # 3. search in *args (.starargs)
+                if self.starargs is not None:
+                    its = []
+                    for infered in self.starargs.infer(context):
+                        if infered is YES:
+                            its.append((YES,))
+                            continue
+                        try:
+                            its.append(infered.getitem(argindex, context).infer(context))
+                        except (InferenceError, AttributeError):
+                            its.append((YES,))
+                        except (IndexError, TypeError):
+                            continue
+                    if its:
+                        return chain(*its)
+        # 4. XXX search in **kwargs (.dstarargs)
+        if self.dstarargs is not None:
+            its = []
+            for infered in self.dstarargs.infer(context):
+                if infered is YES:
+                    its.append((YES,))
+                    continue
+                try:
+                    its.append(infered.getitem(name, context).infer(context))
+                except (InferenceError, AttributeError):
+                    its.append((YES,))
+                except (IndexError, TypeError):
+                    continue
+            if its:
+                return chain(*its)
+        # 5. */** argument, (Tuple or Dict)
+        if name == funcnode.args.vararg:
+            return iter((nodes.const_factory(())))
+        if name == funcnode.args.kwarg:
+            return iter((nodes.const_factory({})))
+        # 6. return default value if any
+        try:
+            return funcnode.args.default_value(name).infer(context)
+        except NoDefault:
+            raise InferenceError(name)
+
+
+# .infer method ###############################################################
+
+
+def infer_end(self, context=None):
+    """inference's end for node such as Module, Class, Function, Const...
+    """
+    yield self
+nodes.Module.infer = infer_end
+nodes.Class.infer = infer_end
+nodes.Function.infer = infer_end
+nodes.Lambda.infer = infer_end
+nodes.Const.infer = infer_end
+nodes.List.infer = infer_end
+nodes.Tuple.infer = infer_end
+nodes.Dict.infer = infer_end
+
+
+def infer_name(self, context=None):
+    """infer a Name: use name lookup rules"""
+    frame, stmts = self.lookup(self.name)
+    if not stmts:
+        raise UnresolvableName(self.name)
+    context = context.clone()
+    context.lookupname = self.name
+    return _infer_stmts(stmts, context, frame)
+nodes.Name.infer = path_wrapper(infer_name)
+nodes.AssName.infer_lhs = infer_name # won't work with a path wrapper
+
+
+def infer_callfunc(self, context=None):
+    """infer a CallFunc node by trying to guess what the function returns"""
+    callcontext = context.clone()
+    callcontext.callcontext = CallContext(self.args, self.starargs, self.kwargs)
+    callcontext.boundnode = None
+    for callee in self.func.infer(context):
+        if callee is YES:
+            yield callee
+            continue
+        try:
+            if hasattr(callee, 'infer_call_result'):
+                for infered in callee.infer_call_result(self, callcontext):
+                    yield infered
+        except InferenceError:
+            ## XXX log error ?
+            continue
+nodes.CallFunc.infer = path_wrapper(raise_if_nothing_infered(infer_callfunc))
+
+
+def infer_import(self, context=None, asname=True):
+    """infer an Import node: return the imported module/object"""
+    name = context.lookupname
+    if name is None:
+        raise InferenceError()
+    if asname:
+        yield self.do_import_module(self.real_name(name))
+    else:
+        yield self.do_import_module(name)
+nodes.Import.infer = path_wrapper(infer_import)
+
+def infer_name_module(self, name):
+    context = InferenceContext()
+    context.lookupname = name
+    return self.infer(context, asname=False)
+nodes.Import.infer_name_module = infer_name_module
+
+
+def infer_from(self, context=None, asname=True):
+    """infer a From nodes: return the imported module/object"""
+    name = context.lookupname
+    if name is None:
+        raise InferenceError()
+    if asname:
+        name = self.real_name(name)
+    module = self.do_import_module(self.modname)
+    try:
+        context = copy_context(context)
+        context.lookupname = name
+        return _infer_stmts(module.getattr(name, ignore_locals=module is self.root()), context)
+    except NotFoundError:
+        raise InferenceError(name)
+nodes.From.infer = path_wrapper(infer_from)
+
+
+def infer_getattr(self, context=None):
+    """infer a Getattr node by using getattr on the associated object"""
+    #context = context.clone()
+    for owner in self.expr.infer(context):
+        if owner is YES:
+            yield owner
+            continue
+        try:
+            context.boundnode = owner
+            for obj in owner.igetattr(self.attrname, context):
+                yield obj
+            context.boundnode = None
+        except (NotFoundError, InferenceError):
+            context.boundnode = None
+        except AttributeError:
+            # XXX method / function
+            context.boundnode = None
+nodes.Getattr.infer = path_wrapper(raise_if_nothing_infered(infer_getattr))
+nodes.AssAttr.infer_lhs = raise_if_nothing_infered(infer_getattr) # # won't work with a path wrapper
+
+
+def infer_global(self, context=None):
+    if context.lookupname is None:
+        raise InferenceError()
+    try:
+        return _infer_stmts(self.root().getattr(context.lookupname), context)
+    except NotFoundError:
+        raise InferenceError()
+nodes.Global.infer = path_wrapper(infer_global)
+
+
+def infer_subscript(self, context=None):
+    """infer simple subscription such as [1,2,3][0] or (1,2,3)[-1]"""