diff options
author | Brett Cannon <brett@python.org> | 2015-01-30 11:00:56 -0500 |
---|---|---|
committer | Brett Cannon <brett@python.org> | 2015-01-30 11:00:56 -0500 |
commit | 8cf7420e74d4dbedfaff0f2c3b02aebe24bc04c8 (patch) | |
tree | 9a8113d6e6664e13aaa90d1d95e324ce6fc92a7b | |
parent | d179ac9d4ea83aad6c054e2049a83fd340912663 (diff) | |
download | pylint-8cf7420e74d4dbedfaff0f2c3b02aebe24bc04c8.tar.gz |
Check zip, filter, and range are used in iterating contexts
-rw-r--r-- | checkers/python3.py | 21 | ||||
-rw-r--r-- | test/unittest_checker_python3.py | 78 |
2 files changed, 69 insertions, 30 deletions
diff --git a/checkers/python3.py b/checkers/python3.py index 6c582ca..d8d5bf3 100644 --- a/checkers/python3.py +++ b/checkers/python3.py @@ -301,6 +301,21 @@ class Python3Checker(checkers.BaseChecker): 'Used when the map built-in is referenced in a non-iterating ' 'context (returns an iterator in Python 3)', {'maxversion': (3, 0)}), + 'W1635': ('zip built-in referenced when not iterating', + 'zip-builtin-not-iterating', + 'Used when the zip built-in is referenced in a non-iterating ' + 'context (returns an iterator in Python 3)', + {'maxversion': (3, 0)}), + 'W1636': ('range built-in referenced when not iterating', + 'range-builtin-not-iterating', + 'Used when the range built-in is referenced in a non-iterating ' + 'context (returns an iterator in Python 3)', + {'maxversion': (3, 0)}), + 'W1637': ('filter built-in referenced when not iterating', + 'filter-builtin-not-iterating', + 'Used when the filter built-in is referenced in a non-iterating ' + 'context (returns an iterator in Python 3)', + {'maxversion': (3, 0)}), } _bad_builtins = frozenset([ @@ -417,8 +432,10 @@ class Python3Checker(checkers.BaseChecker): elif isinstance(node.func, astroid.Name): found_node = node.func.lookup(node.func.name)[0] if _is_builtin(found_node): - if node.func.name == 'map' and not _in_iterating_context(node): - self.add_message('map-builtin-not-iterating', node=node) + if node.func.name in ('filter', 'map', 'range', 'zip'): + if not _in_iterating_context(node): + checker = '{}-builtin-not-iterating'.format(node.func.name) + self.add_message(checker, node=node) @utils.check_messages('indexing-exception') diff --git a/test/unittest_checker_python3.py b/test/unittest_checker_python3.py index 4a7c749..a8e5a4b 100644 --- a/test/unittest_checker_python3.py +++ b/test/unittest_checker_python3.py @@ -62,64 +62,86 @@ class Python3CheckerTest(testutils.CheckerTestCase): for builtin in builtins: self.check_bad_builtin(builtin) - @python2_only - def test_returned_iterator(self): - module = test_utils.build_module("for x in map(None, [1]): pass") + def iterating_context_test(self, fxn): + """Helper for verifying a function isn't used as an iterator.""" + checker = '{}-builtin-not-iterating'.format(fxn) + code = "for x in {}(None, [1]): pass".format(fxn) + module = test_utils.build_module(code) with self.assertNoMessages(): self.walk(module) node = test_utils.extract_node(""" for x in (y( - map(None, [1]) #@ + {}(None, [1]) #@ )): pass - """) - message = testutils.Message('map-builtin-not-iterating', node=node) + """.format(fxn)) + message = testutils.Message(checker, node=node) with self.assertAddsMessages(message): self.checker.visit_callfunc(node) - module = test_utils.build_module("x = (x for x in map(None, [1]))") + code = "x = (x for x in {}(None, [1]))".format(fxn) + module = test_utils.build_module(code) with self.assertNoMessages(): self.walk(module) - module = test_utils.build_module("x = [x for x in map(None, [1])]") + code = "x = [x for x in {}(None, [1])]".format(fxn) + module = test_utils.build_module(code) with self.assertNoMessages(): self.walk(module) node = test_utils.extract_node(""" list( - map(None, x) #@ + {}(None, x) #@ for x in [1] ) - """) + """.format(fxn)) # For some reason extract_node won't grab the map() call. assert isinstance(node, astroid.GenExpr) node = node.elt - message = testutils.Message('map-builtin-not-iterating', node=node) + message = testutils.Message(checker, node=node) with self.assertAddsMessages(message): self.checker.visit_callfunc(node) node = test_utils.extract_node(""" [ - map(None, x) #@ + {}(None, x) #@ for x in [[1]]] - """) + """.format(fxn)) # For some reason extract_node won't grab the map() call. node = node.elt - message = testutils.Message('map-builtin-not-iterating', node=node) + message = testutils.Message(checker, node=node) with self.assertAddsMessages(message): self.checker.visit_callfunc(node) - module = test_utils.build_module("x = list(map(None, [1]))") + module = test_utils.build_module("x = list({}(None, [1]))".format(fxn)) with self.assertNoMessages(): self.walk(module) node = test_utils.extract_node(""" y( - map(None, [1]) #@ + {}(None, [1]) #@ ) - """) - message = testutils.Message('map-builtin-not-iterating', node=node) + """.format(fxn)) + message = testutils.Message(checker, node=node) with self.assertAddsMessages(message): self.checker.visit_callfunc(node) - module = test_utils.build_module("x = ''.join(map(None, [1]))") + code = "x = ''.join({}(None, [1]))".format(fxn) + module = test_utils.build_module(code) with self.assertNoMessages(): self.walk(module) - def _test_defined_method(self, method, warning): + @python2_only + def test_map_in_iterating_context(self): + self.iterating_context_test('map') + + @python2_only + def test_zip_in_iterating_context(self): + self.iterating_context_test('zip') + + @python2_only + def test_range_in_iterating_context(self): + self.iterating_context_test('range') + + @python2_only + def test_filter_in_iterating_context(self): + self.iterating_context_test('filter') + + def defined_method_test(self, method, warning): + """Helper for verifying that a certain method is not defined.""" node = test_utils.extract_node(""" class Foo(object): def __{0}__(self, other): #@ @@ -129,28 +151,28 @@ class Python3CheckerTest(testutils.CheckerTestCase): self.checker.visit_function(node) def test_delslice_method(self): - self._test_defined_method('delslice', 'delslice-method') + self.defined_method_test('delslice', 'delslice-method') def test_getslice_method(self): - self._test_defined_method('getslice', 'getslice-method') + self.defined_method_test('getslice', 'getslice-method') def test_setslice_method(self): - self._test_defined_method('setslice', 'setslice-method') + self.defined_method_test('setslice', 'setslice-method') def test_coerce_method(self): - self._test_defined_method('coerce', 'coerce-method') + self.defined_method_test('coerce', 'coerce-method') def test_oct_method(self): - self._test_defined_method('oct', 'oct-method') + self.defined_method_test('oct', 'oct-method') def test_hex_method(self): - self._test_defined_method('hex', 'hex-method') + self.defined_method_test('hex', 'hex-method') def test_nonzero_method(self): - self._test_defined_method('nonzero', 'nonzero-method') + self.defined_method_test('nonzero', 'nonzero-method') def test_cmp_method(self): - self._test_defined_method('cmp', 'cmp-method') + self.defined_method_test('cmp', 'cmp-method') @python2_only def test_print_statement(self): |