# HG changeset patch
# User Martin Pool <mbp@google.com>
# Date 1361917051 -3600
# Tue Feb 26 23:17:31 2013 +0100
# Node ID 4af14e854aebda909a85cfc47d58732e3fe96ff6
# Parent 9ee22d817eda42874cbb99e995e1cf4a66a3d0d8
give [deprecated-lambda] when a map/filter of a lambda could be a comprehension. Closes #120657
# User Martin Pool <mbp@google.com>
# Date 1361917051 -3600
# Tue Feb 26 23:17:31 2013 +0100
# Node ID 4af14e854aebda909a85cfc47d58732e3fe96ff6
# Parent 9ee22d817eda42874cbb99e995e1cf4a66a3d0d8
give [deprecated-lambda] when a map/filter of a lambda could be a comprehension. Closes #120657
@@ -22,10 +22,13 @@
1 and useless suppression pragmas. (patch by Torsten Marek) 2 3 * #112728: add warning E0604 for non-string objects in __all__ 4 (patch by Torsten Marek) 5 6 + * #120657: add warning W0110/deprecated-lambda when a map/filter 7 + of a lambda could be a comprehension (patch by Martin Pool) 8 + 9 * #113231: logging checker now looks at instances of Logger classes 10 in addition to the base logging module. (patch by Mike Bryant) 11 12 * #111799: don't warn about octal escape sequence, warn about \o 13 which is not octal in Python (patch by Martin Pool)
@@ -21,11 +21,16 @@
14 from logilab.astng import are_exclusive 15 16 from pylint.interfaces import IASTNGChecker 17 from pylint.reporters import diff_string 18 from pylint.checkers import BaseChecker, EmptyReport 19 -from pylint.checkers.utils import check_messages, clobber_in_except, is_inside_except 20 +from pylint.checkers.utils import ( 21 + check_messages, 22 + clobber_in_except, 23 + is_inside_except, 24 + safe_infer, 25 + ) 26 27 28 import re 29 30 # regex for class/function/variable/constant name
@@ -796,20 +801,51 @@
31 class PassChecker(_BasicChecker): 32 """check is the pass statement is really necessary""" 33 msgs = {'W0107': ('Unnecessary pass statement', 34 'unnecessary-pass', 35 'Used when a "pass" statement that can be avoided is ' 36 - 'encountered.)'), 37 + 'encountered.'), 38 } 39 40 def visit_pass(self, node): 41 if len(node.parent.child_sequence(node)) > 1: 42 self.add_message('W0107', node=node) 43 44 45 +class LambdaForComprehensionChecker(_BasicChecker): 46 + """check for using a lambda where a comprehension would do. 47 + 48 + See <http://www.artima.com/weblogs/viewpost.jsp?thread=98196> 49 + where GvR says comprehensions would be clearer. 50 + """ 51 + 52 + msgs = {'W0110': ('map/filter on lambda could be replaced by comprehension', 53 + 'deprecated-lambda', 54 + 'Used when a lambda is the first argument to "map" or ' 55 + '"filter". It could be clearer as a list ' 56 + 'comprehension or generator expression.'), 57 + } 58 + 59 + @check_messages('W0110') 60 + def visit_callfunc(self, node): 61 + """visit a CallFunc node, check if map or filter are called with a 62 + lambda 63 + """ 64 + if not node.args: 65 + return 66 + if not isinstance(node.args[0], astng.Lambda): 67 + return 68 + infered = safe_infer(node.func) 69 + if (infered 70 + and infered.parent.name == '__builtin__' 71 + and infered.name in ['map', 'filter']): 72 + self.add_message('W0110', node=node) 73 + 74 + 75 def register(linter): 76 """required method to auto register this checker""" 77 linter.register_checker(BasicErrorChecker(linter)) 78 linter.register_checker(BasicChecker(linter)) 79 linter.register_checker(NameChecker(linter)) 80 linter.register_checker(DocStringChecker(linter)) 81 linter.register_checker(PassChecker(linter)) 82 + linter.register_checker(LambdaForComprehensionChecker(linter))
@@ -0,0 +1,22 @@
83 +# pylint: disable=missing-docstring,bad-builtin,invalid-name 84 +__revision__ = "$Id$" 85 + 86 +# Don't do this, use a comprehension instead. 87 +assert map(lambda x: x*2, [1, 2, 3]) == [2, 4, 6] 88 + 89 +assert filter(lambda x: x != 1, [1, 2, 3]) == [2, 3] 90 + 91 +# It's still ok to use map and filter with anything but an inline lambda. 92 +double = lambda x: x * 2 93 +assert map(double, [1, 2, 3]) == [2, 4, 6] 94 + 95 +# It's also ok to pass lambdas to other functions. 96 +assert reduce(lambda x, y: x * y, [1, 2, 3, 4]) == 24 97 + 98 +# Or to a undefined function or one with varargs 99 +def f(*a): 100 + return len(a) 101 + 102 +f(lambda x, y: x + y, [1, 2, 3]) 103 + 104 +undefined_function(lambda: 2) # pylint: disable=undefined-variable
@@ -0,0 +1,2 @@
105 +W: 5: map/filter on lambda could be replaced by comprehension 106 +W: 7: map/filter on lambda could be replaced by comprehension