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 / pylint / checkers / utils.py @ 745

History | View | Annotate | Download (26 KB)

1
# pylint: disable=W0611
2
#
3
# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
4
# http://www.logilab.fr/ -- mailto:contact@logilab.fr
5
#
6
# This program is free software; you can redistribute it and/or modify it under
7
# the terms of the GNU General Public License as published by the Free Software
8
# Foundation; either version 2 of the License, or (at your option) any later
9
# version.
10
#
11
# This program is distributed in the hope that it will be useful, but WITHOUT
12
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License along with
16
# this program; if not, write to the Free Software Foundation, Inc.,
17
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18
"""some functions that may be useful for various checkers
19
"""
20
import functools
21
import re
22
import sys
23
import string
24
import warnings
25

    
26
import six
27
from six.moves import map, builtins # pylint: disable=redefined-builtin
28

    
29
import astroid
30
from astroid import scoped_nodes
31

    
32
BUILTINS_NAME = builtins.__name__
33
COMP_NODE_TYPES = (astroid.ListComp, astroid.SetComp,
34
                   astroid.DictComp, astroid.GeneratorExp)
35
PY3K = sys.version_info[0] == 3
36

    
37
if not PY3K:
38
    EXCEPTIONS_MODULE = "exceptions"
39
else:
40
    EXCEPTIONS_MODULE = "builtins"
41
ABC_METHODS = set(('abc.abstractproperty', 'abc.abstractmethod',
42
                   'abc.abstractclassmethod', 'abc.abstractstaticmethod'))
43
ITER_METHOD = '__iter__'
44
NEXT_METHOD = 'next' if six.PY2 else '__next__'
45
GETITEM_METHOD = '__getitem__'
46
CONTAINS_METHOD = '__contains__'
47
KEYS_METHOD = 'keys'
48

    
49
# Dictionary which maps the number of expected parameters a
50
# special method can have to a set of special methods.
51
# The following keys are used to denote the parameters restrictions:
52
#
53
# * None: variable number of parameters
54
# * number: exactly that number of parameters
55
# * tuple: this are the odd ones. Basically it means that the function
56
#          can work with any number of arguments from that tuple,
57
#          although it's best to implement it in order to accept
58
#          all of them.
59
_SPECIAL_METHODS_PARAMS = {
60
    None: ('__new__', '__init__', '__call__'),
61

    
62
    0: ('__del__', '__repr__', '__str__', '__bytes__', '__hash__', '__bool__',
63
        '__dir__', '__len__', '__length_hint__', '__iter__', '__reversed__',
64
        '__neg__', '__pos__', '__abs__', '__invert__', '__complex__', '__int__',
65
        '__float__', '__neg__', '__pos__', '__abs__', '__complex__', '__int__',
66
        '__float__', '__index__', '__enter__', '__aenter__', '__getnewargs_ex__',
67
        '__getnewargs__', '__getstate__', '__reduce__', '__copy__',
68
        '__unicode__', '__nonzero__', '__await__', '__aiter__', '__anext__'),
69

    
70
    1: ('__format__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__',
71
        '__ge__', '__getattr__', '__getattribute__', '__delattr__',
72
        '__delete__', '__instancecheck__', '__subclasscheck__',
73
        '__getitem__', '__missing__', '__delitem__', '__contains__',
74
        '__add__', '__sub__', '__mul__', '__truediv__', '__floordiv__',
75
        '__mod__', '__divmod__', '__lshift__', '__rshift__', '__and__',
76
        '__xor__', '__or__', '__radd__', '__rsub__', '__rmul__', '__rtruediv__',
77
        '__rmod__', '__rdivmod__', '__rpow__', '__rlshift__', '__rrshift__',
78
        '__rand__', '__rxor__', '__ror__', '__iadd__', '__isub__', '__imul__',
79
        '__itruediv__', '__ifloordiv__', '__imod__', '__ilshift__',
80
        '__irshift__', '__iand__', '__ixor__', '__ior__', '__ipow__',
81
        '__setstate__', '__reduce_ex__', '__deepcopy__', '__cmp__',
82
        '__matmul__', '__rmatmul__'),
83

    
84
    2: ('__setattr__', '__get__', '__set__', '__setitem__'),
85

    
86
    3: ('__exit__', '__aexit__'),
87

    
88
    (0, 1): ('__round__', ),
89
}
90

    
91
SPECIAL_METHODS_PARAMS = {
92
    name: params
93
    for params, methods in _SPECIAL_METHODS_PARAMS.items()
94
    for name in methods
95
}
96
PYMETHODS = set(SPECIAL_METHODS_PARAMS)
97

    
98

    
99
class NoSuchArgumentError(Exception):
100
    pass
