summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrett Cannon <brett@python.org>2015-01-30 11:00:56 -0500
committerBrett Cannon <brett@python.org>2015-01-30 11:00:56 -0500
commit8cf7420e74d4dbedfaff0f2c3b02aebe24bc04c8 (patch)
tree9a8113d6e6664e13aaa90d1d95e324ce6fc92a7b
parentd179ac9d4ea83aad6c054e2049a83fd340912663 (diff)
downloadpylint-8cf7420e74d4dbedfaff0f2c3b02aebe24bc04c8.tar.gz
Check zip, filter, and range are used in iterating contexts
-rw-r--r--checkers/python3.py21
-rw-r--r--test/unittest_checker_python3.py78
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):