22Should emit:
33B023 - on lines 12, 13, 16, 28, 29, 30, 31, 40, 42, 50, 51, 52, 53, 61, 68.
44"""
5+ from functools import reduce
56
67functions = []
78z = 0
8-
99for x in range (3 ):
1010 y = x + 1
1111 # Subject to late-binding problems
@@ -25,10 +25,10 @@ def f_ok_1(x):
2525
2626
2727def check_inside_functions_too ():
28- ls = [lambda : x for x in range (2 )]
29- st = {lambda : x for x in range (2 )}
30- gn = (lambda : x for x in range (2 ))
31- dt = {x : lambda : x for x in range (2 )}
28+ ls = [lambda : x for x in range (2 )] # error
29+ st = {lambda : x for x in range (2 )} # error
30+ gn = (lambda : x for x in range (2 )) # error
31+ dt = {x : lambda : x for x in range (2 )} # error
3232
3333
3434async def pointless_async_iterable ():
@@ -37,28 +37,28 @@ async def pointless_async_iterable():
3737
3838async def container_for_problems ():
3939 async for x in pointless_async_iterable ():
40- functions .append (lambda : x )
40+ functions .append (lambda : x ) # error
4141
42- [lambda : x async for x in pointless_async_iterable ()]
42+ [lambda : x async for x in pointless_async_iterable ()] # error
4343
4444
4545a = 10
4646b = 0
4747while True :
4848 a = a_ = a - 1
4949 b += 1
50- functions .append (lambda : a )
51- functions .append (lambda : a_ )
52- functions .append (lambda : b )
53- functions .append (lambda : c ) # not a name error because of late binding!
50+ functions .append (lambda : a ) # error
51+ functions .append (lambda : a_ ) # error
52+ functions .append (lambda : b ) # error
53+ functions .append (lambda : c ) # error, but not a name error due to late binding
5454 c : bool = a > 3
5555 if not c :
5656 break
5757
5858# Nested loops should not duplicate reports
5959for j in range (2 ):
6060 for k in range (3 ):
61- lambda : j * k
61+ lambda : j * k # error
6262
6363
6464for j , k , l in [(1 , 2 , 3 )]:
@@ -76,3 +76,87 @@ def f():
7676
7777 def explicit_capture (captured = var ):
7878 return captured
79+
80+
81+ # `query` is defined in the function, so also defining it in the loop should be OK.
82+ for name in ["a" , "b" ]:
83+ query = name
84+
85+ def myfunc (x ):
86+ query = x
87+ query_post = x
88+ _ = query
89+ _ = query_post
90+
91+ query_post = name # in case iteration order matters
92+
93+
94+ # Bug here because two dict comprehensions reference `name`, one of which is inside
95+ # the lambda. This should be totally fine, of course.
96+ _ = {
97+ k : v
98+ for k , v in reduce (
99+ lambda data , event : merge_mappings (
100+ [data , {name : f (caches , data , event ) for name , f in xx }]
101+ ),
102+ events ,
103+ {name : getattr (group , name ) for name in yy },
104+ ).items ()
105+ if k in backfill_fields
106+ }
107+
108+
109+ # OK to define lambdas if they're immediately consumed, typically as the `key=`
110+ # argument or in a consumed `filter()` (even if a comprehension is better style)
111+ for x in range (2 ):
112+ # It's not a complete get-out-of-linting-free construct - these should fail:
113+ min ([None , lambda : x ], key = repr )
114+ sorted ([None , lambda : x ], key = repr )
115+ any (filter (bool , [None , lambda : x ]))
116+ list (filter (bool , [None , lambda : x ]))
117+ all (reduce (bool , [None , lambda : x ]))
118+
119+ # But all these ones should be OK:
120+ min (range (3 ), key = lambda y : x * y )
121+ max (range (3 ), key = lambda y : x * y )
122+ sorted (range (3 ), key = lambda y : x * y )
123+
124+ any (filter (lambda y : x < y , range (3 )))
125+ all (filter (lambda y : x < y , range (3 )))
126+ set (filter (lambda y : x < y , range (3 )))
127+ list (filter (lambda y : x < y , range (3 )))
128+ tuple (filter (lambda y : x < y , range (3 )))
129+ sorted (filter (lambda y : x < y , range (3 )))
130+ frozenset (filter (lambda y : x < y , range (3 )))
131+
132+ any (reduce (lambda y : x | y , range (3 )))
133+ all (reduce (lambda y : x | y , range (3 )))
134+ set (reduce (lambda y : x | y , range (3 )))
135+ list (reduce (lambda y : x | y , range (3 )))
136+ tuple (reduce (lambda y : x | y , range (3 )))
137+ sorted (reduce (lambda y : x | y , range (3 )))
138+ frozenset (reduce (lambda y : x | y , range (3 )))
139+
140+ import functools
141+
142+ any (functools .reduce (lambda y : x | y , range (3 )))
143+ all (functools .reduce (lambda y : x | y , range (3 )))
144+ set (functools .reduce (lambda y : x | y , range (3 )))
145+ list (functools .reduce (lambda y : x | y , range (3 )))
146+ tuple (functools .reduce (lambda y : x | y , range (3 )))
147+ sorted (functools .reduce (lambda y : x | y , range (3 )))
148+ frozenset (functools .reduce (lambda y : x | y , range (3 )))
149+
150+ # OK because the lambda which references a loop variable is defined in a `return`
151+ # statement, and after we return the loop variable can't be redefined.
152+ # In principle we could do something fancy with `break`, but it's not worth it.
153+ def iter_f (names ):
154+ for name in names :
155+ if exists (name ):
156+ return lambda : name if exists (name ) else None
157+
158+ if foo (name ):
159+ return [lambda : name ] # false alarm, should be fixed?
160+
161+ if False :
162+ return [lambda : i for i in range (3 )] # error
0 commit comments