101

    
102
def is_inside_except(node):
103
    """Returns true if node is inside the name of an except handler."""
104
    current = node
105
    while current and not isinstance(current.parent, astroid.ExceptHandler):
106
        current = current.parent
107

    
108
    return current and current is current.parent.name
109

    
110

    
111
def get_all_elements(node):
112
    """Recursively returns all atoms in nested lists and tuples."""
113
    if isinstance(node, (astroid.Tuple, astroid.List)):
114
        for child in node.elts:
115
            for e in get_all_elements(child):
116
                yield e
117
    else:
118
        yield node
119

    
120

    
121
def clobber_in_except(node):
122
    """Checks if an assignment node in an except handler clobbers an existing
123
    variable.
124

125
    Returns (True, args for W0623) if assignment clobbers an existing variable,
126
    (False, None) otherwise.
127
    """
128
    if isinstance(node, astroid.AssignAttr):
129
        return (True, (node.attrname, 'object %r' % (node.expr.as_string(),)))
130
    elif isinstance(node, astroid.AssignName):
131
        name = node.name
132
        if is_builtin(name):
133
            return (True, (name, 'builtins'))
134
        else:
135
            stmts = node.lookup(name)[1]
136
            if (stmts and not isinstance(stmts[0].assign_type(),
137
                                         (astroid.Assign, astroid.AugAssign,
138
                                          astroid.ExceptHandler))):
139
                return (True, (name, 'outer scope (line %s)' % stmts[0].fromlineno))
140
    return (False, None)
141

    
142

    
143
def is_super(node):
144
    """return True if the node is referencing the "super" builtin function
145
    """
146
    if getattr(node, 'name', None) == 'super' and \
147
           node.root().name == BUILTINS_NAME:
148
        return True
149
    return False
150

    
151
def is_error(node):
152
    """return true if the function does nothing but raising an exception"""
153
    for child_node in node.get_children():
154
        if isinstance(child_node, astroid.Raise):
155
            return True
156
        return False
157

    
158
def is_raising(body):
159
    """return true if the given statement node raise an exception"""
160
    for node in body:
161
        if isinstance(node, astroid.Raise):
162
            return True
163
    return False
164

    
165
builtins = builtins.__dict__.copy()
166
SPECIAL_BUILTINS = ('__builtins__',) # '__path__', '__file__')
167

    
168
def is_builtin_object(node):
169
    """Returns True if the given node is an object from the __builtin__ module."""
170
    return node and node.root().name == BUILTINS_NAME
171

    
172
def is_builtin(name):
173
    """return true if <name> could be considered as a builtin defined by python
174
    """
175
    return name in builtins or name in SPECIAL_BUILTINS
176

    
177
def is_defined_before(var_node):
178
    """return True if the variable node is defined by a parent node (list,
179
    set, dict, or generator comprehension, lambda) or in a previous sibling
180
    node on the same line (statement_defining ; statement_using)
181
    """
182
    varname = var_node.name
183
    _node = var_node.parent
184
    while _node:
185
        if isinstance(_node, COMP_NODE_TYPES):
186
            for ass_node in _node.nodes_of_class(astroid.AssignName):
187
                if ass_node.name == varname:
188
                    return True
189
        elif isinstance(_node, astroid.For):
190
            for ass_node in _node.target.nodes_of_class(astroid.AssignName):
191
                if ass_node.name == varname:
192
                    return True
193
        elif isinstance(_node, astroid.With):
194
            for expr, ids in _node.items:
195
                if expr.parent_of(var_node):
196
                    break
197
                if (ids and
198
                        isinstance(ids, astroid.AssignName) and
199
                        ids.name == varname):
200
                    return True
201
        elif isinstance(_node, (astroid.Lambda, astroid.FunctionDef)):
202
            if _node.args.is_argument(varname):
203
                # If the name is found inside a default value
204
                # of a function, then let the search continue
205
                # in the parent's tree.
206
                if _node.args.parent_of(var_node):
207
                    try:
208
                        _node.args.default_value(varname)
209
                        _node = _node.parent
210
                        continue
211
                    except astroid.NoDefault:
212
                        pass
213
                return True
214
            if getattr(_node, 'name', None) == varname:
215
                return True
216
            break
217
        elif isinstance(_node, astroid.ExceptHandler):
