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