summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrett Cannon <brett@python.org>2014-10-31 15:06:36 -0400
committerBrett Cannon <brett@python.org>2014-10-31 15:06:36 -0400
commit4d52dadee16ce7ceb252ce13249ac390044c8239 (patch)
tree48a860d6108feaef36995497619e25917d6784da
parent2baad676aba7e4a47cc1f66c1b5a66b85a4af873 (diff)
downloadpylint-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--ChangeLog10
-rw-r--r--checkers/python3.py25
-rw-r--r--test/input/func_noerror_crash_122793.py2
-rw-r--r--test/unittest_checker_python3.py44
4 files changed, 78 insertions, 3 deletions
diff --git a/ChangeLog b/ChangeLog
index 2d7eef2..20a8c0d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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()