218
            if isinstance(_node.name, astroid.AssignName):
219
                ass_node = _node.name
220
                if ass_node.name == varname:
221
                    return True
222
        _node = _node.parent
223
    # possibly multiple statements on the same line using semi colon separator
224
    stmt = var_node.statement()
225
    _node = stmt.previous_sibling()
226
    lineno = stmt.fromlineno
227
    while _node and _node.fromlineno == lineno:
228
        for ass_node in _node.nodes_of_class(astroid.AssignName):
229
            if ass_node.name == varname:
230
                return True
231
        for imp_node in _node.nodes_of_class((astroid.ImportFrom, astroid.Import)):
232
            if varname in [name[1] or name[0] for name in imp_node.names]:
233
                return True
234
        _node = _node.previous_sibling()
235
    return False
236

    
237
def is_func_default(node):
238
    """return true if the given Name node is used in function default argument's
239
    value
240
    """
241
    parent = node.scope()
242
    if isinstance(parent, astroid.FunctionDef):
243
        for default_node in parent.args.defaults:
244
            for default_name_node in default_node.nodes_of_class(astroid.Name):
245
                if default_name_node is node:
246
                    return True
247
    return False
248

    
249
def is_func_decorator(node):
250
    """return true if the name is used in function decorator"""
251
    parent = node.parent
252
    while parent is not None:
253
        if isinstance(parent, astroid.Decorators):
254
            return True
255
        if (parent.is_statement or
256
                isinstance(parent, astroid.Lambda) or
257
                isinstance(parent, (scoped_nodes.ComprehensionScope,
258
                                    scoped_nodes.ListComp))):
259
            break
260
        parent = parent.parent
261
    return False
262

    
263
def is_ancestor_name(frame, node):
264
    """return True if `frame` is a astroid.Class node with `node` in the
265
    subtree of its bases attribute
266
    """
267
    try:
268
        bases = frame.bases
269
    except AttributeError:
270
        return False
271
    for base in bases:
272
        if node in base.nodes_of_class(astroid.Name):
273
            return True
274
    return False
275

    
276
def assign_parent(node):
277
    """return the higher parent which is not an AssName, Tuple or List node
278
    """
279
    while node and isinstance(node, (astroid.AssignName,
280
                                     astroid.Tuple,
281
                                     astroid.List)):
282
        node = node.parent
283
    return node
284

    
285

    
286
def overrides_a_method(class_node, name):
287
    """return True if <name> is a method overridden from an ancestor"""
288
    for ancestor in class_node.ancestors():
289
        if name in ancestor and isinstance(ancestor[name], astroid.FunctionDef):
290
            return True
291
    return False
292

    
293
def check_messages(*messages):
294
    """decorator to store messages that are handled by a checker method"""
295

    
296
    def store_messages(func):
297
        func.checks_msgs = messages
298
        return func
299
    return store_messages
300

    
301
class IncompleteFormatString(Exception):
302
    """A format string ended in the middle of a format specifier."""
303
    pass
304

    
305
class UnsupportedFormatCharacter(Exception):
306
    """A format character in a format string is not one of the supported
307
    format characters."""
308
    def __init__(self, index):
309
        Exception.__init__(self, index)
310
        self.index = index
311

    
312
def parse_format_string(format_string):
313
    """Parses a format string, returning a tuple of (keys, num_args), where keys
314
    is the set of mapping keys in the format string, and num_args is the number
315
    of arguments required by the format string.  Raises
316
    IncompleteFormatString or UnsupportedFormatCharacter if a
317
    parse error occurs."""
318
    keys = set()
319
    num_args = 0
320
    def next_char(i):
321
        i += 1
322
        if i == len(format_string):
323
            raise IncompleteFormatString
324
        return (i, format_string[i])
325
    i = 0
326
    while i < len(format_string):
327
        char = format_string[i]
328
        if char == '%':
329
            i, char = next_char(i)
330
            # Parse the mapping key (optional).
331
            key = None
332
            if char == '(':
333
                depth = 1
334
                i, char = next_char(i)
335
                key_start = i
336
                while depth != 0:
337
                    if char == '(':
338
                        depth += 1
339
                    elif char == ')':
340
                        depth -= 1
341
                    i, char = next_char(i)
342
                key_end = i - 1
343
                key = format_string[key_start:key_end]
344

    
345
            # Parse the conversion flags (optional).
346
            while char in '#0- +':
347
                i, char = next_char(i)
348
            # Parse the minimum field width (optional).
