diff options
Diffstat (limited to 'checkers')
| -rw-r--r-- | checkers/base.py | 38 | ||||
| -rw-r--r-- | checkers/exceptions.py | 41 | ||||
| -rw-r--r-- | checkers/newstyle.py | 9 | ||||
| -rw-r--r-- | checkers/stdlib.py | 2 | ||||
| -rw-r--r-- | checkers/typecheck.py | 13 |
5 files changed, 58 insertions, 45 deletions
diff --git a/checkers/base.py b/checkers/base.py index 604685a2e..17e671339 100644 --- a/checkers/base.py +++ b/checkers/base.py @@ -179,7 +179,7 @@ def _determine_function_name_type(node): return 'attr' return 'method' -def decorated_with_abc(func): +def _decorated_with_abc(func): """ Determine if the `func` node is decorated with `abc` decorators (abstractmethod et co.) """ @@ -192,12 +192,12 @@ def decorated_with_abc(func): if infered and infered.qname() in ABC_METHODS: return True -def has_abstract_methods(node): +def _has_abstract_methods(node): """ Determine if the given `node` has abstract methods, defined with `abc` module. """ - return any(decorated_with_abc(meth) + return any(_decorated_with_abc(meth) for meth in node.methods()) def report_by_type_stats(sect, stats, old_stats): @@ -398,7 +398,7 @@ class BasicErrorChecker(_BasicChecker): return # __init__ was called metaclass = infered.metaclass() - abstract_methods = has_abstract_methods(infered) + abstract_methods = _has_abstract_methods(infered) if metaclass is None: # Python 3.4 has `abc.ABC`, which won't be detected # by ClassNode.metaclass() @@ -676,7 +676,13 @@ functions, methods variable names, max locals """ self.stats[node.is_method() and 'method' or 'function'] += 1 + self._check_dangerous_default(node) + + def _check_dangerous_default(self, node): # check for dangerous default values as arguments + is_iterable = lambda n: isinstance(n, (astroid.List, + astroid.Set, + astroid.Dict)) for default in node.args.defaults: try: value = next(default.infer()) @@ -685,24 +691,30 @@ functions, methods if (isinstance(value, astroid.Instance) and value.qname() in DEFAULT_ARGUMENT_SYMBOLS): - is_infered_builtin = isinstance( - value, - (astroid.List, astroid.Tuple, astroid.Set)) + if value is default: msg = DEFAULT_ARGUMENT_SYMBOLS[value.qname()] - elif type(value) is astroid.Instance or is_infered_builtin: - if isinstance(default, astroid.CallFunc): - # this argument is direct call to list() or dict() etc + elif type(value) is astroid.Instance or is_iterable(value): + # We are here in the following situation(s): + # * a dict/set/list/tuple call which wasn't inferred + # to a syntax node ({}, () etc.). This can happen + # when the arguments are invalid or unknown to + # the inference. + # * a variable from somewhere else, which turns out to be a list + # or a dict. + if is_iterable(default): + msg = value.pytype() + elif isinstance(default, astroid.CallFunc): msg = '%s() (%s)' % (value.name, value.qname()) else: - # this argument is a variable from somewhere else which turns - # out to be a list or dict msg = '%s (%s)' % (default.as_string(), value.qname()) else: # this argument is a name msg = '%s (%s)' % (default.as_string(), DEFAULT_ARGUMENT_SYMBOLS[value.qname()]) - self.add_message('dangerous-default-value', node=node, args=(msg,)) + self.add_message('dangerous-default-value', + node=node, + args=(msg, )) @check_messages('unreachable', 'lost-exception') def visit_return(self, node): diff --git a/checkers/exceptions.py b/checkers/exceptions.py index e8e5a5412..21c863d55 100644 --- a/checkers/exceptions.py +++ b/checkers/exceptions.py @@ -25,7 +25,7 @@ from pylint.checkers import BaseChecker from pylint.checkers.utils import ( is_empty, is_raising, check_messages, inherit_from_std_ex, - EXCEPTIONS_MODULE, has_known_bases) + EXCEPTIONS_MODULE, has_known_bases, safe_infer) from pylint.interfaces import IAstroidChecker, INFERENCE, INFERENCE_FAILURE def _annotated_unpack_infer(stmt, context=None): @@ -42,21 +42,14 @@ def _annotated_unpack_infer(stmt, context=None): # as well. if isinstance(stmt, (List, Tuple)): for elt in stmt.elts: - for infered_elt in unpack_infer(elt, context): - yield elt, infered_elt + inferred = safe_infer(elt) + if inferred and inferred is not YES: + yield elt, inferred return - # if infered is a final node, return it and stop - infered = next(stmt.infer(context)) - if infered is stmt: - yield stmt, infered - return - # else, infer recursivly, except YES object that should be returned as is for infered in stmt.infer(context): if infered is YES: - yield stmt, infered - else: - for inf_inf in unpack_infer(infered, context): - yield stmt, inf_inf + continue + yield stmt, infered PY3K = sys.version_info >= (3, 0) @@ -145,21 +138,17 @@ class ExceptionsChecker(BaseChecker): if node.exc is None: return if PY3K and node.cause: - try: - cause = next(node.cause.infer()) - except astroid.InferenceError: - pass - else: - if cause is YES: - return - if isinstance(cause, astroid.Const): - if cause.value is not None: - self.add_message('bad-exception-context', - node=node) - elif (not isinstance(cause, astroid.Class) and - not inherit_from_std_ex(cause)): + cause = safe_infer(node.cause) + if cause is YES or cause is None: + return + if isinstance(cause, astroid.Const): + if cause.value is not None: self.add_message('bad-exception-context', node=node) + elif (not isinstance(cause, astroid.Class) and + not inherit_from_std_ex(cause)): + self.add_message('bad-exception-context', + node=node) expr = node.exc if self._check_raise_value(node, expr): return diff --git a/checkers/newstyle.py b/checkers/newstyle.py index 1656806c1..f74e7f150 100644 --- a/checkers/newstyle.py +++ b/checkers/newstyle.py @@ -21,7 +21,11 @@ import astroid from pylint.interfaces import IAstroidChecker, INFERENCE, INFERENCE_FAILURE, HIGH from pylint.checkers import BaseChecker -from pylint.checkers.utils import check_messages, has_known_bases +from pylint.checkers.utils import ( + check_messages, + has_known_bases, + node_frame_class, +) MSGS = { 'E1001': ('Use of __slots__ on an old style class', @@ -112,6 +116,9 @@ class NewStyleConflictChecker(BaseChecker): return klass = node.parent.frame() for stmt in node.nodes_of_class(astroid.CallFunc): + if node_frame_class(stmt) != node_frame_class(node): + # Don't look down in other scopes. + continue expr = stmt.func if not isinstance(expr, astroid.Getattr): continue diff --git a/checkers/stdlib.py b/checkers/stdlib.py index 54f8c1aee..aafb97041 100644 --- a/checkers/stdlib.py +++ b/checkers/stdlib.py @@ -40,13 +40,13 @@ def _check_mode_str(mode): # check syntax modes = set(mode) _mode = "rwatb+U" + creating = False if six.PY3: _mode += "x" creating = "x" in modes if modes - set(_mode) or len(mode) > len(modes): return False # check logic - creating = False reading = "r" in modes writing = "w" in modes appending = "a" in modes diff --git a/checkers/typecheck.py b/checkers/typecheck.py index 10b9f8669..9f074ae05 100644 --- a/checkers/typecheck.py +++ b/checkers/typecheck.py @@ -344,11 +344,16 @@ accessed. Python regular expressions are accepted.'} if not isinstance(attr, astroid.Function): continue - # Decorated, see if it is decorated with a property + # Decorated, see if it is decorated with a property. + # Also, check the returns and see if they are callable. if decorated_with_property(attr): - self.add_message('not-callable', node=node, - args=node.func.as_string()) - break + if all(return_node.callable() + for return_node in attr.infer_call_result(node)): + continue + else: + self.add_message('not-callable', node=node, + args=node.func.as_string()) + break @check_messages(*(list(MSGS.keys()))) def visit_callfunc(self, node): |
