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 / bases.py @ 745

History | View | Annotate | Download (21.7 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 base classes and functions for the nodes and some
19
inference utils.
20
"""
21
import functools
22
import sys
23
import warnings
24

    
25
import wrapt
26

    
27
from astroid import context as contextmod
28
from astroid import decorators as decoratorsmod
29
from astroid import exceptions
30
from astroid import util
31

    
32

    
33
if sys.version_info >= (3, 0):
34
    BUILTINS = 'builtins'
35
else:
36
    BUILTINS = '__builtin__'
37
PROPERTIES = {BUILTINS + '.property', 'abc.abstractproperty'}
38
# List of possible property names. We use this list in order
39
# to see if a method is a property or not. This should be
40
# pretty reliable and fast, the alternative being to check each
41
# decorator to see if its a real property-like descriptor, which
42
# can be too complicated.
43
# Also, these aren't qualified, because each project can
44
# define them, we shouldn't expect to know every possible
45
# property-like decorator!
46
# TODO(cpopa): just implement descriptors already.
47
POSSIBLE_PROPERTIES = {"cached_property", "cachedproperty",
48
                       "lazyproperty", "lazy_property", "reify",
49
                       "lazyattribute", "lazy_attribute",
50
                       "LazyProperty", "lazy"}
51

    
52

    
53
def _is_property(meth):
54
    if PROPERTIES.intersection(meth.decoratornames()):
55
        return True
56
    stripped = {name.split(".")[-1] for name in meth.decoratornames()
57
                if name is not util.YES}
58
    return any(name in stripped for name in POSSIBLE_PROPERTIES)
59

    
60

    
61
class Proxy(object):
62
    """a simple proxy object"""
63

    
64
    _proxied = None # proxied object may be set by class or by instance
65

    
66
    def __init__(self, proxied=None):
67
        if proxied is not None:
68
            self._proxied = proxied
69

    
70
    def __getattr__(self, name):
71
        if name == '_proxied':
72
            return getattr(self.__class__, '_proxied')
73
        if name in self.__dict__:
74
            return self.__dict__[name]
75
        return getattr(self._proxied, name)
76

    
77
    def infer(self, context=None):
78
        yield self
79

    
80

    
81
def _infer_stmts(stmts, context, frame=None):
82
    """Return an iterator on statements inferred by each statement in *stmts*."""
83
    stmt = None
84
    inferred = False
85
    if context is not None:
86
        name = context.lookupname
87
        context = context.clone()
88
    else:
89
        name = None
90
        context = contextmod.InferenceContext()
91

    
92
    for stmt in stmts:
93
        if stmt is util.YES:
94
            yield stmt
95
            inferred = True
96
            continue
97
        context.lookupname = stmt._infer_name(frame, name)
98
        try:
99
            for inferred in stmt.infer(context=context):
100
                yield inferred
101
                inferred = True
102
        except exceptions.UnresolvableName:
103
            continue
104
        except exceptions.InferenceError:
105
            yield util.YES
106
            inferred = True
107
    if not inferred:
108
        raise exceptions.InferenceError(str(stmt))
109

    
110

    
111
class Instance(Proxy):
112
    """a special node representing a class instance"""
113
    def getattr(self, name, context=None, lookupclass=True):
114
        try:
115
            values = self._proxied.instance_attr(name, context)
116
        except exceptions.NotFoundError:
117
            if name == '__class__':
118
                return [self._proxied]
119
            if lookupclass:
120
                # class attributes not available through the instance
121
                # unless they are explicitly defined
122
                if name in ('__name__', '__bases__', '__mro__', '__subclasses__'):
123
                    return self._proxied.local_attr(name)
124
                return self._proxied.getattr(name, context)
125
            raise exceptions.NotFoundError(name)
126
        # since we've no context information, return matching class members as
127
        # well
128
        if lookupclass:
129
            try:
130
                return values + self._proxied.getattr(name, context)
131
            except exceptions.NotFoundError:
132
                pass
133
        return values
134

    
135
    def igetattr(self, name, context=None):
136
        """inferred getattr"""
137
        if not context:
138
            context = contextmod.InferenceContext()
139
        try:
140
            # avoid recursively inferring the same attr on the same class
141
            context.push((self._proxied, name))
142
            # XXX frame should be self._proxied, or not ?
143
            get_attr = self.getattr(name, context, lookupclass=False)
144
            return _infer_stmts(
145
                self._wrap_attr(get_attr, context),
146
                context,
147
                frame=self,
148
            )
149
        except exceptions.NotFoundError:
150
            try:
151
                # fallback to class'igetattr since it has some logic to handle
152
                # descriptors
153
                return self._wrap_attr(self._proxied.igetattr(name, context),
154
                                       context)
155
            except exceptions.NotFoundError:
156
                raise exceptions.InferenceError(name)
157

    
158
    def _wrap_attr(self, attrs, context=None):
159
        """wrap bound methods of attrs in a InstanceMethod proxies"""
160
        for attr in attrs:
161
            if isinstance(attr, UnboundMethod):
162
                if _is_property(attr):
163
                    for inferred in attr.infer_call_result(self, context):
164
                        yield inferred
165
                else:
166
                    yield BoundMethod(attr, self)
167
            elif hasattr(attr, 'name') and attr.name == '<lambda>':
168
                # This is a lambda function defined at class level,
169
                # since its scope is the underlying _proxied class.
170
                # Unfortunately, we can't do an isinstance check here,
171
                # because of the circular dependency between astroid.bases
172
                # and astroid.scoped_nodes.
173
                if attr.statement().scope() == self._proxied:
174
                    if attr.args.args and attr.args.args[0].name == 'self':
175
                        yield BoundMethod(attr, self)
176
                        continue
177
                yield attr
178
            else:
179
                yield attr
180

    
181
    def infer_call_result(self, caller, context=None):
182
        """infer what a class instance is returning when called"""
183
        inferred = False
184
        for node in self._proxied.igetattr('__call__', context):
185
            if node is util.YES or not node.callable():
186
                continue
187
            for res in node.infer_call_result(caller, context):
188
                inferred = True
189
                yield res
190
        if not inferred:
191
            raise exceptions.InferenceError()
192

    
193
    def __repr__(self):
194
        return '<Instance of %s.%s at 0x%s>' % (self._proxied.root().name,
195
                                                self._proxied.name,
196
                                                id(self))
197
    def __str__(self):
198
        return 'Instance of %s.%s' % (self._proxied.root().name,
199
                                      self._proxied.name)
200

    
201
    def callable(self):
202
        try:
203
            self._proxied.getattr('__call__')
204
            return True
205
        except exceptions.NotFoundError:
206
            return False
207

    
208
    def pytype(self):
209
        return self._proxied.qname()
210

    
211
    def display_type(self):
212
        return 'Instance of'
213

    
214

    
215
    # TODO(cpopa): this is set in inference.py
216
    # The circular dependency hell goes deeper and deeper.
217
    # pylint: disable=unused-argument
218
    def getitem(self, index, context=None):
219
        pass
220

    
221
class UnboundMethod(Proxy):
222
    """a special node representing a method not bound to an instance"""
223
    def __repr__(self):
224
        frame = self._proxied.parent.frame()
225
        return '<%s %s of %s at 0x%s' % (self.__class__.__name__,
226
                                         self._proxied.name,
227
                                         frame.qname(), id(self))
228

    
229
    def is_bound(self):
230
        return False
231

    
232
    def getattr(self, name, context=None):
233
        if name == 'im_func':
234
            return [self._proxied]
235
        return self._proxied.getattr(name, context)
236

    
237
    def igetattr(self, name, context=None):
238
        if name == 'im_func':
239
            return iter((self._proxied,))
240
        return self._proxied.igetattr(name, context)
241

    
242
    def infer_call_result(self, caller, context):
243
        # If we're unbound method __new__ of builtin object, the result is an
244
        # instance of the class given as first argument.
245
        if (self._proxied.name == '__new__' and
246
                self._proxied.parent.frame().qname() == '%s.object' % BUILTINS):
247
            infer = caller.args[0].infer() if caller.args else []
248
            return ((x is util.YES and x or Instance(x)) for x in infer)
249
        return self._proxied.infer_call_result(caller, context)
250

    
251

    
252
class BoundMethod(UnboundMethod):
253
    """a special node representing a method bound to an instance"""
254
    def __init__(self, proxy, bound):
255
        UnboundMethod.__init__(self, proxy)
256
        self.bound = bound
257

    
258
    def is_bound(self):
259
        return True
260

    
261
    def infer_call_result(self, caller, context=None):
262

    
263
        if context is None:
264
            context = contextmod.InferenceContext()
265
        context = context.clone()
266
        context.boundnode = self.bound
267
        return super(BoundMethod, self).infer_call_result(caller, context)
268

    
269

    
270
class Generator(Instance):
271
    """a special node representing a generator.
272

273
    Proxied class is set once for all in raw_building.
274
    """
275
    def callable(self):
276
        return False
277

    
278
    def pytype(self):
279
        return '%s.generator' % BUILTINS
280

    
281
    def display_type(self):
282
        return 'Generator'
283

    
284
    def __repr__(self):
285
        return '<Generator(%s) l.%s at 0x%s>' % (self._proxied.name, self.lineno, id(self))
286

    
287
    def __str__(self):
288
        return 'Generator(%s)' % (self._proxied.name)
289

    
290

    
291
# decorators ##################################################################
292

    
293
def path_wrapper(func):
294
    """return the given infer function wrapped to handle the path"""
295
    @functools.wraps(func)
296
    def wrapped(node, context=None, _func=func, **kwargs):
297
        """wrapper function handling context"""
298
        if context is None:
299
            context = contextmod.InferenceContext()
300
        context.push(node)
301
        yielded = set()
302
        for res in _func(node, context, **kwargs):
303
            # unproxy only true instance, not const, tuple, dict...
304
            if res.__class__ is Instance:
305
                ares = res._proxied
306
            else:
307
                ares = res
308
            if ares not in yielded:
309
                yield res
310
                yielded.add(ares)
311
    return wrapped
312

    
313
@wrapt.decorator
314
def yes_if_nothing_inferred(func, instance, args, kwargs):
315
    inferred = False
316
    for node in func(*args, **kwargs):
317
        inferred = True
318
        yield node
319
    if not inferred:
320
        yield util.YES
321

    
322
@wrapt.decorator
323
def raise_if_nothing_inferred(func, instance, args, kwargs):
324
    inferred = False
325
    for node in func(*args, **kwargs):
326
        inferred = True
327
        yield node
328
    if not inferred:
329
        raise exceptions.InferenceError()
330

    
331

    
332
# Node  ######################################################################
333

    
334
class NodeNG(object):
335
    """Base Class for all Astroid node classes.
336

337
    It represents a node of the new abstract syntax tree.
338
    """
339
    is_statement = False
340
    optional_assign = False # True for For (and for Comprehension if py <3.0)
341
    is_function = False # True for FunctionDef nodes
342
    # attributes below are set by the builder module or by raw factories
343
    lineno = None
344
    fromlineno = None
345
    tolineno = None
346
    col_offset = None
347
    # parent node in the tree
348
    parent = None
349
    # attributes containing child node(s) redefined in most concrete classes:
350
    _astroid_fields = ()
351
    # instance specific inference function infer(node, context)
352
    _explicit_inference = None
353

    
354
    def infer(self, context=None, **kwargs):
355
        """main interface to the interface system, return a generator on infered
356
        values.
357

358
        If the instance has some explicit inference function set, it will be
359
        called instead of the default interface.
360
        """
361
        if self._explicit_inference is not None:
362
            # explicit_inference is not bound, give it self explicitly
363
            try:
364
                return self._explicit_inference(self, context, **kwargs)
365
            except exceptions.UseInferenceDefault:
366
                pass
367

    
368
        if not context:
369
            return self._infer(context, **kwargs)
370

    
371
        key = (self, context.lookupname,
372
               context.callcontext, context.boundnode)
373
        if key in context.inferred:
374
            return iter(context.inferred[key])
375

    
376
        return context.cache_generator(key, self._infer(context, **kwargs))
377

    
378
    def _repr_name(self):
379
        """return self.name or self.attrname or '' for nice representation"""
380
        return getattr(self, 'name', getattr(self, 'attrname', ''))
381

    
382
    def __str__(self):
383
        return '%s(%s)' % (self.__class__.__name__, self._repr_name())
384

    
385
    def __repr__(self):
386
        return '<%s(%s) l.%s [%s] at 0x%x>' % (self.__class__.__name__,
387
                                               self._repr_name(),
388
                                               self.fromlineno,
389
                                               self.root().name,
390
                                               id(self))
391

    
392

    
393
    def accept(self, visitor):
394
        func = getattr(visitor, "visit_" + self.__class__.__name__.lower())
395
        return func(self)
396

    
397
    def get_children(self):
398
        for field in self._astroid_fields:
399
            attr = getattr(self, field)
400
            if attr is None:
401
                continue
402
            if isinstance(attr, (list, tuple)):
403
                for elt in attr:
404
                    yield elt
405
            else:
406
                yield attr
407

    
408
    def last_child(self):
409
        """an optimized version of list(get_children())[-1]"""
410
        for field in self._astroid_fields[::-1]:
411
            attr = getattr(self, field)
412
            if not attr: # None or empty listy / tuple
413
                continue
414
            if isinstance(attr, (list, tuple)):
415
                return attr[-1]
416
            else:
417
                return attr
418
        return None
419

    
420
    def parent_of(self, node):
421
        """return true if i'm a parent of the given node"""
422
        parent = node.parent
423
        while parent is not None:
424
            if self is parent:
425
                return True
426
            parent = parent.parent
427
        return False
428

    
429
    def statement(self):
430
        """return the first parent node marked as statement node"""
431
        if self.is_statement:
432
            return self
433
        return self.parent.statement()
434

    
435
    def frame(self):
436
        """return the first parent frame node (i.e. Module, FunctionDef or
437
        ClassDef)
438

439
        """
440
        return self.parent.frame()
441

    
442
    def scope(self):
443
        """return the first node defining a new scope (i.e. Module,
444
        FunctionDef, ClassDef, Lambda but also GenExpr)
445

446
        """
447
        return self.parent.scope()
448

    
449
    def root(self):
450
        """return the root node of the tree, (i.e. a Module)"""
451
        if self.parent:
452
            return self.parent.root()
453
        return self
454

    
455
    def child_sequence(self, child):
456
        """search for the right sequence where the child lies in"""
457
        for field in self._astroid_fields:
458
            node_or_sequence = getattr(self, field)
459
            if node_or_sequence is child:
460
                return [node_or_sequence]
461
            # /!\ compiler.ast Nodes have an __iter__ walking over child nodes
462
            if (isinstance(node_or_sequence, (tuple, list))
463
                    and child in node_or_sequence):
464
                return node_or_sequence
465

    
466
        msg = 'Could not find %s in %s\'s children'
467
        raise exceptions.AstroidError(msg % (repr(child), repr(self)))
468

    
469
    def locate_child(self, child):
470
        """return a 2-uple (child attribute name, sequence or node)"""
471
        for field in self._astroid_fields:
472
            node_or_sequence = getattr(self, field)
473
            # /!\ compiler.ast Nodes have an __iter__ walking over child nodes
474
            if child is node_or_sequence:
475
                return field, child
476
            if isinstance(node_or_sequence, (tuple, list)) and child in node_or_sequence:
477
                return field, node_or_sequence
478
        msg = 'Could not find %s in %s\'s children'
479
        raise exceptions.AstroidError(msg % (repr(child), repr(self)))
480
    # FIXME : should we merge child_sequence and locate_child ? locate_child
481
    # is only used in are_exclusive, child_sequence one time in pylint.
482

    
483
    def next_sibling(self):
484
        """return the next sibling statement"""
485
        return self.parent.next_sibling()
486

    
487
    def previous_sibling(self):
488
        """return the previous sibling statement"""
489
        return self.parent.previous_sibling()
490

    
491
    def nearest(self, nodes):
492
        """return the node which is the nearest before this one in the
493
        given list of nodes
494
        """
495
        myroot = self.root()
496
        mylineno = self.fromlineno
497
        nearest = None, 0
498
        for node in nodes:
499
            assert node.root() is myroot, \
500
                   'nodes %s and %s are not from the same module' % (self, node)
501
            lineno = node.fromlineno
502
            if node.fromlineno > mylineno:
503
                break
504
            if lineno > nearest[1]:
505
                nearest = node, lineno
506
        # FIXME: raise an exception if nearest is None ?
507
        return nearest[0]
508

    
509
    # these are lazy because they're relatively expensive to compute for every
510
    # single node, and they rarely get looked at
511

    
512
    @decoratorsmod.cachedproperty
513
    def fromlineno(self):
514
        if self.lineno is None:
515
            return self._fixed_source_line()
516
        else:
517
            return self.lineno
518

    
519
    @decoratorsmod.cachedproperty
520
    def tolineno(self):
521
        if not self._astroid_fields:
522
            # can't have children
523
            lastchild = None
524
        else:
525
            lastchild = self.last_child()
526
        if lastchild is None:
527
            return self.fromlineno
528
        else:
529
            return lastchild.tolineno
530

    
531
        # TODO / FIXME:
532
        assert self.fromlineno is not None, self
533
        assert self.tolineno is not None, self
534

    
535
    def _fixed_source_line(self):
536
        """return the line number where the given node appears
537

538
        we need this method since not all nodes have the lineno attribute
539
        correctly set...
540
        """
541
        line = self.lineno
542
        _node = self
543
        try:
544
            while line is None:
545
                _node = next(_node.get_children())
546
                line = _node.lineno
547
        except StopIteration:
548
            _node = self.parent
549
            while _node and line is None:
550
                line = _node.lineno
551
                _node = _node.parent
552
        return line
553

    
554
    def block_range(self, lineno):
555
        """handle block line numbers range for non block opening statements
556
        """
557
        return lineno, self.tolineno
558

    
559
    def set_local(self, name, stmt):
560
        """delegate to a scoped parent handling a locals dictionary"""
561
        self.parent.set_local(name, stmt)
562

    
563
    def nodes_of_class(self, klass, skip_klass=None):
564
        """return an iterator on nodes which are instance of the given class(es)
565

566
        klass may be a class object or a tuple of class objects
567
        """
568
        if isinstance(self, klass):
569
            yield self
570
        for child_node in self.get_children():
571
            if skip_klass is not None and isinstance(child_node, skip_klass):
572
                continue
573
            for matching in child_node.nodes_of_class(klass, skip_klass):
574
                yield matching
575

    
576
    def _infer_name(self, frame, name):
577
        # overridden for ImportFrom, Import, Global, TryExcept and Arguments
578
        return None
579

    
580
    def _infer(self, context=None):
581
        """we don't know how to resolve a statement by default"""
582
        # this method is overridden by most concrete classes
583
        raise exceptions.InferenceError(self.__class__.__name__)
584

    
585
    def inferred(self):
586
        '''return list of inferred values for a more simple inference usage'''
587
        return list(self.infer())
588

    
589
    def infered(self):
590
        warnings.warn('%s.infered() is deprecated and slated for removal '
591
                      'in astroid 2.0, use %s.inferred() instead.'
592
                      % (type(self).__name__, type(self).__name__),
593
                      PendingDeprecationWarning, stacklevel=2)
594
        return self.inferred()
595

    
596
    def instanciate_class(self):
597
        """instanciate a node if it is a ClassDef node, else return self"""
598
        return self
599

    
600
    def has_base(self, node):
601
        return False
602

    
603
    def callable(self):
604
        return False
605

    
606
    def eq(self, value):
607
        return False
608

    
609
    def as_string(self):
610
        from astroid.as_string import to_code
611
        return to_code(self)
612

    
613
    def repr_tree(self, ids=False):
614
        from astroid.as_string import dump
615
        return dump(self)
616

    
617

    
618
class Statement(NodeNG):
619
    """Statement node adding a few attributes"""
620
    is_statement = True
621

    
622
    def next_sibling(self):
623
        """return the next sibling statement"""
624
        stmts = self.parent.child_sequence(self)
625
        index = stmts.index(self)
626
        try:
627
            return stmts[index +1]
628
        except IndexError:
629
            pass
630

    
631
    def previous_sibling(self):
632
        """return the previous sibling statement"""
633
        stmts = self.parent.child_sequence(self)
634
        index = stmts.index(self)
635
        if index >= 1:
636
            return stmts[index -1]