10000 fixed issues · python-trio/flake8-async@e01300b · GitHub
[go: up one dir, main page]

Skip to content

Commit e01300b

Browse files
committed
fixed issues
1 parent fcc02d4 commit e01300b

File tree

3 files changed

+138
-76
lines changed

3 files changed

+138
-76
lines changed

flake8_trio.py

Lines changed: 39 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
"TRIO108": "{0} from async iterable with no guaranteed checkpoint since {1.name} on line {1.lineno}",
3030
"TRIO109": "Async function definition with a `timeout` parameter - use `trio.[fail/move_on]_[after/at]` instead",
3131
"TRIO110": "`while <condition>: await trio.sleep()` should be replaced by a `trio.Event`.",
32-
"TRIO302": "call to nursery.start/start_soon with resource from context manager opened on line {} something something nursery on line {}",
32+
"TRIO302": "variable {2}, from context manager on line {0}, passed to {3} from nursery opened on {1}, might get closed while in use",
3333
}
3434

3535

@@ -191,10 +191,16 @@ class VisitorMiscChecks(Flake8TrioVisitor):
191191
def __init__(self):
192192
super().__init__()
193193

194-
# variables only used for 101
194+
# 101
195195
self._yield_is_error = False
196196
self._safe_decorator = False
197+
198+
# 302
197199
self._context_manager_stack: List[Tuple[ast.expr, str, bool]] = []
200+
self._nursery_call_index: Optional[int] = None
201+
self._nursery_call_name: Optional[str] = None
202+
203+
self.defaults = self.get_state()
198204

199205
# ---- 100, 101, 302 ----
200206
def visit_With(self, node: Union[ast.With, ast.AsyncWith]):
@@ -244,8 +250,7 @@ def check_for_trio100(self, node: Union[ast.With, ast.AsyncWith]):
244250
# ---- 101 ----
245251
def visit_FunctionDef(self, node: Union[ast.FunctionDef, ast.AsyncFunctionDef]):
246252
outer = self.get_state()
247-
self._yield_is_error = False
248-
self._context_manager_stack = []
253+
self.set_state(self.defaults, copy=True)
249254

250255
# check for @<context_manager_name> and @<library>.<context_manager_name>
251256
if has_decorator(node.decorator_list, *context_manager_names):
@@ -260,6 +265,12 @@ def visit_AsyncFunctionDef(self, node: ast.AsyncFunctionDef):
260265
self.check_109(node)
261266
self.visit_FunctionDef(node)
262267

268+
def visit_Lambda(self, node: ast.Lambda):
269+
outer = self.get_state()
270+
self.set_state(self.defaults, copy=True)
271+
self.generic_visit(node)
272+
self.set_state(outer)
273+
263274
# ---- 101 ----
264275
def visit_Yield(self, node: ast.Yield):
265276
if self._yield_is_error:
@@ -286,6 +297,7 @@ def visit_Import(self, node: ast.Import):
286297
for name in node.names:
287298
if name.name == "trio" and name.asname is not None:
288299
self.error("TRIO106", node)
300+
self.generic_visit(node)
289301

290302
# ---- 110 ----
291303
def visit_While(self, node: ast.While):
@@ -302,38 +314,37 @@ def check_for_110(self, node: ast.While):
302314
self.error("TRIO110", node)
303315

304316
def visit_Call(self, node: ast.Call):
305-
def get_id(node: ast.AST) -> Optional[ast.Name]:
306-
if isinstance(node, ast.Name):
307-
return node
308-
if isinstance(node, ast.Attribute):
309-
return get_id(node.value)
310-
if isinstance(node, ast.keyword):
311-
return get_id(node.value)
312-
return None
317+
outer = self.get_state("_nursery_call_index", "_nursery_call_name")
313318

314319
if (
315320
isinstance(node.func, ast.Attribute)
316321
and isinstance(node.func.value, ast.Name)
317322
and node.func.attr in ("start", "start_soon")
318323
):
319-
called_vars: Dict[str, ast.Name] = {}
320-
for arg in (*node.args, *node.keywords):
321-
name = get_id(arg)
322-
if name:
323-
called_vars[name.id] = name
324-
325-
nursery_call = None
326-
for expr, cm_name, is_nursery in self._context_manager_stack:
324+
self._nursery_call_index = None
325+
for i, (_, cm_name, is_nursery) in enumerate(self._context_manager_stack):
327326
if node.func.value.id == cm_name:
328-
if not is_nursery:
329-
break
330-
nursery_call = expr
331-
continue
332-
if nursery_call is None:
333-
continue
334-
if cm_name in called_vars:
335-
self.error("TRIO302", node, expr.lineno, nursery_call.lineno)
327+
if is_nursery:
328+
self._nursery_call_index = i
329+
self._nursery_call_name = node.func.attr
330+
else:
331+
self._nursery_call_index = self._nursery_call_name = None
332+
333+
self.generic_visit(node)
334+
self.set_state(outer)
336335

