summaryrefslogtreecommitdiff
path: root/checkers
diff options
context:
space:
mode:
Diffstat (limited to 'checkers')
-rw-r--r--checkers/base.py38
-rw-r--r--checkers/exceptions.py41
-rw-r--r--checkers/newstyle.py9
-rw-r--r--checkers/stdlib.py2
-rw-r--r--checkers/typecheck.py13
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):