| OLD | NEW |
| (Empty) |
| 1 # copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. | |
| 2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr | |
| 3 # copyright 2003-2010 Sylvain Thenault, all rights reserved. | |
| 4 # contact mailto:thenault@gmail.com | |
| 5 # | |
| 6 # This file is part of logilab-astng. | |
| 7 # | |
| 8 # logilab-astng is free software: you can redistribute it and/or modify it | |
| 9 # under the terms of the GNU Lesser General Public License as published by the | |
| 10 # Free Software Foundation, either version 2.1 of the License, or (at your | |
| 11 # option) any later version. | |
| 12 # | |
| 13 # logilab-astng is distributed in the hope that it will be useful, but | |
| 14 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| 15 # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License | |
| 16 # for more details. | |
| 17 # | |
| 18 # You should have received a copy of the GNU Lesser General Public License along | |
| 19 # with logilab-astng. If not, see <http://www.gnu.org/licenses/>. | |
| 20 """this module contains a set of functions to handle inference on astng trees | |
| 21 """ | |
| 22 | |
| 23 __doctype__ = "restructuredtext en" | |
| 24 | |
| 25 from itertools import chain | |
| 26 import sys | |
| 27 | |
| 28 from logilab.astng import nodes | |
| 29 | |
| 30 from logilab.astng.manager import ASTNGManager | |
| 31 from logilab.astng.exceptions import (ASTNGBuildingException, ASTNGError, | |
| 32 InferenceError, NoDefault, NotFoundError, UnresolvableName) | |
| 33 from logilab.astng.bases import YES, Instance, InferenceContext, Generator, \ | |
| 34 _infer_stmts, copy_context, path_wrapper, raise_if_nothing_infered | |
| 35 from logilab.astng.protocols import _arguments_infer_argname | |
| 36 | |
| 37 MANAGER = ASTNGManager() | |
| 38 | |
| 39 | |
| 40 class CallContext: | |
| 41 """when inferring a function call, this class is used to remember values | |
| 42 given as argument | |
| 43 """ | |
| 44 def __init__(self, args, starargs, dstarargs): | |
| 45 self.args = [] | |
| 46 self.nargs = {} | |
| 47 for arg in args: | |
| 48 if isinstance(arg, nodes.Keyword): | |
| 49 self.nargs[arg.arg] = arg.value | |
| 50 else: | |
| 51 self.args.append(arg) | |
| 52 self.starargs = starargs | |
| 53 self.dstarargs = dstarargs | |
| 54 | |
| 55 def infer_argument(self, funcnode, name, context): | |
| 56 """infer a function argument value according to the call context""" | |
| 57 # 1. search in named keywords | |
| 58 try: | |
| 59 return self.nargs[name].infer(context) | |
| 60 except KeyError: | |
| 61 # Function.args.args can be None in astng (means that we don't have | |
| 62 # information on argnames) | |
| 63 argindex = funcnode.args.find_argname(name)[0] | |
| 64 if argindex is not None: | |
| 65 # 2. first argument of instance/class method | |
| 66 if argindex == 0 and funcnode.type in ('method', 'classmethod'): | |
| 67 if context.boundnode is not None: | |
| 68 boundnode = context.boundnode | |
| 69 else: | |
| 70 # XXX can do better ? | |
| 71 boundnode = funcnode.parent.frame() | |
| 72 if funcnode.type == 'method': | |
| 73 if not isinstance(boundnode, Instance): | |
| 74 boundnode = Instance(boundnode) | |
| 75 return iter((boundnode,)) | |
| 76 if funcnode.type == 'classmethod': | |
| 77 return iter((boundnode,)) | |
| 78 # 2. search arg index | |
| 79 try: | |
| 80 return self.args[argindex].infer(context) | |
| 81 except IndexError: | |
| 82 pass | |
| 83 # 3. search in *args (.starargs) | |
| 84 if self.starargs is not None: | |
| 85 its = [] | |
| 86 for infered in self.starargs.infer(context): | |
| 87 if infered is YES: | |
| 88 its.append((YES,)) | |
| 89 continue | |
| 90 try: | |
| 91 its.append(infered.getitem(argindex, context).infer(
context)) | |
| 92 except (InferenceError, AttributeError): | |
| 93 its.append((YES,)) | |
| 94 except (IndexError, TypeError): | |
| 95 continue | |
| 96 if its: | |
| 97 return chain(*its) | |
| 98 # 4. XXX search in **kwargs (.dstarargs) | |
| 99 if self.dstarargs is not None: | |
| 100 its = [] | |
| 101 for infered in self.dstarargs.infer(context): | |
| 102 if infered is YES: | |
| 103 its.append((YES,)) | |
| 104 continue | |
| 105 try: | |
| 106 its.append(infered.getitem(name, context).infer(context)) | |
| 107 except (InferenceError, AttributeError): | |
| 108 its.append((YES,)) | |
| 109 except (IndexError, TypeError): | |
| 110 continue | |
| 111 if its: | |
| 112 return chain(*its) | |
| 113 # 5. */** argument, (Tuple or Dict) | |
| 114 if name == funcnode.args.vararg: | |
| 115 return iter((nodes.const_factory(()))) | |
| 116 if name == funcnode.args.kwarg: | |
| 117 return iter((nodes.const_factory({}))) | |
| 118 # 6. return default value if any | |
| 119 try: | |
| 120 return funcnode.args.default_value(name).infer(context) | |
| 121 except NoDefault: | |
| 122 raise InferenceError(name) | |
| 123 | |
| 124 | |
| 125 # .infer method ############################################################### | |
| 126 | |
| 127 | |
| 128 def infer_end(self, context=None): | |
| 129 """inference's end for node such as Module, Class, Function, Const... | |
| 130 """ | |
| 131 yield self | |
| 132 nodes.Module.infer = infer_end | |
| 133 nodes.Class.infer = infer_end | |
| 134 nodes.Function.infer = infer_end | |
| 135 nodes.Lambda.infer = infer_end | |
| 136 nodes.Const.infer = infer_end | |
| 137 nodes.List.infer = infer_end | |
| 138 nodes.Tuple.infer = infer_end | |
| 139 nodes.Dict.infer = infer_end | |
| 140 | |
| 141 | |
| 142 def infer_name(self, context=None): | |
| 143 """infer a Name: use name lookup rules""" | |
| 144 frame, stmts = self.lookup(self.name) | |
| 145 if not stmts: | |
| 146 raise UnresolvableName(self.name) | |
| 147 context = context.clone() | |
| 148 context.lookupname = self.name | |
| 149 return _infer_stmts(stmts, context, frame) | |
| 150 nodes.Name.infer = path_wrapper(infer_name) | |
| 151 nodes.AssName.infer_lhs = infer_name # won't work with a path wrapper | |
| 152 | |
| 153 | |
| 154 def infer_callfunc(self, context=None): | |
| 155 """infer a CallFunc node by trying to guess what the function returns""" | |
| 156 callcontext = context.clone() | |
| 157 callcontext.callcontext = CallContext(self.args, self.starargs, self.kwargs) | |
| 158 callcontext.boundnode = None | |
| 159 for callee in self.func.infer(context): | |
| 160 if callee is YES: | |
| 161 yield callee | |
| 162 continue | |
| 163 try: | |
| 164 if hasattr(callee, 'infer_call_result'): | |
| 165 for infered in callee.infer_call_result(self, callcontext): | |
| 166 yield infered | |
| 167 except InferenceError: | |
| 168 ## XXX log error ? | |
| 169 continue | |
| 170 nodes.CallFunc.infer = path_wrapper(raise_if_nothing_infered(infer_callfunc)) | |
| 171 | |
| 172 | |
| 173 def infer_import(self, context=None, asname=True): | |
| 174 """infer an Import node: return the imported module/object""" | |
| 175 name = context.lookupname | |
| 176 if name is None: | |
| 177 raise InferenceError() | |
| 178 if asname: | |
| 179 yield self.do_import_module(self.real_name(name)) | |
| 180 else: | |
| 181 yield self.do_import_module(name) | |
| 182 nodes.Import.infer = path_wrapper(infer_import) | |
| 183 | |
| 184 def infer_name_module(self, name): | |
| 185 context = InferenceContext() | |
| 186 context.lookupname = name | |
| 187 return self.infer(context, asname=False) | |
| 188 nodes.Import.infer_name_module = infer_name_module | |
| 189 | |
| 190 | |
| 191 def infer_from(self, context=None, asname=True): | |
| 192 """infer a From nodes: return the imported module/object""" | |
| 193 name = context.lookupname | |
| 194 if name is None: | |
| 195 raise InferenceError() | |
| 196 if asname: | |
| 197 name = self.real_name(name) | |
| 198 module = self.do_import_module(self.modname) | |
| 199 try: | |
| 200 context = copy_context(context) | |
| 201 context.lookupname = name | |
| 202 return _infer_stmts(module.getattr(name, ignore_locals=module is self.ro
ot()), context) | |
| 203 except NotFoundError: | |
| 204 raise InferenceError(name) | |
| 205 nodes.From.infer = path_wrapper(infer_from) | |
| 206 | |
| 207 | |
| 208 def infer_getattr(self, context=None): | |
| 209 """infer a Getattr node by using getattr on the associated object""" | |
| 210 #context = context.clone() | |
| 211 for owner in self.expr.infer(context): | |
| 212 if owner is YES: | |
| 213 yield owner | |
| 214 continue | |
| 215 try: | |
| 216 context.boundnode = owner | |
| 217 for obj in owner.igetattr(self.attrname, context): | |
| 218 yield obj | |
| 219 context.boundnode = None | |
| 220 except (NotFoundError, InferenceError): | |
| 221 context.boundnode = None | |
| 222 except AttributeError: | |
| 223 # XXX method / function | |
| 224 context.boundnode = None | |
| 225 nodes.Getattr.infer = path_wrapper(raise_if_nothing_infered(infer_getattr)) | |
| 226 nodes.AssAttr.infer_lhs = raise_if_nothing_infered(infer_getattr) # # won't work
with a path wrapper | |
| 227 | |
| 228 | |
| 229 def infer_global(self, context=None): | |
| 230 if context.lookupname is None: | |
| 231 raise InferenceError() | |
| 232 try: | |
| 233 return _infer_stmts(self.root().getattr(context.lookupname), context) | |
| 234 except NotFoundError: | |
| 235 raise InferenceError() | |
| 236 nodes.Global.infer = path_wrapper(infer_global) | |
| 237 | |
| 238 | |
| 239 def infer_subscript(self, context=None): | |
| 240 """infer simple subscription such as [1,2,3][0] or (1,2,3)[-1]""" | |
| 241 if isinstance(self.slice, nodes.Index): | |
| 242 index = self.slice.value.infer(context).next() | |
| 243 if index is YES: | |
| 244 yield YES | |
| 245 return | |
| 246 try: | |
| 247 # suppose it's a Tuple/List node (attribute error else) | |
| 248 assigned = self.value.getitem(index.value, context) | |
| 249 except AttributeError: | |
| 250 raise InferenceError() | |
| 251 except (IndexError, TypeError): | |
| 252 yield YES | |
| 253 return | |
| 254 for infered in assigned.infer(context): | |
| 255 yield infered | |
| 256 else: | |
| 257 raise InferenceError() | |
| 258 nodes.Subscript.infer = path_wrapper(infer_subscript) | |
| 259 nodes.Subscript.infer_lhs = raise_if_nothing_infered(infer_subscript) | |
| 260 | |
| 261 | |
| 262 UNARY_OP_METHOD = {'+': '__pos__', | |
| 263 '-': '__neg__', | |
| 264 '~': '__invert__', | |
| 265 'not': None, # XXX not '__nonzero__' | |
| 266 } | |
| 267 | |
| 268 def infer_unaryop(self, context=None): | |
| 269 for operand in self.operand.infer(context): | |
| 270 try: | |
| 271 yield operand.infer_unary_op(self.op) | |
| 272 except TypeError: | |
| 273 continue | |
| 274 except AttributeError: | |
| 275 meth = UNARY_OP_METHOD[self.op] | |
| 276 if meth is None: | |
| 277 yield YES | |
| 278 else: | |
| 279 try: | |
| 280 # XXX just suppose if the type implement meth, returned type | |
| 281 # will be the same | |
| 282 operand.getattr(meth) | |
| 283 yield operand | |
| 284 except GeneratorExit: | |
| 285 raise | |
| 286 except: | |
| 287 yield YES | |
| 288 nodes.UnaryOp.infer = path_wrapper(infer_unaryop) | |
| 289 | |
| 290 | |
| 291 BIN_OP_METHOD = {'+': '__add__', | |
| 292 '-': '__sub__', | |
| 293 '/': '__div__', | |
| 294 '//': '__floordiv__', | |
| 295 '*': '__mul__', | |
| 296 '**': '__power__', | |
| 297 '%': '__mod__', | |
| 298 '&': '__and__', | |
| 299 '|': '__or__', | |
| 300 '^': '__xor__', | |
| 301 '<<': '__lshift__', | |
| 302 '>>': '__rshift__', | |
| 303 } | |
| 304 | |
| 305 def _infer_binop(operator, operand1, operand2, context, failures=None): | |
| 306 if operand1 is YES: | |
| 307 yield operand1 | |
| 308 return | |
| 309 try: | |
| 310 for valnode in operand1.infer_binary_op(operator, operand2, context): | |
| 311 yield valnode | |
| 312 except AttributeError: | |
| 313 try: | |
| 314 # XXX just suppose if the type implement meth, returned type | |
| 315 # will be the same | |
| 316 operand1.getattr(BIN_OP_METHOD[operator]) | |
| 317 yield operand1 | |
| 318 except: | |
| 319 if failures is None: | |
| 320 yield YES | |
| 321 else: | |
| 322 failures.append(operand1) | |
| 323 | |
| 324 def infer_binop(self, context=None): | |
| 325 failures = [] | |
| 326 for lhs in self.left.infer(context): | |
| 327 for val in _infer_binop(self.op, lhs, self.right, context, failures): | |
| 328 yield val | |
| 329 for lhs in failures: | |
| 330 for rhs in self.right.infer(context): | |
| 331 for val in _infer_binop(self.op, rhs, lhs, context): | |
| 332 yield val | |
| 333 nodes.BinOp.infer = path_wrapper(infer_binop) | |
| 334 | |
| 335 | |
| 336 def infer_arguments(self, context=None): | |
| 337 name = context.lookupname | |
| 338 if name is None: | |
| 339 raise InferenceError() | |
| 340 return _arguments_infer_argname(self, name, context) | |
| 341 nodes.Arguments.infer = infer_arguments | |
| 342 | |
| 343 | |
| 344 def infer_ass(self, context=None): | |
| 345 """infer a AssName/AssAttr: need to inspect the RHS part of the | |
| 346 assign node | |
| 347 """ | |
| 348 stmt = self.statement() | |
| 349 if isinstance(stmt, nodes.AugAssign): | |
| 350 return stmt.infer(context) | |
| 351 stmts = list(self.assigned_stmts(context=context)) | |
| 352 return _infer_stmts(stmts, context) | |
| 353 nodes.AssName.infer = path_wrapper(infer_ass) | |
| 354 nodes.AssAttr.infer = path_wrapper(infer_ass) | |
| 355 | |
| 356 def infer_augassign(self, context=None): | |
| 357 failures = [] | |
| 358 for lhs in self.target.infer_lhs(context): | |
| 359 for val in _infer_binop(self.op, lhs, self.value, context, failures): | |
| 360 yield val | |
| 361 for lhs in failures: | |
| 362 for rhs in self.value.infer(context): | |
| 363 for val in _infer_binop(self.op, rhs, lhs, context): | |
| 364 yield val | |
| 365 nodes.AugAssign.infer = path_wrapper(infer_augassign) | |
| 366 | |
| 367 | |
| 368 # no infer method on DelName and DelAttr (expected InferenceError) | |
| 369 | |
| 370 | |
| 371 def infer_empty_node(self, context=None): | |
| 372 if not self.has_underlying_object(): | |
| 373 yield YES | |
| 374 else: | |
| 375 try: | |
| 376 for infered in MANAGER.infer_astng_from_something(self.object, | |
| 377 context=context): | |
| 378 yield infered | |
| 379 except ASTNGError: | |
| 380 yield YES | |
| 381 nodes.EmptyNode.infer = path_wrapper(infer_empty_node) | |
| 382 | |
| OLD | NEW |