336+
def visit_Name(self, node: ast.Name):
337+
if self._nursery_call_index is not None:
338+
for i, (expr, cm_name, _) in enumerate(self._context_manager_stack):
339+
if cm_name == node.id and i > self._nursery_call_index:
340+
self.error(
341+
"TRIO302",
342+
node,
343+
expr.lineno,
344+
self._context_manager_stack[self._nursery_call_index][0].lineno,
345+
node.id,
346+
self._nursery_call_name,
347+
)
337348
self.generic_visit(node)
338349

339350

tests/test_flake8_trio.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -66,15 +66,19 @@ def test_eval(test: str, path: str):
6666
try:
6767
# Append a bunch of empty strings so string formatting gives garbage
6868
# instead of throwing an exception
69-
args = eval(
70-
f"[{reg_match}]",
71-
{
72-
"lineno": lineno,
73-
"line": lineno,
74-
"Statement": Statement,
75-
"Stmt": Statement,
76-
},
77-
)
69+
try:
70+
args = eval(
71+
f"[{reg_match}]",
72+
{
73+
"lineno": lineno,
74+
"line": lineno,
75+
"Statement": Statement,
76+
"Stmt": Statement,
77+
},
78+
)
79+
except NameError:
80+
print(f"failed to eval on line {lineno}", file=sys.stderr)
81+
raise
7882

7983
except Exception as e:
8084
print(f"lineno: {lineno}, line: {line}", file=sys.stderr)

tests/trio302.py

Lines changed: 86 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import trio as noterror
55

66

7+
# fmt: off
78
async def foo():
89
async with trio.open_nursery():
910
...
@@ -12,37 +13,37 @@ async def foo():
1213
async with trio.open_nursery() as nursery:
1314
# async context manager
1415
async with trio.open_process() as bar:
15-
nursery.start(bar) # error: 12, line-1, line-3
16-
nursery.start(foo=bar) # error: 12, line-2, line-4
17-
nursery.start(..., ..., bar, ...) # error: 12, line-3, line-5
16+
nursery.start(bar) # error: 26, line-1, line-3, "bar", "start"
17+
nursery.start(foo=bar) # error: 30, line-2, line-4, "bar", "start"
18+
nursery.start(..., ..., bar, ...) # error: 36, line-3, line-5, "bar", "start"
1819

19-
nursery.start_soon(bar) # error: 12, line-5, line-7
20+
nursery.start_soon(bar) # error: 31, line-5, line-7, "bar", "start_soon"
2021

2122
# sync context manager
2223
with open("") as bar:
23-
nursery.start(bar) # error: 12, line-1, line-11
24-
nursery.start(foo=bar) # error: 12, line-2, line-12
25-
nursery.start(..., ..., bar, ...) # error: 12, line-3, line-13
24+
nursery.start(bar) # error: 26, line-1, line-11, "bar", "start"
25+
nursery.start(foo=bar) # error: 30, line-2, line-12, "bar", "start"
26+
nursery.start(..., ..., bar, ...) # error: 36, line-3, line-13, "bar", "start"
2627

27-
nursery.start_soon(bar) # error: 12, line-5, line-15
28+
nursery.start_soon(bar) # error: 31, line-5, line-15, "bar", "start_soon"
2829

2930
# sync nursery
3031
with trio.open_nursery() as nursery:
3132
# async context manager
3233
async with trio.open_process() as bar:
33-
nursery.start(bar) # error: 12, line-1, line-3
34-
nursery.start(foo=bar) # error: 12, line-2, line-4
35-
nursery.start(..., ..., bar, ...) # error: 12, line-3, line-5
34+
nursery.start(bar) # error: 26, line-1, line-3, "bar", "start"
35+
nursery.start(foo=bar) # error: 30, line-2, line-4, "bar", "start"
36+
nursery.start(..., ..., bar, ...) # error: 36, line-3, line-5, "bar", "start"
3637

37-
nursery.start_soon(bar) # error: 12, line-5, line-7
38+
nursery.start_soon(bar) # error: 31, line-5, line-7, "bar", "start_soon"
3839