349
            if char == '*':
350
                num_args += 1
351
                i, char = next_char(i)
352
            else:
353
                while char in string.digits:
354
                    i, char = next_char(i)
355
            # Parse the precision (optional).
356
            if char == '.':
357
                i, char = next_char(i)
358
                if char == '*':
359
                    num_args += 1
360
                    i, char = next_char(i)
361
                else:
362
                    while char in string.digits:
363
                        i, char = next_char(i)
364
            # Parse the length modifier (optional).
365
            if char in 'hlL':
366
                i, char = next_char(i)
367
            # Parse the conversion type (mandatory).
368
            if PY3K:
369
                flags = 'diouxXeEfFgGcrs%a'
370
            else:
371
                flags = 'diouxXeEfFgGcrs%'
372
            if char not in flags:
373
                raise UnsupportedFormatCharacter(i)
374
            if key:
375
                keys.add(key)
376
            elif char != '%':
377
                num_args += 1
378
        i += 1
379
    return keys, num_args
380

    
381

    
382
def is_attr_protected(attrname):
383
    """return True if attribute name is protected (start with _ and some other
384
    details), False otherwise.
385
    """
386
    return attrname[0] == '_' and attrname != '_' and not (
387
        attrname.startswith('__') and attrname.endswith('__'))
388

    
389
def node_frame_class(node):
390
    """return klass node for a method node (or a staticmethod or a
391
    classmethod), return null otherwise
392
    """
393
    klass = node.frame()
394

    
395
    while klass is not None and not isinstance(klass, astroid.ClassDef):
396
        if klass.parent is None:
397
            klass = None
398
        else:
399
            klass = klass.parent.frame()
400

    
401
    return klass
402

    
403

    
404
def is_attr_private(attrname):
405
    """Check that attribute name is private (at least two leading underscores,
406
    at most one trailing underscore)
407
    """
408
    regex = re.compile('^_{2,}.*[^_]+_?$')
409
    return regex.match(attrname)
410

    
411
def get_argument_from_call(callfunc_node, position=None, keyword=None):
412
    """Returns the specified argument from a function call.
413

414
    :param callfunc_node: Node representing a function call to check.
415
    :param int position: position of the argument.
416
    :param str keyword: the keyword of the argument.
417

418
    :returns: The node representing the argument, None if the argument is not found.
419
    :raises ValueError: if both position and keyword are None.
420
    :raises NoSuchArgumentError: if no argument at the provided position or with
421
    the provided keyword.
422
    """
423
    if position is None and keyword is None:
424
        raise ValueError('Must specify at least one of: position or keyword.')
425
    if position is not None:
426
        try:
427
            return callfunc_node.args[position]
428
        except IndexError:
429
            pass
430
    if keyword and callfunc_node.keywords:
431
        for arg in callfunc_node.keywords:
432
            if arg.arg == keyword:
433
                return arg.value
434

    
435
    raise NoSuchArgumentError
436

    
437
def inherit_from_std_ex(node):
438
    """
439
    Return true if the given class node is subclass of
440
    exceptions.Exception.
441
    """
442
    if node.name in ('Exception', 'BaseException') \
443
            and node.root().name == EXCEPTIONS_MODULE:
444
        return True
445
    return any(inherit_from_std_ex(parent)
446
               for parent in node.ancestors(recurs=False))
447

    
448
def error_of_type(handler, error_type):
449
    """
450
    Check if the given exception handler catches
451
    the given error_type.
452

453
    The *handler* parameter is a node, representing an ExceptHandler node.
454
    The *error_type* can be an exception, such as AttributeError, or it
455
    can be a tuple of errors.
456
    The function will return True if the handler catches any of the
457
    given errors.
458
    """
459
    if not isinstance(error_type, tuple):
460
        error_type = (error_type, )
461
    expected_errors = {error.__name__ for error in error_type}
462
    return handler.catch(expected_errors)
463

    
464

    
465
def is_import_error(handler):
466
    warnings.warn("This function is deprecated in the favour of "
467
                  "error_of_type. It will be removed in Pylint 1.6.",
468
                  DeprecationWarning, stacklevel=2)
469
    return error_of_type(handler, ImportError)
470

    
471

    
472
def decorated_with_property(node):
473
    """ Detect if the given function node is decorated with a property. """
474
    if not node.decorators:
475
        return False
476
    for decorator in node.decorators.nodes:
477
        if not isinstance(decorator, astroid.Name):
478
            continue
479
        try:
