summaryrefslogtreecommitdiff
path: root/pylint
diff options
context:
space:
mode:
authorRoy Williams <roy.williams.iii@gmail.com>2016-12-05 15:06:45 -0800
committerGitHub <noreply@github.com>2016-12-05 15:06:45 -0800
commit38c652c17ad76e15a9da2f17f11f632e1027a76d (patch)
tree12b0e50d71623b189d11c3eea699320f77046166 /pylint
parent1c29c5156e954ec43be210b9521b92f987bf48fa (diff)
downloadpylint-git-38c652c17ad76e15a9da2f17f11f632e1027a76d.tar.gz
Add confidence to error messages about `str.translate` and `str.encode/decode` (#1197)
Diffstat (limited to 'pylint')
-rw-r--r--pylint/checkers/python3.py22
-rw-r--r--pylint/test/unittest_checker_python3.py19
-rw-r--r--pylint/testutils.py20
3 files changed, 45 insertions, 16 deletions
diff --git a/pylint/checkers/python3.py b/pylint/checkers/python3.py
index 2b91e82d6..e2c6f12e2 100644
--- a/pylint/checkers/python3.py
+++ b/pylint/checkers/python3.py
@@ -16,6 +16,7 @@ import astroid
from astroid import bases
from pylint import checkers, interfaces
+from pylint.interfaces import INFERENCE_FAILURE, INFERENCE
from pylint.utils import WarningScope
from pylint.checkers import utils
@@ -614,12 +615,14 @@ class Python3Checker(checkers.BaseChecker):
@staticmethod
def _could_be_string(inferred_types):
+ confidence = INFERENCE if inferred_types else INFERENCE_FAILURE
for inferred_type in inferred_types:
- if not (inferred_type == astroid.Uninferable or (
- isinstance(inferred_type, astroid.Const) and
- isinstance(inferred_type.value, six.string_types))):
- return False
- return True
+ if inferred_type is astroid.Uninferable:
+ confidence = INFERENCE_FAILURE
+ elif not (isinstance(inferred_type, astroid.Const) and
+ isinstance(inferred_type.value, six.string_types)):
+ return None
+ return confidence
def visit_call(self, node):
self._check_cmp_argument(node)
@@ -628,14 +631,15 @@ class Python3Checker(checkers.BaseChecker):
inferred_types = set()
try:
for inferred_receiver in node.func.expr.infer():
- inferred_types.add(type(inferred_receiver))
+ inferred_types.add(inferred_receiver)
if isinstance(inferred_receiver, astroid.Module):
self._warn_if_deprecated(node, inferred_receiver.name,
{node.func.attrname})
except astroid.InferenceError:
pass
if node.args:
- if self._could_be_string(inferred_types):
+ is_str_confidence = self._could_be_string(inferred_types)
+ if is_str_confidence:
if (node.func.attrname in ('encode', 'decode') and
len(node.args) >= 1 and node.args[0]):
first_arg = node.args[0]
@@ -656,7 +660,9 @@ class Python3Checker(checkers.BaseChecker):
# after checking several large codebases it did not have any false
# positives while finding several real issues. This call pattern seems
# rare enough that the trade off is worth it.
- self.add_message('deprecated-str-translate-call', node=node)
+ self.add_message('deprecated-str-translate-call',
+ node=node,
+ confidence=is_str_confidence)
return
if node.keywords:
return
diff --git a/pylint/test/unittest_checker_python3.py b/pylint/test/unittest_checker_python3.py
index 28a6d8052..165d9dad0 100644
--- a/pylint/test/unittest_checker_python3.py
+++ b/pylint/test/unittest_checker_python3.py
@@ -15,6 +15,7 @@ import astroid
from pylint import testutils
from pylint.checkers import python3 as checker
+from pylint.interfaces import INFERENCE_FAILURE, INFERENCE
def python2_only(test):
@@ -652,16 +653,30 @@ class Python3CheckerTest(testutils.CheckerTestCase):
node = astroid.extract_node('''
foobar.translate(None, 'abc123') #@
''')
- message = testutils.Message('deprecated-str-translate-call', node=node)
+ message = testutils.Message('deprecated-str-translate-call', node=node,
+ confidence=INFERENCE_FAILURE)
with self.assertAddsMessages(message):
self.checker.visit_call(node)
@python2_only
def test_bad_str_translate_call_variable(self):
node = astroid.extract_node('''
+ def raz(foobar):
+ foobar.translate(None, 'hello') #@
+ ''')
+ message = testutils.Message('deprecated-str-translate-call', node=node,
+ confidence=INFERENCE_FAILURE)
+ with self.assertAddsMessages(message):
+ self.checker.visit_call(node)
+
+ @python2_only
+ def test_bad_str_translate_call_infer_str(self):
+ node = astroid.extract_node('''
+ foobar = "hello world"
foobar.translate(None, foobar) #@
''')
- message = testutils.Message('deprecated-str-translate-call', node=node)
+ message = testutils.Message('deprecated-str-translate-call', node=node,
+ confidence=INFERENCE)
with self.assertAddsMessages(message):
self.checker.visit_call(node)
diff --git a/pylint/testutils.py b/pylint/testutils.py
index 892ca7c73..c6bd0a7a2 100644
--- a/pylint/testutils.py
+++ b/pylint/testutils.py
@@ -132,9 +132,18 @@ class TestReporter(BaseReporter):
class Message(collections.namedtuple('Message',
- ['msg_id', 'line', 'node', 'args'])):
- def __new__(cls, msg_id, line=None, node=None, args=None):
- return tuple.__new__(cls, (msg_id, line, node, args))
+ ['msg_id', 'line', 'node', 'args', 'confidence'])):
+ def __new__(cls, msg_id, line=None, node=None, args=None, confidence=None):
+ return tuple.__new__(cls, (msg_id, line, node, args, confidence))
+
+ def __eq__(self, other):
+ if isinstance(other, Message):
+ if self.confidence and other.confidence:
+ return super(Message, self).__eq__(other)
+ return self[:-1] == other[:-1]
+ return NotImplemented # pragma: no cover
+
+ __hash__ = None
class UnittestLinter(object):
@@ -151,9 +160,8 @@ class UnittestLinter(object):
finally:
self._messages = []
- def add_message(self, msg_id, line=None, node=None, args=None,
- confidence=None):
- self._messages.append(Message(msg_id, line, node, args))
+ def add_message(self, msg_id, line=None, node=None, args=None, confidence=None):
+ self._messages.append(Message(msg_id, line, node, args, confidence))
def is_message_enabled(self, *unused_args, **unused_kwargs):
return True