3940
# sync context manager
4041
with open("") as bar:
41-
nursery.start(bar) # error: 12, line-1, line-11
42-
nursery.start(foo=bar) # error: 12, line-2, line-12
43-
nursery.start(..., ..., bar, ...) # error: 12, line-3, line-13
42+
nursery.start(bar) # error: 26, line-1, line-11, "bar", "start"
43+
nursery.start(foo=bar) # error: 30, line-2, line-12, "bar", "start"
44+
nursery.start(..., ..., bar, ...) # error: 36, line-3, line-13, "bar", "start"
4445

45-
nursery.start_soon(bar) # error: 12, line-5, line-15
46+
nursery.start_soon(bar) # error: 31, line-5, line-15, "bar", "start_soon"
4647

4748
# nursery inside context manager
4849
async with trio.open_process() as bar:
@@ -91,18 +92,18 @@ def foo_2():
9192
nursery_2.start(bar_1)
9293
nursery_2.start(bar_2)
9394
with open("") as bar_1:
94-
nursery_1.start(bar_1) # error: 16, line-1, line-11
95+
nursery_1.start(bar_1) # error: 32, line-1, line-11, "bar_1", "start"
9596
nursery_1.start(bar_2)
96-
nursery_2.start(bar_1) # error: 16, line-3, line-8
97+
nursery_2.start(bar_1) # error: 32, line-3, line-8, "bar_1", "start"
9798
nursery_2.start(bar_2)
9899
async with trio.open("") as bar_2:
99-
nursery_1.start(bar_1) # error: 20, line-6, line-16
100-
nursery_1.start(bar_2) # error: 20, line-2, line-17
101-
nursery_2.start(bar_1) # error: 20, line-8, line-13
102-
nursery_2.start(bar_2) # error: 20, line-4, line-14
103-
nursery_1.start(bar_1) # error: 16, line-10, line-20
100+
nursery_1.start(bar_1) # error: 36, line-6, line-16, "bar_1", "start"
101+
nursery_1.start(bar_2) # error: 36, line-2, line-17, "bar_2", "start"
102+
nursery_2.start(bar_1) # error: 36, line-8, line-13, "bar_1", "start"
103+
nursery_2.start(bar_2) # error: 36, line-4, line-14, "bar_2", "start"
104+
nursery_1.start(bar_1) # error: 32, line-10, line-20, "bar_1", "start"
104105
nursery_1.start(bar_2)
105-
nursery_2.start(bar_1) # error: 16, line-12, line-17
106+
nursery_2.start(bar_1) # error: 32, line-12, line-17, "bar_1", "start"
106107
nursery_2.start(bar_2)
107108
nursery_1.start(bar_1)
108109
nursery_1.start(bar_2)
@@ -119,25 +120,25 @@ def foo_2():
119120
nursery_2.start(bar_1)
120121
nursery_2.start(bar_2)
121122
with open("") as bar_1:
122-
nursery_1.start(bar_1) # error: 12, line-1, line-6
123+
nursery_1.start(bar_1) # error: 28, line-1, line-6, "bar_1", "start"
123124
nursery_1.start(bar_2)
124125
nursery_2.start(bar_1)
125126
nursery_2.start(bar_2)
126127
async with trio.open_nursery() as nursery_2:
127-
nursery_1.start(bar_1) # error: 16, line-6, line-11
128+
nursery_1.start(bar_1) # error: 32, line-6, line-11, "bar_1", "start"
128129
nursery_1.start(bar_2)
129130
nursery_2.start(bar_1)
130131
nursery_2.start(bar_2)
131132
async with trio.open("") as bar_2:
132-
nursery_1.start(bar_1) # error: 20, line-11, line-16
133-
nursery_1.start(bar_2) # error: 20, line-2, line-17
133+
nursery_1.start(bar_1) # error: 36, line-11, line-16, "bar_1", "start"
134+
nursery_1.start(bar_2) # error: 36, line-2, line-17, "bar_2", "start"
134135
nursery_2.start(bar_1)
135-
nursery_2.start(bar_2) # error: 20, line-4, line-9
136-
nursery_1.start(bar_1) # error: 16, line-15, line-20
136+
nursery_2.start(bar_2) # error: 36, line-4, line-9, "bar_2", "start"
137+
nursery_1.start(bar_1) # error: 32, line-15, line-20, "bar_1", "start"
137138
nursery_1.start(bar_2)
138139
nursery_2.start(bar_1)
139140
nursery_2.start(bar_2)
140-
nursery_1.start(bar_1) # error: 12, line-19, line-24
141+
nursery_1.start(bar_1) # error: 28, line-19, line-24, "bar_1", "start"
141142
nursery_1.start(bar_2)
142143
nursery_2.start(bar_1)
143144
nursery_2.start(bar_2)
@@ -147,29 +148,75 @@ def foo_2():
147148
nursery_2.start(bar_2)
148149

