summaryrefslogtreecommitdiff
path: root/pylint/checkers/python3.py
diff options
context:
space:
mode:
authorRoy Williams <roy.williams.iii@gmail.com>2016-12-05 10:37:00 -0800
committerGitHub <noreply@github.com>2016-12-05 10:37:00 -0800
commit1c29c5156e954ec43be210b9521b92f987bf48fa (patch)
tree99c84527e24e6aa5aab07d96d49ccd42c1329401 /pylint/checkers/python3.py
parent230bebb08fe92b7d537ba2e64f8fcb045361d210 (diff)
downloadpylint-git-1c29c5156e954ec43be210b9521b92f987bf48fa.tar.gz
Add a Python 3 checker for finding deprecated calls to `str.translate`. (#1188)
* Add a Python 3 checker for finding deprecated calls to `str.translate`. This checker could possibly have some false positives, but after checking our codebase and several other large codebases using this implementation, I did not find any false positives. * Add filter for types proven not to be strings
Diffstat (limited to 'pylint/checkers/python3.py')
-rw-r--r--pylint/checkers/python3.py51
1 files changed, 49 insertions, 2 deletions
diff --git a/pylint/checkers/python3.py b/pylint/checkers/python3.py
index a82de4d33..2b91e82d6 100644
--- a/pylint/checkers/python3.py
+++ b/pylint/checkers/python3.py
@@ -389,6 +389,11 @@ class Python3Checker(checkers.BaseChecker):
'deprecated-string-function',
'Used when accessing a string function that has been deprecated in Python 3.',
{'maxversion': (3, 0)}),
+ 'W1650': ('Using str.translate with deprecated deletechars parameters',
+ 'deprecated-str-translate-call',
+ 'Used when using the deprecated deletechars parameters from str.translate. Use'
+ 're.sub to remove the desired characters ',
+ {'maxversion': (3, 0)}),
}
_bad_builtins = frozenset([
@@ -594,22 +599,64 @@ class Python3Checker(checkers.BaseChecker):
self.add_message('using-cmp-argument', node=node)
return
+ @staticmethod
+ def _is_constant_string_or_name(node):
+ return ((isinstance(node, astroid.Const) and isinstance(node.value, six.string_types)) or
+ isinstance(node, astroid.Name))
+
+ @staticmethod
+ def _is_none(node):
+ return isinstance(node, astroid.Const) and node.value is None
+
+ @staticmethod
+ def _has_only_n_positional_args(node, number_of_args):
+ return len(node.args) == number_of_args and all(node.args) and not node.keywords
+
+ @staticmethod
+ def _could_be_string(inferred_types):
+ 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
+
def visit_call(self, node):
self._check_cmp_argument(node)
if isinstance(node.func, astroid.Attribute):
+ inferred_types = set()
try:
for inferred_receiver in node.func.expr.infer():
+ inferred_types.add(type(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 node.func.attrname in ('encode', 'decode'):
- if len(node.args) >= 1 and node.args[0]:
+ if self._could_be_string(inferred_types):
+ if (node.func.attrname in ('encode', 'decode') and
+ len(node.args) >= 1 and node.args[0]):
first_arg = node.args[0]
self._validate_encoding(first_arg, node)
+ if (node.func.attrname == 'translate' and
+ self._has_only_n_positional_args(node, 2) and
+ self._is_none(node.args[0]) and
+ self._is_constant_string_or_name(node.args[1])):
+ # The above statement looking for calls of the form:
+ #
+ # foo.translate(None, 'abc123')
+ #
+ # or
+ #
+ # foo.translate(None, some_variable)
+ #
+ # This check is somewhat broad and _may_ have some false positives, but
+ # 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)
return
if node.keywords:
return