480
            for infered in decorator.infer():
481
                if isinstance(infered, astroid.ClassDef):
482
                    if (infered.root().name == BUILTINS_NAME and
483
                            infered.name == 'property'):
484
                        return True
485
                    for ancestor in infered.ancestors():
486
                        if (ancestor.name == 'property' and
487
                                ancestor.root().name == BUILTINS_NAME):
488
                            return True
489
        except astroid.InferenceError:
490
            pass
491

    
492

    
493
def decorated_with(func, qnames):
494
    """Determine if the `func` node has a decorator with the qualified name `qname`."""
495
    decorators = func.decorators.nodes if func.decorators else []
496
    for decorator_node in decorators:
497
        dec = safe_infer(decorator_node)
498
        if dec and dec.qname() in qnames:
499
            return True
500

    
501

    
502
def unimplemented_abstract_methods(node, is_abstract_cb=None):
503
    """
504
    Get the unimplemented abstract methods for the given *node*.
505

506
    A method can be considered abstract if the callback *is_abstract_cb*
507
    returns a ``True`` value. The check defaults to verifying that
508
    a method is decorated with abstract methods.
509
    The function will work only for new-style classes. For old-style
510
    classes, it will simply return an empty dictionary.
511
    For the rest of them, it will return a dictionary of abstract method
512
    names and their inferred objects.
513
    """
514
    if is_abstract_cb is None:
515
        is_abstract_cb = functools.partial(
516
            decorated_with, qnames=ABC_METHODS)
517
    visited = {}
518
    try:
519
        mro = reversed(node.mro())
520
    except NotImplementedError:
521
        # Old style class, it will not have a mro.
522
        return {}
523
    except astroid.ResolveError:
524
        # Probably inconsistent hierarchy, don'try
525
        # to figure this out here.
526
        return {}
527
    for ancestor in mro:
528
        for obj in ancestor.values():
529
            infered = obj
530
            if isinstance(obj, astroid.AssignName):
531
                infered = safe_infer(obj)
532
                if not infered:
533
                    # Might be an abstract function,
534
                    # but since we don't have enough information
535
                    # in order to take this decision, we're taking
536
                    # the *safe* decision instead.
537
                    if obj.name in visited:
538
                        del visited[obj.name]
539
                    continue
540
                if not isinstance(infered, astroid.FunctionDef):
541
                    if obj.name in visited:
542
                        del visited[obj.name]
543
            if isinstance(infered, astroid.FunctionDef):
544
                # It's critical to use the original name,
545
                # since after inferring, an object can be something
546
                # else than expected, as in the case of the
547
                # following assignment.
548
                #
549
                # class A:
550
                #     def keys(self): pass
551
                #     __iter__ = keys
552
                abstract = is_abstract_cb(infered)
553
                if abstract:
554
                    visited[obj.name] = infered
555
                elif not abstract and obj.name in visited:
556
                    del visited[obj.name]
557
    return visited
558

    
559

    
560
def node_ignores_exception(node, exception):
561
    """Check if the node is in a TryExcept which handles the given exception.
562

563
    This will also return ``True`` if the node is protected by an `except Exception`
564
    or by a bare except clause.
565
    """
566
    current = node
567
    ignores = (astroid.ExceptHandler, astroid.TryExcept)
568
    while current and not isinstance(current.parent, ignores):
569
        current = current.parent
570

    
571
    func = functools.partial(error_of_type,
572
                             error_type=(Exception, exception))
573
    if current and isinstance(current.parent, astroid.TryExcept):
574
        handles_errors = any(map(func, current.parent.handlers))
575
        empty_handlers = any(handler.type is None
576
                             for handler in current.parent.handlers)
577
        if handles_errors or empty_handlers:
578
            return True
579
    return False
580

    
581

    
582
def class_is_abstract(node):
583
    """return true if the given class node should be considered as an abstract
584
    class
585
    """
586
    for method in node.methods():
587
        if method.parent.frame() is node:
588
            if method.is_abstract(pass_is_abstract=False):
589
                return True
590
    return False
591

    
592

    
593
def _hasattr(value, attr):
594
    try:
595
        value.getattr(attr)
596
        return True
597
    except astroid.NotFoundError:
598
        return False
599

    
600

    
601
def is_comprehension(node):
602
    comprehensions = (astroid.ListComp,
603
                      astroid.SetComp,
604
                      astroid.DictComp,
605
                      astroid.GeneratorExp)
606
    return isinstance(node, comprehensions)
