diff options
author | Brett Cannon <brett@python.org> | 2014-10-31 15:06:36 -0400 |
---|---|---|
committer | Brett Cannon <brett@python.org> | 2014-10-31 15:06:36 -0400 |
commit | 4d52dadee16ce7ceb252ce13249ac390044c8239 (patch) | |
tree | 48a860d6108feaef36995497619e25917d6784da | |
parent | 2baad676aba7e4a47cc1f66c1b5a66b85a4af873 (diff) | |
download | pylint-4d52dadee16ce7ceb252ce13249ac390044c8239.tar.gz |
Warn when calling dict.iter*(), dict.view*() or .next() on any object.
These methods either do not exist in Python 3 or are never used.
-rw-r--r-- | ChangeLog | 10 | ||||
-rw-r--r-- | checkers/python3.py | 25 | ||||
-rw-r--r-- | test/input/func_noerror_crash_122793.py | 2 | ||||
-rw-r--r-- | test/unittest_checker_python3.py | 44 |
4 files changed, 78 insertions, 3 deletions
@@ -142,15 +142,21 @@ ChangeLog for Pylint * 'too-many-lines' disable pragma can be located on any line, not only the first. Closes issue #321. - * Warn in Python 2.7 when an import statement is found without a + * Warn in Python 2 when an import statement is found without a corresponding `from __future__ import absolute_import`. - * Warn in Python 2.7 when a non-floor division operation is found without + * Warn in Python 2 when a non-floor division operation is found without a corresponding `from __future__ import division`. * Add a new option, 'exclude-protected', for excluding members from the protected-access warning. Closes issue #48. + * Warn in Python 2 when using dict.iter*(), dict.view*(); none of these + methods are available in Python 3. + + * Warn in Python 2 when calling an object's next() method; Python 3 uses + __next__() instead. + 2014-07-26 -- 1.3.0 diff --git a/checkers/python3.py b/checkers/python3.py index 5a3d245..d055638 100644 --- a/checkers/python3.py +++ b/checkers/python3.py @@ -130,6 +130,21 @@ class Python3Checker(checkers.BaseChecker): '``from __future__ import division``' '(Python 3 returns a float for int division unconditionally)', {'maxversion': (3, 0)}), + 'W1620': ('Calling a dict.iter*() method', + 'dict-iter-method', + 'Used for calls to dict.iterkeys(), itervalues() or iteritems() ' + '(Python 3 lacks these methods)', + {'maxversion': (3, 0)}), + 'W1621': ('Calling a dict.view*() method', + 'dict-view-method', + 'Used for calls to dict.viewkeys(), viewvalues() or viewitems() ' + '(Python 3 lacks these methods)', + {'maxversion': (3, 0)}), + 'W1622': ('Called a next() method on an object', + 'next-method-called', + "Used when an object's next() method is called " + '(Python 3 uses the next() built-in function)', + {'maxversion': (3, 0)}), } _missing_builtins = frozenset([ @@ -204,6 +219,16 @@ class Python3Checker(checkers.BaseChecker): else: self.add_message('old-division', node=node) + def visit_callfunc(self, node): + if hasattr(node.func, 'attrname'): + if not any([node.args, node.starargs, node.kwargs]): + if node.func.attrname in ('iterkeys', 'itervalues', 'iteritems'): + self.add_message('dict-iter-method', node=node) + elif node.func.attrname in ('viewkeys', 'viewvalues', 'viewitems'): + self.add_message('dict-view-method', node=node) + elif node.func.attrname == 'next': + self.add_message('next-method-called', node=node) + def register(linter): linter.register_checker(Python3Checker(linter)) diff --git a/test/input/func_noerror_crash_122793.py b/test/input/func_noerror_crash_122793.py index 1d14d8e..2bb3d2e 100644 --- a/test/input/func_noerror_crash_122793.py +++ b/test/input/func_noerror_crash_122793.py @@ -6,4 +6,4 @@ def gen(): yield GEN = gen() -GEN.next() +next(GEN) diff --git a/test/unittest_checker_python3.py b/test/unittest_checker_python3.py index 03ad66b..4ce485f 100644 --- a/test/unittest_checker_python3.py +++ b/test/unittest_checker_python3.py @@ -173,6 +173,50 @@ class Python3CheckerTest(testutils.CheckerTestCase): for node in (left_node, right_node): self.checker.visit_binop(node) + def test_dict_iter_method(self): + for meth in ('keys', 'values', 'items'): + node = test_utils.extract_node('x.iter%s() #@' % meth) + message = testutils.Message('dict-iter-method', node=node) + with self.assertAddsMessages(message): + self.checker.visit_callfunc(node) + + def test_dict_not_iter_method(self): + arg_node = test_utils.extract_node('x.iterkeys(x) #@') + stararg_node = test_utils.extract_node('x.iterkeys(*x) #@') + kwarg_node = test_utils.extract_node('x.iterkeys(y=x) #@') + with self.assertNoMessages(): + for node in (arg_node, stararg_node, kwarg_node): + self.checker.visit_callfunc(node) + + def test_dict_view_method(self): + for meth in ('keys', 'values', 'items'): + node = test_utils.extract_node('x.view%s() #@' % meth) + message = testutils.Message('dict-view-method', node=node) + with self.assertAddsMessages(message): + self.checker.visit_callfunc(node) + + def test_dict_not_view_method(self): + arg_node = test_utils.extract_node('x.viewkeys(x) #@') + stararg_node = test_utils.extract_node('x.viewkeys(*x) #@') + kwarg_node = test_utils.extract_node('x.viewkeys(y=x) #@') + with self.assertNoMessages(): + for node in (arg_node, stararg_node, kwarg_node): + self.checker.visit_callfunc(node) + + def test_next_method(self): + node = test_utils.extract_node('x.next() #@') + message = testutils.Message('next-method-called', node=node) + with self.assertAddsMessages(message): + self.checker.visit_callfunc(node) + + def test_not_next_method(self): + arg_node = test_utils.extract_node('x.next(x) #@') + stararg_node = test_utils.extract_node('x.next(*x) #@') + kwarg_node = test_utils.extract_node('x.next(y=x) #@') + with self.assertNoMessages(): + for node in (arg_node, stararg_node, kwarg_node): + self.checker.visit_callfunc(node) + if __name__ == '__main__': unittest.main() |