diff options
author | Roy Williams <roy.williams.iii@gmail.com> | 2016-12-05 10:37:00 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-12-05 10:37:00 -0800 |
commit | 1c29c5156e954ec43be210b9521b92f987bf48fa (patch) | |
tree | 99c84527e24e6aa5aab07d96d49ccd42c1329401 /pylint/checkers/python3.py | |
parent | 230bebb08fe92b7d537ba2e64f8fcb045361d210 (diff) | |
download | pylint-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.py | 51 |
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 |