From fb6b47b10084423e6c43a30538cb8eca39ce7408 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Fri, 20 Nov 2015 12:52:19 +0200 Subject: Import has_known_bases and safe_infer back into pylint from astroid, until the latter stabilizes its API. Currently astroid goes into a total revamp, having a couple of development branches with partially incompatible APIs, which means that pylint can't rely on the exact location of has_known_bases and safe_infer until astroid reaches a new major release. With this in mind, these two functions are backported in pylint again. --- pylint/checkers/async.py | 5 ++-- pylint/checkers/base.py | 27 +++++++++++---------- pylint/checkers/classes.py | 10 ++++---- pylint/checkers/exceptions.py | 17 ++++++------- pylint/checkers/logging.py | 3 +-- pylint/checkers/newstyle.py | 9 +++---- pylint/checkers/python3.py | 5 ++-- pylint/checkers/stdlib.py | 3 +-- pylint/checkers/strings.py | 7 +++--- pylint/checkers/typecheck.py | 33 +++++++++++++------------- pylint/checkers/utils.py | 55 ++++++++++++++++++++++++++++++++++--------- pylint/checkers/variables.py | 6 ++--- 12 files changed, 106 insertions(+), 74 deletions(-) diff --git a/pylint/checkers/async.py b/pylint/checkers/async.py index bd82931..c1914f4 100644 --- a/pylint/checkers/async.py +++ b/pylint/checkers/async.py @@ -17,7 +17,6 @@ import astroid from astroid import exceptions -from astroid import helpers from pylint import checkers from pylint.checkers import utils as checker_utils @@ -53,7 +52,7 @@ class AsyncChecker(checkers.BaseChecker): @checker_utils.check_messages('not-async-context-manager') def visit_asyncwith(self, node): for ctx_mgr, _ in node.items: - infered = helpers.safe_infer(ctx_mgr) + infered = utils.safe_infer(ctx_mgr) if infered is None or infered is astroid.YES: continue @@ -65,7 +64,7 @@ class AsyncChecker(checkers.BaseChecker): if isinstance(infered, astroid.Instance): # If we do not know the bases of this class, # just skip it. - if not helpers.has_known_bases(infered): + if not utils.has_known_bases(infered): continue # Just ignore mixin classes. if self._ignore_mixin_members: diff --git a/pylint/checkers/base.py b/pylint/checkers/base.py index 6929a7a..fe2578e 100644 --- a/pylint/checkers/base.py +++ b/pylint/checkers/base.py @@ -28,7 +28,6 @@ import astroid import astroid.bases import astroid.scoped_nodes from astroid import are_exclusive, InferenceError -from astroid import helpers from pylint.interfaces import (IAstroidChecker, ITokenChecker, INFERENCE, INFERENCE_FAILURE, HIGH) @@ -46,6 +45,8 @@ from pylint.checkers.utils import ( NoSuchArgumentError, error_of_type, unimplemented_abstract_methods, + has_known_bases, + safe_infer ) from pylint.reporters.ureports.nodes import Table @@ -188,7 +189,7 @@ def _determine_function_name_type(node): if (isinstance(decorator, astroid.Name) or (isinstance(decorator, astroid.Attribute) and decorator.attrname == 'abstractproperty')): - infered = helpers.safe_infer(decorator) + infered = safe_infer(decorator) if infered and infered.qname() in PROPERTY_CLASSES: return 'attr' # If the function is decorated using the prop_method.{setter,getter} @@ -735,7 +736,7 @@ functions, methods inferred = None emit = isinstance(test, (astroid.Const, ) + structs + const_nodes) if not isinstance(test, except_nodes): - inferred = helpers.safe_infer(test) + inferred = safe_infer(test) if emit or isinstance(inferred, const_nodes): self.add_message('using-constant-test', node=node) @@ -1032,7 +1033,7 @@ functions, methods def _check_reversed(self, node): """ check that the argument to `reversed` is a sequence """ try: - argument = helpers.safe_infer(get_argument_from_call(node, position=0)) + argument = safe_infer(get_argument_from_call(node, position=0)) except NoSuchArgumentError: pass else: @@ -1230,7 +1231,7 @@ class NameChecker(_BasicChecker): if node.is_method(): if overrides_a_method(node.parent.frame(), node.name): return - confidence = (INFERENCE if helpers.has_known_bases(node.parent.frame()) + confidence = (INFERENCE if has_known_bases(node.parent.frame()) else INFERENCE_FAILURE) self._check_name(_determine_function_name_type(node), @@ -1254,7 +1255,7 @@ class NameChecker(_BasicChecker): self._check_name('inlinevar', node.name, node) elif isinstance(frame, astroid.Module): if isinstance(ass_type, astroid.Assign) and not in_loop(ass_type): - if isinstance(helpers.safe_infer(ass_type.value), astroid.ClassDef): + if isinstance(safe_infer(ass_type.value), astroid.ClassDef): self._check_name('class', node.name, node) else: if not _redefines_import(node): @@ -1365,7 +1366,7 @@ class DocStringChecker(_BasicChecker): ftype = node.is_method() and 'method' or 'function' if isinstance(node.parent.frame(), astroid.ClassDef): overridden = False - confidence = (INFERENCE if helpers.has_known_bases(node.parent.frame()) + confidence = (INFERENCE if has_known_bases(node.parent.frame()) else INFERENCE_FAILURE) # check if node is from a method overridden by its ancestor for ancestor in node.parent.frame().ancestors(): @@ -1403,7 +1404,7 @@ class DocStringChecker(_BasicChecker): if (node.body and isinstance(node.body[0], astroid.Expr) and isinstance(node.body[0].value, astroid.Call)): # Most likely a string with a format call. Let's see. - func = helpers.safe_infer(node.body[0].value.func) + func = safe_infer(node.body[0].value.func) if (isinstance(func, astroid.BoundMethod) and isinstance(func.bound, astroid.Instance)): # Strings in Python 3, others in Python 2. @@ -1456,7 +1457,7 @@ class LambdaForComprehensionChecker(_BasicChecker): return if not isinstance(node.args[0], astroid.Lambda): return - infered = helpers.safe_infer(node.func) + infered = safe_infer(node.func) if (is_builtin_object(infered) and infered.name in ['map', 'filter']): self.add_message('deprecated-lambda', node=node) @@ -1472,7 +1473,7 @@ class RecommandationChecker(_BasicChecker): @staticmethod def _is_builtin(node, function): - inferred = helpers.safe_infer(node) + inferred = safe_infer(node) if not inferred: return False return is_builtin_object(inferred) and inferred.name == function @@ -1617,17 +1618,17 @@ class ComparisonChecker(_BasicChecker): def _check_type_x_is_y(self, node, left, operator, right): """Check for expressions like type(x) == Y.""" - left_func = helpers.safe_infer(left.func) + left_func = safe_infer(left.func) if not (isinstance(left_func, astroid.ClassDef) and left_func.qname() == TYPE_QNAME): return if operator in ('is', 'is not') and _is_one_arg_pos_call(right): - right_func = helpers.safe_infer(right.func) + right_func = safe_infer(right.func) if (isinstance(right_func, astroid.ClassDef) and right_func.qname() == TYPE_QNAME): # type(x) == type(a) - right_arg = helpers.safe_infer(right.args[0]) + right_arg = safe_infer(right.args[0]) if not isinstance(right_arg, LITERAL_NODE_TYPES): # not e.g. type(x) == type([]) return diff --git a/pylint/checkers/classes.py b/pylint/checkers/classes.py index 5e941c5..2dc45e1 100644 --- a/pylint/checkers/classes.py +++ b/pylint/checkers/classes.py @@ -25,7 +25,6 @@ from astroid.bases import Generator, BUILTINS from astroid.exceptions import InconsistentMroError, DuplicateBasesError from astroid import objects from astroid.scoped_nodes import function_to_method -from astroid import helpers from pylint.interfaces import IAstroidChecker from pylint.checkers import BaseChecker @@ -34,7 +33,8 @@ from pylint.checkers.utils import ( overrides_a_method, check_messages, is_attr_private, is_attr_protected, node_frame_class, is_builtin_object, decorated_with_property, unimplemented_abstract_methods, - decorated_with, class_is_abstract) + decorated_with, class_is_abstract, + safe_infer, has_known_bases) from pylint.utils import deprecated_option, get_global_option import six @@ -353,7 +353,7 @@ a metaclass class method.'} self._accessed.append(defaultdict(list)) self._check_bases_classes(node) # if not an exception or a metaclass - if node.type == 'class' and helpers.has_known_bases(node): + if node.type == 'class' and has_known_bases(node): try: node.local_attr('__init__') except astroid.NotFoundError: @@ -382,7 +382,7 @@ a metaclass class method.'} a class or a type. """ for base in node.bases: - ancestor = helpers.safe_infer(base) + ancestor = safe_infer(base) if ancestor in (astroid.YES, None): continue if (isinstance(ancestor, astroid.Instance) and @@ -602,7 +602,7 @@ a metaclass class method.'} """ Check that the given assattr node is defined in the class slots. """ - infered = helpers.safe_infer(node.expr) + infered = safe_infer(node.expr) if infered and isinstance(infered, astroid.Instance): klass = infered._proxied if '__slots__' not in klass.locals or not klass.newstyle: diff --git a/pylint/checkers/exceptions.py b/pylint/checkers/exceptions.py index c068c98..b66ff8d 100644 --- a/pylint/checkers/exceptions.py +++ b/pylint/checkers/exceptions.py @@ -18,7 +18,6 @@ import inspect import sys import astroid -from astroid import helpers from six.moves import builtins import six @@ -27,7 +26,9 @@ from pylint.checkers.utils import ( is_raising, check_messages, inherit_from_std_ex, - EXCEPTIONS_MODULE) + EXCEPTIONS_MODULE, + safe_infer, + has_known_bases) from pylint.interfaces import IAstroidChecker @@ -48,7 +49,7 @@ def _annotated_unpack_infer(stmt, context=None): """ if isinstance(stmt, (astroid.List, astroid.Tuple)): for elt in stmt.elts: - inferred = helpers.safe_infer(elt) + inferred = safe_infer(elt) if inferred and inferred is not astroid.YES: yield elt, inferred return @@ -194,7 +195,7 @@ class ExceptionsChecker(BaseChecker): An exception context can be only `None` or an exception. """ - cause = helpers.safe_infer(node.cause) + cause = safe_infer(node.cause) if cause in (astroid.YES, None): return if isinstance(cause, astroid.Const): @@ -230,7 +231,7 @@ class ExceptionsChecker(BaseChecker): # Verifying the other arguments is not # the scope of this check. first = expr.elts[0] - inferred = helpers.safe_infer(first) + inferred = safe_infer(first) if isinstance(inferred, astroid.Instance): # pylint: disable=protected-access inferred = inferred._proxied @@ -253,7 +254,7 @@ class ExceptionsChecker(BaseChecker): expr = expr._proxied if (isinstance(expr, astroid.ClassDef) and not inherit_from_std_ex(expr) and - helpers.has_known_bases(expr)): + has_known_bases(expr)): if expr.newstyle: self.add_message('raising-non-exception', node=node) else: @@ -267,7 +268,7 @@ class ExceptionsChecker(BaseChecker): def _check_catching_non_exception(self, handler, exc, part): if isinstance(exc, astroid.Tuple): # Check if it is a tuple of exceptions. - inferred = [helpers.safe_infer(elt) for elt in exc.elts] + inferred = [safe_infer(elt) for elt in exc.elts] if any(node is astroid.YES for node in inferred): # Don't emit if we don't know every component. return @@ -299,7 +300,7 @@ class ExceptionsChecker(BaseChecker): if (not inherit_from_std_ex(exc) and exc.name not in self.builtin_exceptions): - if helpers.has_known_bases(exc): + if has_known_bases(exc): self.add_message('catching-non-exception', node=handler.type, args=(exc.name, )) diff --git a/pylint/checkers/logging.py b/pylint/checkers/logging.py index 9ef85dd..a5b1edf 100644 --- a/pylint/checkers/logging.py +++ b/pylint/checkers/logging.py @@ -15,7 +15,6 @@ """ import astroid -from astroid import helpers from pylint import checkers from pylint import interfaces @@ -80,7 +79,7 @@ def is_method_call(callfunc_node, types=(), methods=()): """ if not isinstance(callfunc_node, astroid.Call): return False - func = helpers.safe_infer(callfunc_node.func) + func = utils.safe_infer(callfunc_node.func) return (isinstance(func, astroid.BoundMethod) and isinstance(func.bound, astroid.Instance) and (func.bound.name in types if types else True) diff --git a/pylint/checkers/newstyle.py b/pylint/checkers/newstyle.py index fe8fa1b..30bc6a6 100644 --- a/pylint/checkers/newstyle.py +++ b/pylint/checkers/newstyle.py @@ -18,13 +18,14 @@ import sys import astroid -from astroid import helpers from pylint.interfaces import IAstroidChecker, INFERENCE, INFERENCE_FAILURE, HIGH from pylint.checkers import BaseChecker from pylint.checkers.utils import ( check_messages, node_frame_class, + safe_infer, + has_known_bases ) MSGS = { @@ -82,7 +83,7 @@ class NewStyleConflictChecker(BaseChecker): style class definition. """ if '__slots__' in node and not node.newstyle: - confidence = (INFERENCE if helpers.has_known_bases(node) + confidence = (INFERENCE if has_known_bases(node) else INFERENCE_FAILURE) self.add_message('slots-on-old-class', node=node, confidence=confidence) @@ -101,7 +102,7 @@ class NewStyleConflictChecker(BaseChecker): if (isinstance(parent, astroid.ClassDef) and not parent.newstyle and isinstance(node.func, astroid.Name)): - confidence = (INFERENCE if helpers.has_known_bases(parent) + confidence = (INFERENCE if has_known_bases(parent) else INFERENCE_FAILURE) name = node.func.name if name == 'property': @@ -128,7 +129,7 @@ class NewStyleConflictChecker(BaseChecker): isinstance(call.func, astroid.Name) and call.func.name == 'super'): continue - confidence = (INFERENCE if helpers.has_known_bases(klass) + confidence = (INFERENCE if has_known_bases(klass) else INFERENCE_FAILURE) if not klass.newstyle: # super should not be used on an old style class diff --git a/pylint/checkers/python3.py b/pylint/checkers/python3.py index bc78892..038c2c2 100644 --- a/pylint/checkers/python3.py +++ b/pylint/checkers/python3.py @@ -19,7 +19,6 @@ import tokenize import astroid from astroid import bases -from astroid import helpers from pylint import checkers, interfaces from pylint.utils import WarningScope @@ -448,7 +447,7 @@ class Python3Checker(checkers.BaseChecker): kwargs = [] if (isinstance(node.func, astroid.Attribute) and node.func.attrname == 'sort'): - inferred = helpers.safe_infer(node.func.expr) + inferred = utils.safe_infer(node.func.expr) if not inferred: return @@ -459,7 +458,7 @@ class Python3Checker(checkers.BaseChecker): elif (isinstance(node.func, astroid.Name) and node.func.name == 'sorted'): - inferred = helpers.safe_infer(node.func) + inferred = utils.safe_infer(node.func) if not inferred: return diff --git a/pylint/checkers/stdlib.py b/pylint/checkers/stdlib.py index f583215..61df660 100644 --- a/pylint/checkers/stdlib.py +++ b/pylint/checkers/stdlib.py @@ -20,7 +20,6 @@ import sys import astroid from astroid.bases import Instance -from astroid import helpers from pylint.interfaces import IAstroidChecker from pylint.checkers import BaseChecker @@ -263,7 +262,7 @@ class StdlibChecker(BaseChecker): except utils.NoSuchArgumentError: return if mode_arg: - mode_arg = helpers.safe_infer(mode_arg) + mode_arg = utils.safe_infer(mode_arg) if (isinstance(mode_arg, astroid.Const) and not _check_mode_str(mode_arg.value)): self.add_message('bad-open-mode', node=node, diff --git a/pylint/checkers/strings.py b/pylint/checkers/strings.py index 5fb9a44..31f025d 100644 --- a/pylint/checkers/strings.py +++ b/pylint/checkers/strings.py @@ -24,7 +24,6 @@ import string import numbers import astroid -from astroid import helpers from pylint.interfaces import ITokenChecker, IAstroidChecker, IRawChecker from pylint.checkers import BaseChecker, BaseTokenChecker @@ -206,7 +205,7 @@ def get_args(callfunc): is the keyword arguments in a dict. """ if callfunc.keywords: - named = {arg.arg: helpers.safe_infer(arg.value) + named = {arg.arg: utils.safe_infer(arg.value) for arg in callfunc.keywords} else: named = {} @@ -333,12 +332,12 @@ class StringMethodsChecker(BaseChecker): @check_messages(*(MSGS.keys())) def visit_call(self, node): - func = helpers.safe_infer(node.func) + func = utils.safe_infer(node.func) if (isinstance(func, astroid.BoundMethod) and isinstance(func.bound, astroid.Instance) and func.bound.name in ('str', 'unicode', 'bytes')): if func.name in ('strip', 'lstrip', 'rstrip') and node.args: - arg = helpers.safe_infer(node.args[0]) + arg = utils.safe_infer(node.args[0]) if not isinstance(arg, astroid.Const): return if len(arg.value) != len(set(arg.value)): diff --git a/pylint/checkers/typecheck.py b/pylint/checkers/typecheck.py index f8c75dd..0f5d7af 100644 --- a/pylint/checkers/typecheck.py +++ b/pylint/checkers/typecheck.py @@ -27,7 +27,6 @@ import astroid.context import astroid.arguments from astroid import exceptions from astroid import objects -from astroid import helpers from astroid import node_classes import six @@ -38,7 +37,9 @@ from pylint.checkers.utils import ( decorated_with, node_ignores_exception, is_iterable, is_mapping, supports_membership_test, is_comprehension, is_inside_abstract_class, - supports_subscript) + supports_subscript, + safe_infer, + has_known_bases) from pylint import utils @@ -189,7 +190,7 @@ def _emit_no_member(node, owner, owner_name, ignored_mixins): if isinstance(owner, astroid.FunctionDef) and owner.decorators: return False if isinstance(owner, astroid.Instance): - if owner.has_dynamic_getattr() or not helpers.has_known_bases(owner): + if owner.has_dynamic_getattr() or not has_known_bases(owner): return False if isinstance(owner, objects.Super): # Verify if we are dealing with an invalid Super object. @@ -200,7 +201,7 @@ def _emit_no_member(node, owner, owner_name, ignored_mixins): owner.super_mro() except (exceptions.MroError, exceptions.SuperError): return False - if not all(map(helpers.has_known_bases, owner.type.mro())): + if not all(map(has_known_bases, owner.type.mro())): return False return True @@ -400,7 +401,7 @@ accessed. Python regular expressions are accepted.'} """ if not isinstance(node.value, astroid.Call): return - function_node = helpers.safe_infer(node.value.func) + function_node = safe_infer(node.value.func) # skip class, generator and incomplete function definition if not (isinstance(function_node, astroid.FunctionDef) and function_node.root().fully_defined()): @@ -438,7 +439,7 @@ accessed. Python regular expressions are accepted.'} # we will not handle them here, right now. expr = node.func.expr - klass = helpers.safe_infer(expr) + klass = safe_infer(expr) if (klass is None or klass is astroid.YES or not isinstance(klass, astroid.Instance)): return @@ -477,7 +478,7 @@ accessed. Python regular expressions are accepted.'} num_positional_args = len(call_site.positional_arguments) keyword_args = list(call_site.keyword_arguments.keys()) - called = helpers.safe_infer(node.func) + called = safe_infer(node.func) # only function, generator and object defining __call__ are allowed if called is not None and not called.callable(): self.add_message('not-callable', node=node, @@ -629,7 +630,7 @@ accessed. Python regular expressions are accepted.'} # Look for index operations where the parent is a sequence type. # If the types can be determined, only allow indices to be int, # slice or instances with __index__. - parent_type = helpers.safe_infer(node.parent.value) + parent_type = safe_infer(node.parent.value) if not isinstance(parent_type, (astroid.ClassDef, astroid.Instance)): return @@ -670,7 +671,7 @@ accessed. Python regular expressions are accepted.'} if isinstance(node, astroid.ExtSlice): index_type = node else: - index_type = helpers.safe_infer(node) + index_type = safe_infer(node) if index_type is None or index_type is astroid.YES: return # Constants must be of type int @@ -702,7 +703,7 @@ accessed. Python regular expressions are accepted.'} if index is None: continue - index_type = helpers.safe_infer(index) + index_type = safe_infer(index) if index_type is None or index_type is astroid.YES: continue @@ -730,7 +731,7 @@ accessed. Python regular expressions are accepted.'} def visit_with(self, node): for ctx_mgr, _ in node.items: context = astroid.context.InferenceContext() - infered = helpers.safe_infer(ctx_mgr, context=context) + infered = safe_infer(ctx_mgr, context=context) if infered is None or infered is astroid.YES: continue @@ -766,7 +767,7 @@ accessed. Python regular expressions are accepted.'} if isinstance(infered, astroid.Instance): # If we do not know the bases of this class, # just skip it. - if not helpers.has_known_bases(infered): + if not has_known_bases(infered): continue # Just ignore mixin classes. if self.config.ignore_mixin_members: @@ -806,7 +807,7 @@ accessed. Python regular expressions are accepted.'} return if is_comprehension(node): return - infered = helpers.safe_infer(node) + infered = safe_infer(node) if infered is None or infered is astroid.YES: return if not supports_membership_test(infered): @@ -830,7 +831,7 @@ accessed. Python regular expressions are accepted.'} self.add_message('unsubscriptable-object', args=node.value.as_string(), node=node.value) - infered = helpers.safe_infer(node.value) + infered = safe_infer(node.value) if infered is None or infered is astroid.YES: return if not supports_subscript(infered): @@ -870,7 +871,7 @@ class IterableChecker(BaseChecker): return if is_comprehension(node): return - infered = helpers.safe_infer(node) + infered = safe_infer(node) if infered is None or infered is astroid.YES: return if not is_iterable(infered): @@ -883,7 +884,7 @@ class IterableChecker(BaseChecker): return if isinstance(node, astroid.DictComp): return - infered = helpers.safe_infer(node) + infered = safe_infer(node) if infered is None or infered is astroid.YES: return if not is_mapping(infered): diff --git a/pylint/checkers/utils.py b/pylint/checkers/utils.py index a3c8c01..b85c237 100644 --- a/pylint/checkers/utils.py +++ b/pylint/checkers/utils.py @@ -24,7 +24,6 @@ import string import warnings import astroid -from astroid import helpers from astroid import scoped_nodes import six from six.moves import map, builtins # pylint: disable=redefined-builtin @@ -644,7 +643,7 @@ def is_inside_abstract_class(node): def is_iterable(value): if isinstance(value, astroid.ClassDef): - if not helpers.has_known_bases(value): + if not has_known_bases(value): return True # classobj can only be iterable if it has an iterable metaclass meta = value.metaclass() @@ -652,7 +651,7 @@ def is_iterable(value): if _supports_iteration_protocol(meta): return True if isinstance(value, astroid.Instance): - if not helpers.has_known_bases(value): + if not has_known_bases(value): return True if _supports_iteration_protocol(value): return True @@ -661,7 +660,7 @@ def is_iterable(value): def is_mapping(value): if isinstance(value, astroid.ClassDef): - if not helpers.has_known_bases(value): + if not has_known_bases(value): return True # classobj can only be a mapping if it has a metaclass is mapping meta = value.metaclass() @@ -669,7 +668,7 @@ def is_mapping(value): if _supports_mapping_protocol(meta): return True if isinstance(value, astroid.Instance): - if not helpers.has_known_bases(value): + if not has_known_bases(value): return True if _supports_mapping_protocol(value): return True @@ -678,13 +677,13 @@ def is_mapping(value): def supports_membership_test(value): if isinstance(value, astroid.ClassDef): - if not helpers.has_known_bases(value): + if not has_known_bases(value): return True meta = value.metaclass() if meta is not None and _supports_membership_test_protocol(meta): return True if isinstance(value, astroid.Instance): - if not helpers.has_known_bases(value): + if not has_known_bases(value): return True if _supports_membership_test_protocol(value): return True @@ -693,18 +692,52 @@ def supports_membership_test(value): def supports_subscript(value): if isinstance(value, astroid.ClassDef): - if not helpers.has_known_bases(value): + if not has_known_bases(value): return False meta = value.metaclass() if meta is not None and _supports_subscript_protocol(meta): return True if isinstance(value, astroid.Instance): - if not helpers.has_known_bases(value): + if not has_known_bases(value): return True if _supports_subscript_protocol(value): return True return False # TODO(cpopa): deprecate these or leave them as aliases? -safe_infer = astroid.helpers.safe_infer -has_known_bases = astroid.helpers.has_known_bases +def safe_infer(node, context=None): + """Return the inferred value for the given node. + + Return None if inference failed or if there is some ambiguity (more than + one node has been inferred). + """ + try: + inferit = node.infer(context=context) + value = next(inferit) + except astroid.InferenceError: + return + try: + next(inferit) + return # None if there is ambiguity on the inferred node + except astroid.InferenceError: + return # there is some kind of ambiguity + except StopIteration: + return value + + +def has_known_bases(klass, context=None): + """Return true if all base classes of a class could be inferred.""" + try: + return klass._all_bases_known + except AttributeError: + pass + for base in klass.bases: + result = safe_infer(base, context=context) + # TODO: check for A->B->A->B pattern in class structure too? + if (not isinstance(result, astroid.ClassDef) or + result is klass or + not has_known_bases(result, context=context)): + klass._all_bases_known = False + return False + klass._all_bases_known = True + return True diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py index d813e1f..af87776 100644 --- a/pylint/checkers/variables.py +++ b/pylint/checkers/variables.py @@ -21,7 +21,6 @@ import re from copy import copy import astroid -from astroid import helpers from astroid import modutils from pylint.interfaces import IAstroidChecker, INFERENCE, INFERENCE_FAILURE, HIGH @@ -32,7 +31,8 @@ from pylint.checkers.utils import ( is_defined_before, is_error, is_func_default, is_func_decorator, assign_parent, check_messages, is_inside_except, clobber_in_except, get_all_elements, has_known_bases, node_ignores_exception, - is_inside_abstract_class, is_comprehension, is_iterable) + is_inside_abstract_class, is_comprehension, is_iterable, + safe_infer, has_known_bases) import six SPECIAL_OBJ = re.compile("^_{2}[a-z]+_{2}$") @@ -1034,7 +1034,7 @@ builtins. Remember that you should avoid to define new builtins when possible.' targets = node.targets[0].itered() try: - infered = helpers.safe_infer(node.value) + infered = safe_infer(node.value) if infered is not None: self._check_unpacking(infered, node, targets) except astroid.InferenceError: -- cgit v1.2.1