149150
async with trio.open_nursery() as nursery_1, trio.anything() as bar_1, trio.open_nursery() as nursery_2, trio.anything() as bar_2:
150-
nursery_1.start(bar_1) # error: 8, line-1, line-1
151-
nursery_1.start(bar_2) # error: 8, line-2, line-2
151+
nursery_1.start(bar_1) # error: 24, line-1, line-1, "bar_1", "start"
152+
nursery_1.start(bar_2) # error: 24, line-2, line-2, "bar_2", "start"
152153
nursery_2.start(bar_1)
153-
nursery_2.start(bar_2) # error: 8, line-4, line-4
154+
nursery_2.start(bar_2) # error: 24, line-4, line-4, "bar_2", "start"
154155

155156
async with trio.open_nursery() as nursery:
156157
async with trio.anything() as bar:
157158
nursery.start(noterror.bar) # safe
158-
nursery.start(bar.anything) # error: 12, line-2, line-3
159-
nursery.start(bar.anything.anything) # error: 12, line-3, line-4
159+
nursery.start(bar.anything) # error: 26, line-2, line-3, "bar", "start"
160+
nursery.start(bar.anything.anything) # error: 26, line-3, line-4, "bar", "start"
160161

161162
# I think this is an error
162163
async with trio.open_nursery() as nursery:
163164
async with trio.open_nursery() as nursery_2:
164-
nursery.start(nursery_2) # error: 12, line-1, line-2
165+
nursery.start(nursery_2) # error: 26, line-1, line-2, "nursery_2", "start"
166+
nursery_2.start(nursery)
165167

166-
# in theory safe
168+
# in theory safe-ish, but treated as error
167169
async with trio.open_nursery() as nursery:
168170
nursery = noterror.anything
169171
async with trio.anything() as bar:
170-
nursery.start_soon(bar) # error: 12, line-1, line-3
172+
nursery.start_soon(bar) # error: 31, line-1, line-3, "bar", "start_soon"
171173

172174
async with trio.open_nursery() as nursery:
173175
async with trio.anything() as nursery:
174176
async with trio.anything() as bar:
175177
nursery.start_soon(bar)
178+
179+
# weird calls
180+
# async nursery
181+
async with trio.open_nursery() as nursery:
182+
# async context manager
183+
async with trio.open_process() as bar:
184+
nursery.start(*bar) # error: 27, line-1, line-3, "bar", "start"
185+
nursery.start(foo=[*bar]) # error: 32, line-2, line-4, "bar", "start"
186+
nursery.start(..., ..., *bar, ...) # error: 37, line-3, line-5, "bar", "start"
187+
nursery.start_soon(*bar) # error: 32, line-4, line-6, "bar", "start_soon"
188+
189+
# async nursery
190+
async with trio.open_nursery() as nursery:
191+
# async context manager
192+
async with trio.open_process() as bar:
193+
nursery.start(**bar) # error: 28, line-1, line-3, "bar", "start"
194+
nursery.start(foo={**bar}) # error: 33, line-2, line-4, "bar", "start"
195+
nursery.start(..., ..., **bar, foo=...) # error: 38, line-3, line-5, "bar", "start"
196+
nursery.start_soon(**bar) # error: 33, line-4, line-6, "bar", "start_soon"
197+
198+
# async nursery
199+
async with trio.open_nursery() as nursery:
200+
# async context manager
201+
async with trio.open_process() as bar:
202+
nursery.start(
203+
...,
204+
bar, # error: 16, line-3, line-5, "bar", "start"
205+
...,
206+
*bar, # error: 17, line-5, line-7, "bar", "start"
207+
...,
208+
**bar, # error: 18, line-7, line-9, "bar", "start"
209+
)
210+
211+
async with trio.open_nursery() as nursery:
212+
# async context manager
213+
async with trio.open_process() as bar:
214+
nursery.start(list((tuple([0]), (bar)))) # error: 45, line-1, line-3, "bar", "start"
215+
216+
nursery.start("bar")
217+
nursery.start(lambda bar: bar+1)
218+
219+
def myfun(nursery, bar):
220+
nursery.start(bar)
221+
222+
# fmt: on

0 commit comments

Comments
 (0)
0