| Index: third_party/logilab/astng/inference.py
 | 
| ===================================================================
 | 
| --- third_party/logilab/astng/inference.py	(revision 292986)
 | 
| +++ third_party/logilab/astng/inference.py	(working copy)
 | 
| @@ -1,382 +0,0 @@
 | 
| -# 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/>.
 | 
| -"""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]"""
 | 
| -    if isinstance(self.slice, nodes.Index):
 | 
| -        index = self.slice.value.infer(context).next()
 | 
| -        if index is YES:
 | 
| -            yield YES
 | 
| -            return
 | 
| -        try:
 | 
| -            # suppose it's a Tuple/List node (attribute error else)
 | 
| -            assigned = self.value.getitem(index.value, context)
 | 
| -        except AttributeError:
 | 
| -            raise InferenceError()
 | 
| -        except (IndexError, TypeError):
 | 
| -            yield YES
 | 
| -            return
 | 
| -        for infered in assigned.infer(context):
 | 
| -            yield infered
 | 
| -    else:
 | 
| -        raise InferenceError()
 | 
| -nodes.Subscript.infer = path_wrapper(infer_subscript)
 | 
| -nodes.Subscript.infer_lhs = raise_if_nothing_infered(infer_subscript)
 | 
| -
 | 
| -
 | 
| -UNARY_OP_METHOD = {'+': '__pos__',
 | 
| -                   '-': '__neg__',
 | 
| -                   '~': '__invert__',
 | 
| -                   'not': None, # XXX not '__nonzero__'
 | 
| -                  }
 | 
| -
 | 
| -def infer_unaryop(self, context=None):
 | 
| -    for operand in self.operand.infer(context):
 | 
| -        try:
 | 
| -            yield operand.infer_unary_op(self.op)
 | 
| -        except TypeError:
 | 
| -            continue
 | 
| -        except AttributeError:
 | 
| -            meth = UNARY_OP_METHOD[self.op]
 | 
| -            if meth is None:
 | 
| -                yield YES
 | 
| -            else:
 | 
| -                try:
 | 
| -                    # XXX just suppose if the type implement meth, returned type
 | 
| -                    # will be the same
 | 
| -                    operand.getattr(meth)
 | 
| -                    yield operand
 | 
| -                except GeneratorExit:
 | 
| -                    raise
 | 
| -                except:
 | 
| -                    yield YES
 | 
| -nodes.UnaryOp.infer = path_wrapper(infer_unaryop)
 | 
| -
 | 
| -
 | 
| -BIN_OP_METHOD = {'+':  '__add__',
 | 
| -                 '-':  '__sub__',
 | 
| -                 '/':  '__div__',
 | 
| -                 '//': '__floordiv__',
 | 
| -                 '*':  '__mul__',
 | 
| -                 '**': '__power__',
 | 
| -                 '%':  '__mod__',
 | 
| -                 '&':  '__and__',
 | 
| -                 '|':  '__or__',
 | 
| -                 '^':  '__xor__',
 | 
| -                 '<<': '__lshift__',
 | 
| -                 '>>': '__rshift__',
 | 
| -                 }
 | 
| -
 | 
| -def _infer_binop(operator, operand1, operand2, context, failures=None):
 | 
| -    if operand1 is YES:
 | 
| -        yield operand1
 | 
| -        return
 | 
| -    try:
 | 
| -        for valnode in operand1.infer_binary_op(operator, operand2, context):
 | 
| -            yield valnode
 | 
| -    except AttributeError:
 | 
| -        try:
 | 
| -            # XXX just suppose if the type implement meth, returned type
 | 
| -            # will be the same
 | 
| -            operand1.getattr(BIN_OP_METHOD[operator])
 | 
| -            yield operand1
 | 
| -        except:
 | 
| -            if failures is None:
 | 
| -                yield YES
 | 
| -            else:
 | 
| -                failures.append(operand1)
 | 
| -
 | 
| -def infer_binop(self, context=None):
 | 
| -    failures = []
 | 
| -    for lhs in self.left.infer(context):
 | 
| -        for val in _infer_binop(self.op, lhs, self.right, context, failures):
 | 
| -            yield val
 | 
| -    for lhs in failures:
 | 
| -        for rhs in self.right.infer(context):
 | 
| -            for val in _infer_binop(self.op, rhs, lhs, context):
 | 
| -                yield val
 | 
| -nodes.BinOp.infer = path_wrapper(infer_binop)
 | 
| -
 | 
| -
 | 
| -def infer_arguments(self, context=None):
 | 
| -    name = context.lookupname
 | 
| -    if name is None:
 | 
| -        raise InferenceError()
 | 
| -    return _arguments_infer_argname(self, name, context)
 | 
| -nodes.Arguments.infer = infer_arguments
 | 
| -
 | 
| -
 | 
| -def infer_ass(self, context=None):
 | 
| -    """infer a AssName/AssAttr: need to inspect the RHS part of the
 | 
| -    assign node
 | 
| -    """
 | 
| -    stmt = self.statement()
 | 
| -    if isinstance(stmt, nodes.AugAssign):
 | 
| -        return stmt.infer(context)
 | 
| -    stmts = list(self.assigned_stmts(context=context))
 | 
| -    return _infer_stmts(stmts, context)
 | 
| -nodes.AssName.infer = path_wrapper(infer_ass)
 | 
| -nodes.AssAttr.infer = path_wrapper(infer_ass)
 | 
| -
 | 
| -def infer_augassign(self, context=None):
 | 
| -    failures = []
 | 
| -    for lhs in self.target.infer_lhs(context):
 | 
| -        for val in _infer_binop(self.op, lhs, self.value, context, failures):
 | 
| -            yield val
 | 
| -    for lhs in failures:
 | 
| -        for rhs in self.value.infer(context):
 | 
| -            for val in _infer_binop(self.op, rhs, lhs, context):
 | 
| -                yield val
 | 
| -nodes.AugAssign.infer = path_wrapper(infer_augassign)
 | 
| -
 | 
| -
 | 
| -# no infer method on DelName and DelAttr (expected InferenceError)
 | 
| -
 | 
| -
 | 
| -def infer_empty_node(self, context=None):
 | 
| -    if not self.has_underlying_object():
 | 
| -        yield YES
 | 
| -    else:
 | 
| -        try:
 | 
| -            for infered in MANAGER.infer_astng_from_something(self.object,
 | 
| -                                                              context=context):
 | 
| -                yield infered
 | 
| -        except ASTNGError:
 | 
| -            yield YES
 | 
| -nodes.EmptyNode.infer = path_wrapper(infer_empty_node)
 | 
| -
 | 
| 
 |