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 / protocols.py @ 745
History | View | Annotate | Download (16.5 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 python protocols for nodes
|
19 |
where it makes sense.
|
20 |
"""
|
21 |
|
22 |
import collections |
23 |
import operator |
24 |
import sys |
25 |
|
26 |
from astroid import arguments |
27 |
from astroid import bases |
28 |
from astroid import context as contextmod |
29 |
from astroid import exceptions |
30 |
from astroid import node_classes |
31 |
from astroid import nodes |
32 |
from astroid import util |
33 |
|
34 |
BIN_OP_METHOD = {'+': '__add__', |
35 |
'-': '__sub__', |
36 |
'/': '__div__', |
37 |
'//': '__floordiv__', |
38 |
'*': '__mul__', |
39 |
'**': '__pow__', |
40 |
'%': '__mod__', |
41 |
'&': '__and__', |
42 |
'|': '__or__', |
43 |
'^': '__xor__', |
44 |
'<<': '__lshift__', |
45 |
'>>': '__rshift__', |
46 |
'@': '__matmul__' |
47 |
} |
48 |
|
49 |
UNARY_OP_METHOD = {'+': '__pos__', |
50 |
'-': '__neg__', |
51 |
'~': '__invert__', |
52 |
'not': None, # XXX not '__nonzero__' |
53 |
} |
54 |
|
55 |
# unary operations ############################################################
|
56 |
|
57 |
def tl_infer_unary_op(self, operator): |
58 |
if operator == 'not': |
59 |
return node_classes.const_factory(not bool(self.elts)) |
60 |
raise TypeError() # XXX log unsupported operation |
61 |
nodes.Tuple.infer_unary_op = tl_infer_unary_op |
62 |
nodes.List.infer_unary_op = tl_infer_unary_op |
63 |
|
64 |
|
65 |
def dict_infer_unary_op(self, operator): |
66 |
if operator == 'not': |
67 |
return node_classes.const_factory(not bool(self.items)) |
68 |
raise TypeError() # XXX log unsupported operation |
69 |
nodes.Dict.infer_unary_op = dict_infer_unary_op |
70 |
|
71 |
|
72 |
def const_infer_unary_op(self, operator): |
73 |
if operator == 'not': |
74 |
return node_classes.const_factory(not self.value) |
75 |
# XXX log potentially raised TypeError
|
76 |
elif operator == '+': |
77 |
return node_classes.const_factory(+self.value) |
78 |
else: # operator == '-': |
79 |
return node_classes.const_factory(-self.value) |
80 |
nodes.Const.infer_unary_op = const_infer_unary_op |
81 |
|
82 |
|
83 |
# binary operations ###########################################################
|
84 |
|
85 |
BIN_OP_IMPL = {'+': lambda a, b: a + b, |
86 |
'-': lambda a, b: a - b, |
87 |
'/': lambda a, b: a / b, |
88 |
'//': lambda a, b: a // b, |
89 |
'*': lambda a, b: a * b, |
90 |
'**': lambda a, b: a ** b, |
91 |
'%': lambda a, b: a % b, |
92 |
'&': lambda a, b: a & b, |
93 |
'|': lambda a, b: a | b, |
94 |
'^': lambda a, b: a ^ b, |
95 |
'<<': lambda a, b: a << b, |
96 |
'>>': lambda a, b: a >> b, |
97 |
} |
98 |
|
99 |
if sys.version_info >= (3, 5): |
100 |
# MatMult is available since Python 3.5+.
|
101 |
BIN_OP_IMPL['@'] = operator.matmul
|
102 |
|
103 |
for key, impl in list(BIN_OP_IMPL.items()): |
104 |
BIN_OP_IMPL[key+'='] = impl
|
105 |
|
106 |
def const_infer_binary_op(self, operator, other, context): |
107 |
for other in other.infer(context): |
108 |
if isinstance(other, nodes.Const): |
109 |
try:
|
110 |
impl = BIN_OP_IMPL[operator] |
111 |
|
112 |
try:
|
113 |
yield node_classes.const_factory(impl(self.value, other.value)) |
114 |
except Exception: |
115 |
# ArithmeticError is not enough: float >> float is a TypeError
|
116 |
# TODO : let pylint know about the problem
|
117 |
pass
|
118 |
except TypeError: |
119 |
# XXX log TypeError
|
120 |
continue
|
121 |
elif other is util.YES: |
122 |
yield other
|
123 |
else:
|
124 |
try:
|
125 |
for val in other.infer_binary_op(operator, self, context): |
126 |
yield val
|
127 |
except AttributeError: |
128 |
yield util.YES
|
129 |
nodes.Const.infer_binary_op = bases.yes_if_nothing_inferred(const_infer_binary_op) |
130 |
|
131 |
|
132 |
|
133 |
def _multiply_seq_by_int(self, other, context): |
134 |
node = self.__class__()
|
135 |
elts = [] |
136 |
for elt in self.elts: |
137 |
infered = util.safe_infer(elt, context) |
138 |
if infered is None: |
139 |
infered = util.YES |
140 |
elts.append(infered) |
141 |
node.elts = elts * other.value |
142 |
return node
|
143 |
|
144 |
|
145 |
def _filter_uninferable_nodes(elts, context): |
146 |
for elt in elts: |
147 |
if elt is util.YES: |
148 |
yield elt
|
149 |
else:
|
150 |
for inferred in elt.infer(context): |
151 |
yield inferred
|
152 |
|
153 |
|
154 |
def tl_infer_binary_op(self, operator, other, context): |
155 |
for other in other.infer(context): |
156 |
if isinstance(other, self.__class__) and operator == '+': |
157 |
node = self.__class__()
|
158 |
elts = list(_filter_uninferable_nodes(self.elts, context)) |
159 |
elts += list(_filter_uninferable_nodes(other.elts, context))
|
160 |
node.elts = elts |
161 |
yield node
|
162 |
elif isinstance(other, nodes.Const) and operator == '*': |
163 |
if not isinstance(other.value, int): |
164 |
yield util.YES
|
165 |
continue
|
166 |
yield _multiply_seq_by_int(self, other, context) |
167 |
elif isinstance(other, bases.Instance) and not isinstance(other, nodes.Const): |
168 |
yield util.YES
|
169 |
# XXX else log TypeError
|
170 |
nodes.Tuple.infer_binary_op = bases.yes_if_nothing_inferred(tl_infer_binary_op) |
171 |
nodes.List.infer_binary_op = bases.yes_if_nothing_inferred(tl_infer_binary_op) |
172 |
|
173 |
|
174 |
def dict_infer_binary_op(self, operator, other, context): |
175 |
for other in other.infer(context): |
176 |
if isinstance(other, bases.Instance) and isinstance(other._proxied, nodes.ClassDef): |
177 |
yield util.YES
|
178 |
# XXX else log TypeError
|
179 |
nodes.Dict.infer_binary_op = bases.yes_if_nothing_inferred(dict_infer_binary_op) |
180 |
|
181 |
def instance_infer_binary_op(self, operator, other, context): |
182 |
try:
|
183 |
methods = self.getattr(BIN_OP_METHOD[operator])
|
184 |
except (exceptions.NotFoundError, KeyError): |
185 |
# Unknown operator
|
186 |
yield util.YES
|
187 |
else:
|
188 |
for method in methods: |
189 |
if not isinstance(method, nodes.FunctionDef): |
190 |
continue
|
191 |
for result in method.infer_call_result(self, context): |
192 |
if result is not util.YES: |
193 |
yield result
|
194 |
# We are interested only in the first infered method,
|
195 |
# don't go looking in the rest of the methods of the ancestors.
|
196 |
break
|
197 |
|
198 |
bases.Instance.infer_binary_op = bases.yes_if_nothing_inferred(instance_infer_binary_op) |
199 |
|
200 |
|
201 |
# assignment ##################################################################
|
202 |
|
203 |
"""the assigned_stmts method is responsible to return the assigned statement
|
204 |
(e.g. not inferred) according to the assignment type.
|
205 |
|
206 |
The `asspath` argument is used to record the lhs path of the original node.
|
207 |
For instance if we want assigned statements for 'c' in 'a, (b,c)', asspath
|
208 |
will be [1, 1] once arrived to the Assign node.
|
209 |
|
210 |
The `context` argument is the current inference context which should be given
|
211 |
to any intermediary inference necessary.
|
212 |
"""
|
213 |
|
214 |
def _resolve_looppart(parts, asspath, context): |
215 |
"""recursive function to resolve multiple assignments on loops"""
|
216 |
asspath = asspath[:] |
217 |
index = asspath.pop(0)
|
218 |
for part in parts: |
219 |
if part is util.YES: |
220 |
continue
|
221 |
# XXX handle __iter__ and log potentially detected errors
|
222 |
if not hasattr(part, 'itered'): |
223 |
continue
|
224 |
try:
|
225 |
itered = part.itered() |
226 |
except TypeError: |
227 |
continue # XXX log error |
228 |
for stmt in itered: |
229 |
try:
|
230 |
assigned = stmt.getitem(index, context) |
231 |
except (AttributeError, IndexError): |
232 |
continue
|
233 |
except TypeError: # stmt is unsubscriptable Const |
234 |
continue
|
235 |
if not asspath: |
236 |
# we achieved to resolved the assignment path,
|
237 |
# don't infer the last part
|
238 |
yield assigned
|
239 |
elif assigned is util.YES: |
240 |
break
|
241 |
else:
|
242 |
# we are not yet on the last part of the path
|
243 |
# search on each possibly inferred value
|
244 |
try:
|
245 |
for inferred in _resolve_looppart(assigned.infer(context), |
246 |
asspath, context): |
247 |
yield inferred
|
248 |
except exceptions.InferenceError:
|
249 |
break
|
250 |
|
251 |
|
252 |
@bases.raise_if_nothing_inferred
|
253 |
def for_assigned_stmts(self, node=None, context=None, asspath=None): |
254 |
if asspath is None: |
255 |
for lst in self.iter.infer(context): |
256 |
if isinstance(lst, (nodes.Tuple, nodes.List)): |
257 |
for item in lst.elts: |
258 |
yield item
|
259 |
else:
|
260 |
for inferred in _resolve_looppart(self.iter.infer(context), |
261 |
asspath, context): |
262 |
yield inferred
|
263 |
|
264 |
nodes.For.assigned_stmts = for_assigned_stmts |
265 |
nodes.Comprehension.assigned_stmts = for_assigned_stmts |
266 |
|
267 |
|
268 |
def sequence_assigned_stmts(self, node=None, context=None, asspath=None): |
269 |
if asspath is None: |
270 |
asspath = [] |
271 |
try:
|
272 |
index = self.elts.index(node)
|
273 |
except ValueError: |
274 |
util.reraise(exceptions.InferenceError( |
275 |
'Tried to retrieve a node {node!r} which does not exist',
|
276 |
node=self, assign_path=asspath, context=context))
|
277 |
|
278 |
asspath.insert(0, index)
|
279 |
return self.parent.assigned_stmts(node=self, context=context, asspath=asspath) |
280 |
|
281 |
nodes.Tuple.assigned_stmts = sequence_assigned_stmts |
282 |
nodes.List.assigned_stmts = sequence_assigned_stmts |
283 |
|
284 |
|
285 |
def assend_assigned_stmts(self, node=None, context=None, asspath=None): |
286 |
return self.parent.assigned_stmts(node=self, context=context) |
287 |
nodes.AssignName.assigned_stmts = assend_assigned_stmts |
288 |
nodes.AssignAttr.assigned_stmts = assend_assigned_stmts |
289 |
|
290 |
|
291 |
def _arguments_infer_argname(self, name, context): |
292 |
# arguments information may be missing, in which case we can't do anything
|
293 |
# more
|
294 |
if not (self.args or self.vararg or self.kwarg): |
295 |
yield util.YES
|
296 |
return
|
297 |
# first argument of instance/class method
|
298 |
if self.args and getattr(self.args[0], 'name', None) == name: |
299 |
functype = self.parent.type
|
300 |
if functype == 'method': |
301 |
yield bases.Instance(self.parent.parent.frame()) |
302 |
return
|
303 |
if functype == 'classmethod': |
304 |
yield self.parent.parent.frame() |
305 |
return
|
306 |
|
307 |
if context and context.callcontext: |
308 |
call_site = arguments.CallSite(context.callcontext) |
309 |
for value in call_site.infer_argument(self.parent, name, context): |
310 |
yield value
|
311 |
return
|
312 |
|
313 |
# TODO: just provide the type here, no need to have an empty Dict.
|
314 |
if name == self.vararg: |
315 |
vararg = node_classes.const_factory(()) |
316 |
vararg.parent = self
|
317 |
yield vararg
|
318 |
return
|
319 |
if name == self.kwarg: |
320 |
kwarg = node_classes.const_factory({}) |
321 |
kwarg.parent = self
|
322 |
yield kwarg
|
323 |
return
|
324 |
# if there is a default value, yield it. And then yield YES to reflect
|
325 |
# we can't guess given argument value
|
326 |
try:
|
327 |
context = contextmod.copy_context(context) |
328 |
for inferred in self.default_value(name).infer(context): |
329 |
yield inferred
|
330 |
yield util.YES
|
331 |
except exceptions.NoDefault:
|
332 |
yield util.YES
|
333 |
|
334 |
|
335 |
def arguments_assigned_stmts(self, node=None, context=None, asspath=None): |
336 |
if context.callcontext:
|
337 |
# reset call context/name
|
338 |
callcontext = context.callcontext |
339 |
context = contextmod.copy_context(context) |
340 |
context.callcontext = None
|
341 |
args = arguments.CallSite(callcontext) |
342 |
return args.infer_argument(self.parent, node.name, context) |
343 |
return _arguments_infer_argname(self, node.name, context) |
344 |
|
345 |
nodes.Arguments.assigned_stmts = arguments_assigned_stmts |
346 |
|
347 |
|
348 |
@bases.raise_if_nothing_inferred
|
349 |
def assign_assigned_stmts(self, node=None, context=None, asspath=None): |
350 |
if not asspath: |
351 |
yield self.value |
352 |
return
|
353 |
for inferred in _resolve_asspart(self.value.infer(context), asspath, context): |
354 |
yield inferred
|
355 |
|
356 |
nodes.Assign.assigned_stmts = assign_assigned_stmts |
357 |
nodes.AugAssign.assigned_stmts = assign_assigned_stmts |
358 |
|
359 |
|
360 |
def _resolve_asspart(parts, asspath, context): |
361 |
"""recursive function to resolve multiple assignments"""
|
362 |
asspath = asspath[:] |
363 |
index = asspath.pop(0)
|
364 |
for part in parts: |
365 |
if hasattr(part, 'getitem'): |
366 |
try:
|
367 |
assigned = part.getitem(index, context) |
368 |
# XXX raise a specific exception to avoid potential hiding of
|
369 |
# unexpected exception ?
|
370 |
except (TypeError, IndexError): |
371 |
return
|
372 |
if not asspath: |
373 |
# we achieved to resolved the assignment path, don't infer the
|
374 |
# last part
|
375 |
yield assigned
|
376 |
elif assigned is util.YES: |
377 |
return
|
378 |
else:
|
379 |
# we are not yet on the last part of the path search on each
|
380 |
# possibly inferred value
|
381 |
try:
|
382 |
for inferred in _resolve_asspart(assigned.infer(context), |
383 |
asspath, context): |
384 |
yield inferred
|
385 |
except exceptions.InferenceError:
|
386 |
return
|
387 |
|
388 |
|
389 |
@bases.raise_if_nothing_inferred
|
390 |
def excepthandler_assigned_stmts(self, node=None, context=None, asspath=None): |
391 |
for assigned in node_classes.unpack_infer(self.type): |
392 |
if isinstance(assigned, nodes.ClassDef): |
393 |
assigned = bases.Instance(assigned) |
394 |
yield assigned
|
395 |
nodes.ExceptHandler.assigned_stmts = bases.raise_if_nothing_inferred(excepthandler_assigned_stmts) |
396 |
|
397 |
|
398 |
@bases.raise_if_nothing_inferred
|
399 |
def with_assigned_stmts(self, node=None, context=None, asspath=None): |
400 |
if asspath is None: |
401 |
for _, vars in self.items: |
402 |
if vars is None: |
403 |
continue
|
404 |
for lst in vars.infer(context): |
405 |
if isinstance(lst, (nodes.Tuple, nodes.List)): |
406 |
for item in lst.nodes: |
407 |
yield item
|
408 |
nodes.With.assigned_stmts = with_assigned_stmts |
409 |
|
410 |
|
411 |
@bases.yes_if_nothing_inferred
|
412 |
def starred_assigned_stmts(self, node=None, context=None, asspath=None): |
413 |
stmt = self.statement()
|
414 |
if not isinstance(stmt, (nodes.Assign, nodes.For)): |
415 |
raise exceptions.InferenceError()
|
416 |
|
417 |
if isinstance(stmt, nodes.Assign): |
418 |
value = stmt.value |
419 |
lhs = stmt.targets[0]
|
420 |
|
421 |
if sum(1 for node in lhs.nodes_of_class(nodes.Starred)) > 1: |
422 |
# Too many starred arguments in the expression.
|
423 |
raise exceptions.InferenceError()
|
424 |
|
425 |
if context is None: |
426 |
context = contextmod.InferenceContext() |
427 |
try:
|
428 |
rhs = next(value.infer(context))
|
429 |
except exceptions.InferenceError:
|
430 |
yield util.YES
|
431 |
return
|
432 |
if rhs is util.YES or not hasattr(rhs, 'elts'): |
433 |
# Not interested in inferred values without elts.
|
434 |
yield util.YES
|
435 |
return
|
436 |
|
437 |
elts = collections.deque(rhs.elts[:]) |
438 |
if len(lhs.elts) > len(rhs.elts): |
439 |
# a, *b, c = (1, 2)
|
440 |
raise exceptions.InferenceError()
|
441 |
|
442 |
# Unpack iteratively the values from the rhs of the assignment,
|
443 |
# until the find the starred node. What will remain will
|
444 |
# be the list of values which the Starred node will represent
|
445 |
# This is done in two steps, from left to right to remove
|
446 |
# anything before the starred node and from right to left
|
447 |
# to remvoe anything after the starred node.
|
448 |
|
449 |
for index, node in enumerate(lhs.elts): |
450 |
if not isinstance(node, nodes.Starred): |
451 |
elts.popleft() |
452 |
continue
|
453 |
lhs_elts = collections.deque(reversed(lhs.elts[index:]))
|
454 |
for node in lhs_elts: |
455 |
if not isinstance(node, nodes.Starred): |
456 |
elts.pop() |
457 |
continue
|
458 |
# We're done
|
459 |
packed = nodes.List() |
460 |
packed.elts = elts |
461 |
packed.parent = self
|
462 |
yield packed
|
463 |
break
|
464 |
|
465 |
nodes.Starred.assigned_stmts = starred_assigned_stmts |