607

    
608

    
609
def _supports_mapping_protocol(value):
610
    return _hasattr(value, GETITEM_METHOD) and _hasattr(value, KEYS_METHOD)
611

    
612

    
613
def _supports_membership_test_protocol(value):
614
    return _hasattr(value, CONTAINS_METHOD)
615

    
616

    
617
def _supports_iteration_protocol(value):
618
    return _hasattr(value, ITER_METHOD) or _hasattr(value, GETITEM_METHOD)
619

    
620

    
621
def _supports_subscript_protocol(value):
622
    return _hasattr(value, GETITEM_METHOD)
623

    
624

    
625
def _is_abstract_class_name(name):
626
    lname = name.lower()
627
    is_mixin = lname.endswith('mixin')
628
    is_abstract = lname.startswith('abstract')
629
    is_base = lname.startswith('base') or lname.endswith('base')
630
    return is_mixin or is_abstract or is_base
631

    
632

    
633
def is_inside_abstract_class(node):
634
    while node is not None:
635
        if isinstance(node, astroid.ClassDef):
636
            if class_is_abstract(node):
637
                return True
638
            name = getattr(node, 'name', None)
639
            if name is not None and _is_abstract_class_name(name):
640
                return True
641
        node = node.parent
642
    return False
643

    
644

    
645
def is_iterable(value):
646
    if isinstance(value, astroid.ClassDef):
647
        if not has_known_bases(value):
648
            return True
649
        # classobj can only be iterable if it has an iterable metaclass
650
        meta = value.metaclass()
651
        if meta is not None:
652
            if _supports_iteration_protocol(meta):
653
                return True
654
    if isinstance(value, astroid.Instance):
655
        if not has_known_bases(value):
656
            return True
657
        if _supports_iteration_protocol(value):
658
            return True
659
    return False
660

    
661

    
662
def is_mapping(value):
663
    if isinstance(value, astroid.ClassDef):
664
        if not has_known_bases(value):
665
            return True
666
        # classobj can only be a mapping if it has a metaclass is mapping
667
        meta = value.metaclass()
668
        if meta is not None:
669
            if _supports_mapping_protocol(meta):
670
                return True
671
    if isinstance(value, astroid.Instance):
672
        if not has_known_bases(value):
673
            return True
674
        if _supports_mapping_protocol(value):
675
            return True
676
    return False
677

    
678

    
679
def supports_membership_test(value):
680
    if isinstance(value, astroid.ClassDef):
681
        if not has_known_bases(value):
682
            return True
683
        meta = value.metaclass()
684
        if meta is not None and _supports_membership_test_protocol(meta):
685
            return True
686
    if isinstance(value, astroid.Instance):
687
        if not has_known_bases(value):
688
            return True
689
        if _supports_membership_test_protocol(value):
690
            return True
691
    return is_iterable(value)
692

    
693

    
694
def supports_subscript(value):
695
    if isinstance(value, astroid.ClassDef):
696
        if not has_known_bases(value):
697
            return False
698
        meta = value.metaclass()
699
        if meta is not None and _supports_subscript_protocol(meta):
700
            return True
701
    if isinstance(value, astroid.Instance):
702
        if not has_known_bases(value):
703
            return True
704
        if _supports_subscript_protocol(value):
705
            return True
706
    return False
707

    
708
# TODO(cpopa): deprecate these or leave them as aliases?
709
def safe_infer(node, context=None):
710
    """Return the inferred value for the given node.
711

712
    Return None if inference failed or if there is some ambiguity (more than
713
    one node has been inferred).
714
    """
715
    try:
716
        inferit = node.infer(context=context)
717
        value = next(inferit)
718
    except astroid.InferenceError:
719
        return
720
    try:
721
        next(inferit)
722
        return # None if there is ambiguity on the inferred node
723
    except astroid.InferenceError:
724
        return # there is some kind of ambiguity
725
    except StopIteration:
726
        return value
727

    
728

    
729
def has_known_bases(klass, context=None):
730
    """Return true if all base classes of a class could be inferred."""
731
    try:
732
        return klass._all_bases_known
733
    except AttributeError:
734
        pass
735
    for base in klass.bases:
736
        result = safe_infer(base, context=context)
737
        # TODO: check for A->B->A->B pattern in class structure too?
738
        if (not isinstance(result, astroid.ClassDef) or
739
                result is klass or
740
                not has_known_bases(result, context=context)):
741
            klass._all_bases_known = False
742
            return False
743
    klass._all_bases_known = True
744
    return True