gvsig-scripting / org.gvsig.scripting / trunk / org.gvsig.scripting / org.gvsig.scripting.app / org.gvsig.scripting.app.mainplugin / src / main / resources-plugin / scripting / lib / astroid / inference.py @ 745
History | View | Annotate | Download (11.8 KB)
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 |
from __future__ import print_function |
22 |
|
23 |
from astroid import bases |
24 |
from astroid import context as contextmod |
25 |
from astroid import exceptions |
26 |
from astroid import manager |
27 |
from astroid import nodes |
28 |
from astroid import protocols |
29 |
from astroid import util |
30 |
|
31 |
|
32 |
MANAGER = manager.AstroidManager() |
33 |
|
34 |
|
35 |
# .infer method ###############################################################
|
36 |
|
37 |
|
38 |
def infer_end(self, context=None): |
39 |
"""inference's end for node such as Module, ClassDef, FunctionDef,
|
40 |
Const...
|
41 |
|
42 |
"""
|
43 |
yield self |
44 |
nodes.Module._infer = infer_end |
45 |
nodes.ClassDef._infer = infer_end |
46 |
nodes.FunctionDef._infer = infer_end |
47 |
nodes.Lambda._infer = infer_end |
48 |
nodes.Const._infer = infer_end |
49 |
nodes.List._infer = infer_end |
50 |
nodes.Tuple._infer = infer_end |
51 |
nodes.Dict._infer = infer_end |
52 |
nodes.Set._infer = infer_end |
53 |
|
54 |
def _higher_function_scope(node): |
55 |
""" Search for the first function which encloses the given
|
56 |
scope. This can be used for looking up in that function's
|
57 |
scope, in case looking up in a lower scope for a particular
|
58 |
name fails.
|
59 |
|
60 |
:param node: A scope node.
|
61 |
:returns:
|
62 |
``None``, if no parent function scope was found,
|
63 |
otherwise an instance of :class:`astroid.scoped_nodes.Function`,
|
64 |
which encloses the given node.
|
65 |
"""
|
66 |
current = node |
67 |
while current.parent and not isinstance(current.parent, nodes.FunctionDef): |
68 |
current = current.parent |
69 |
if current and current.parent: |
70 |
return current.parent
|
71 |
|
72 |
def infer_name(self, context=None): |
73 |
"""infer a Name: use name lookup rules"""
|
74 |
frame, stmts = self.lookup(self.name) |
75 |
if not stmts: |
76 |
# Try to see if the name is enclosed in a nested function
|
77 |
# and use the higher (first function) scope for searching.
|
78 |
# TODO: should this be promoted to other nodes as well?
|
79 |
parent_function = _higher_function_scope(self.scope())
|
80 |
if parent_function:
|
81 |
_, stmts = parent_function.lookup(self.name)
|
82 |
|
83 |
if not stmts: |
84 |
raise exceptions.UnresolvableName(self.name) |
85 |
context = context.clone() |
86 |
context.lookupname = self.name
|
87 |
return bases._infer_stmts(stmts, context, frame)
|
88 |
nodes.Name._infer = bases.path_wrapper(infer_name) |
89 |
nodes.AssignName.infer_lhs = infer_name # won't work with a path wrapper
|
90 |
|
91 |
|
92 |
@bases.path_wrapper
|
93 |
@bases.raise_if_nothing_inferred
|
94 |
def infer_call(self, context=None): |
95 |
"""infer a Call node by trying to guess what the function returns"""
|
96 |
callcontext = context.clone() |
97 |
callcontext.callcontext = contextmod.CallContext(args=self.args,
|
98 |
keywords=self.keywords)
|
99 |
callcontext.boundnode = None
|
100 |
for callee in self.func.infer(context): |
101 |
if callee is util.YES: |
102 |
yield callee
|
103 |
continue
|
104 |
try:
|
105 |
if hasattr(callee, 'infer_call_result'): |
106 |
for inferred in callee.infer_call_result(self, callcontext): |
107 |
yield inferred
|
108 |
except exceptions.InferenceError:
|
109 |
## XXX log error ?
|
110 |
continue
|
111 |
nodes.Call._infer = infer_call |
112 |
|
113 |
|
114 |
@bases.path_wrapper
|
115 |
def infer_import(self, context=None, asname=True): |
116 |
"""infer an Import node: return the imported module/object"""
|
117 |
name = context.lookupname |
118 |
if name is None: |
119 |
raise exceptions.InferenceError()
|
120 |
if asname:
|
121 |
yield self.do_import_module(self.real_name(name)) |
122 |
else:
|
123 |
yield self.do_import_module(name) |
124 |
nodes.Import._infer = infer_import |
125 |
|
126 |
|
127 |
def infer_name_module(self, name): |
128 |
context = contextmod.InferenceContext() |
129 |
context.lookupname = name |
130 |
return self.infer(context, asname=False) |
131 |
nodes.Import.infer_name_module = infer_name_module |
132 |
|
133 |
|
134 |
@bases.path_wrapper
|
135 |
def infer_import_from(self, context=None, asname=True): |
136 |
"""infer a ImportFrom node: return the imported module/object"""
|
137 |
name = context.lookupname |
138 |
if name is None: |
139 |
raise exceptions.InferenceError()
|
140 |
if asname:
|
141 |
name = self.real_name(name)
|
142 |
module = self.do_import_module()
|
143 |
try:
|
144 |
context = contextmod.copy_context(context) |
145 |
context.lookupname = name |
146 |
stmts = module.getattr(name, ignore_locals=module is self.root()) |
147 |
return bases._infer_stmts(stmts, context)
|
148 |
except exceptions.NotFoundError:
|
149 |
raise exceptions.InferenceError(name)
|
150 |
nodes.ImportFrom._infer = infer_import_from |
151 |
|
152 |
|
153 |
@bases.raise_if_nothing_inferred
|
154 |
def infer_attribute(self, context=None): |
155 |
"""infer an Attribute node by using getattr on the associated object"""
|
156 |
for owner in self.expr.infer(context): |
157 |
if owner is util.YES: |
158 |
yield owner
|
159 |
continue
|
160 |
try:
|
161 |
context.boundnode = owner |
162 |
for obj in owner.igetattr(self.attrname, context): |
163 |
yield obj
|
164 |
context.boundnode = None
|
165 |
except (exceptions.NotFoundError, exceptions.InferenceError):
|
166 |
context.boundnode = None
|
167 |
except AttributeError: |
168 |
# XXX method / function
|
169 |
context.boundnode = None
|
170 |
nodes.Attribute._infer = bases.path_wrapper(infer_attribute) |
171 |
nodes.AssignAttr.infer_lhs = infer_attribute # # won't work with a path wrapper
|
172 |
|
173 |
|
174 |
@bases.path_wrapper
|
175 |
def infer_global(self, context=None): |
176 |
if context.lookupname is None: |
177 |
raise exceptions.InferenceError()
|
178 |
try:
|
179 |
return bases._infer_stmts(self.root().getattr(context.lookupname), |
180 |
context) |
181 |
except exceptions.NotFoundError:
|
182 |
raise exceptions.InferenceError()
|
183 |
nodes.Global._infer = infer_global |
184 |
|
185 |
|
186 |
@bases.raise_if_nothing_inferred
|
187 |
def infer_subscript(self, context=None): |
188 |
"""Inference for subscripts
|
189 |
|
190 |
We're understanding if the index is a Const
|
191 |
or a slice, passing the result of inference
|
192 |
to the value's `getitem` method, which should
|
193 |
handle each supported index type accordingly.
|
194 |
"""
|
195 |
|
196 |
value = next(self.value.infer(context)) |
197 |
if value is util.YES: |
198 |
yield util.YES
|
199 |
return
|
200 |
|
201 |
index = next(self.slice.infer(context)) |
202 |
if index is util.YES: |
203 |
yield util.YES
|
204 |
return
|
205 |
|
206 |
if isinstance(index, nodes.Const): |
207 |
try:
|
208 |
assigned = value.getitem(index.value, context) |
209 |
except AttributeError: |
210 |
raise exceptions.InferenceError()
|
211 |
except (IndexError, TypeError): |
212 |
yield util.YES
|
213 |
return
|
214 |
|
215 |
# Prevent inferring if the infered subscript
|
216 |
# is the same as the original subscripted object.
|
217 |
if self is assigned or assigned is util.YES: |
218 |
yield util.YES
|
219 |
return
|
220 |
for infered in assigned.infer(context): |
221 |
yield infered
|
222 |
else:
|
223 |
raise exceptions.InferenceError()
|
224 |
nodes.Subscript._infer = bases.path_wrapper(infer_subscript) |
225 |
nodes.Subscript.infer_lhs = infer_subscript |
226 |
|
227 |
@bases.raise_if_nothing_inferred
|
228 |
def infer_unaryop(self, context=None): |
229 |
for operand in self.operand.infer(context): |
230 |
try:
|
231 |
yield operand.infer_unary_op(self.op) |
232 |
except TypeError: |
233 |
continue
|
234 |
except AttributeError: |
235 |
meth = protocols.UNARY_OP_METHOD[self.op]
|
236 |
if meth is None: |
237 |
yield util.YES
|
238 |
else:
|
239 |
try:
|
240 |
# XXX just suppose if the type implement meth, returned type
|
241 |
# will be the same
|
242 |
operand.getattr(meth) |
243 |
yield operand
|
244 |
except GeneratorExit: |
245 |
raise
|
246 |
except:
|
247 |
yield util.YES
|
248 |
nodes.UnaryOp._infer = bases.path_wrapper(infer_unaryop) |
249 |
|
250 |
def _infer_binop(operator, operand1, operand2, context, failures=None): |
251 |
if operand1 is util.YES: |
252 |
yield operand1
|
253 |
return
|
254 |
try:
|
255 |
for valnode in operand1.infer_binary_op(operator, operand2, context): |
256 |
yield valnode
|
257 |
except AttributeError: |
258 |
try:
|
259 |
# XXX just suppose if the type implement meth, returned type
|
260 |
# will be the same
|
261 |
operand1.getattr(protocols.BIN_OP_METHOD[operator]) |
262 |
yield operand1
|
263 |
except:
|
264 |
if failures is None: |
265 |
yield util.YES
|
266 |
else:
|
267 |
failures.append(operand1) |
268 |
|
269 |
@bases.yes_if_nothing_inferred
|
270 |
def infer_binop(self, context=None): |
271 |
failures = [] |
272 |
for lhs in self.left.infer(context): |
273 |
for val in _infer_binop(self.op, lhs, self.right, context, failures): |
274 |
yield val
|
275 |
for lhs in failures: |
276 |
for rhs in self.right.infer(context): |
277 |
for val in _infer_binop(self.op, rhs, lhs, context): |
278 |
yield val
|
279 |
nodes.BinOp._infer = bases.path_wrapper(infer_binop) |
280 |
|
281 |
|
282 |
def infer_arguments(self, context=None): |
283 |
name = context.lookupname |
284 |
if name is None: |
285 |
raise exceptions.InferenceError()
|
286 |
return protocols._arguments_infer_argname(self, name, context) |
287 |
nodes.Arguments._infer = infer_arguments |
288 |
|
289 |
|
290 |
@bases.path_wrapper
|
291 |
def infer_assign(self, context=None): |
292 |
"""infer a AssignName/AssignAttr: need to inspect the RHS part of the
|
293 |
assign node
|
294 |
"""
|
295 |
stmt = self.statement()
|
296 |
if isinstance(stmt, nodes.AugAssign): |
297 |
return stmt.infer(context)
|
298 |
|
299 |
stmts = list(self.assigned_stmts(context=context)) |
300 |
return bases._infer_stmts(stmts, context)
|
301 |
nodes.AssignName._infer = infer_assign |
302 |
nodes.AssignAttr._infer = infer_assign |
303 |
|
304 |
def infer_augassign(self, context=None): |
305 |
failures = [] |
306 |
for lhs in self.target.infer_lhs(context): |
307 |
for val in _infer_binop(self.op, lhs, self.value, context, failures): |
308 |
yield val
|
309 |
for lhs in failures: |
310 |
for rhs in self.value.infer(context): |
311 |
for val in _infer_binop(self.op, rhs, lhs, context): |
312 |
yield val
|
313 |
nodes.AugAssign._infer = bases.path_wrapper(infer_augassign) |
314 |
|
315 |
|
316 |
# no infer method on DelName and DelAttr (expected InferenceError)
|
317 |
|
318 |
@bases.path_wrapper
|
319 |
def infer_empty_node(self, context=None): |
320 |
if not self.has_underlying_object(): |
321 |
yield util.YES
|
322 |
else:
|
323 |
try:
|
324 |
for inferred in MANAGER.infer_ast_from_something(self.object, |
325 |
context=context): |
326 |
yield inferred
|
327 |
except exceptions.AstroidError:
|
328 |
yield util.YES
|
329 |
nodes.EmptyNode._infer = infer_empty_node |
330 |
|
331 |
|
332 |
def infer_index(self, context=None): |
333 |
return self.value.infer(context) |
334 |
nodes.Index._infer = infer_index |
335 |
|
336 |
# TODO: move directly into bases.Instance when the dependency hell
|
337 |
# will be solved.
|
338 |
def instance_getitem(self, index, context=None): |
339 |
# Rewrap index to Const for this case
|
340 |
index = nodes.Const(index) |
341 |
if context:
|
342 |
new_context = context.clone() |
343 |
else:
|
344 |
context = new_context = contextmod.InferenceContext() |
345 |
|
346 |
# Create a new callcontext for providing index as an argument.
|
347 |
new_context.callcontext = contextmod.CallContext(args=[index]) |
348 |
new_context.boundnode = self
|
349 |
|
350 |
method = next(self.igetattr('__getitem__', context=context)) |
351 |
if not isinstance(method, bases.BoundMethod): |
352 |
raise exceptions.InferenceError
|
353 |
|
354 |
try:
|
355 |
return next(method.infer_call_result(self, new_context)) |
356 |
except StopIteration: |
357 |
raise exceptions.InferenceError
|
358 |
|
359 |
bases.Instance.getitem = instance_getitem |