Statistics
| Revision:

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