8000 Merge pull request #21 from jakkdl/10_no_trio_sleep_in_loop · python-trio/flake8-async@8bbbe7e · GitHub
[go: up one dir, main page]

Skip to content

Commit 8bbbe7e

Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
## Future
55
- Added TRIO109: Async definitions should not have a `timeout` parameter. Use `trio.[fail/move_on]_[at/after]`
6+
- Added TRIO110: `while <condition>: await trio.sleep()` should be replaced by a `trio.Event`.
67

78
## 22.7.6
89
- Extend TRIO102 to also check inside `except BaseException` and `except trio.Cancelled`
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,4 @@ pip install flake8-trio
3232
- **TRIO108**: Early return from async function must have at least one checkpoin 8000 t on every code path before it, unless an exception is raised.
3333
Checkpoints are `await`, `async with` `async for`.
3434
- **TRIO109**: Async function definition with a `timeout` parameter - use `trio.[fail/move_on]_[after/at]` instead
35+
- **TRIO110**: `while <condition>: await trio.sleep()` should be replaced by a `trio.Event`.
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ def visit_AsyncWith(self, node: ast.AsyncWith):
154154
def visit_FunctionDef(self, node: Union[ast.FunctionDef, ast.AsyncFunctionDef]):
155155
outer = self.get_state()
156156
self._yield_is_error = False
157+
self._inside_loop = False
157158

158159
# check for @<context_manager_name> and @<library>.<context_manager_name>
159160
if has_decorator(node.decorator_list, *context_manager_names):
@@ -198,6 +199,19 @@ def check_109(self, args: ast.arguments):
198199
if arg.arg == "timeout":
199200
self.error(TRIO109, arg.lineno, arg.col_offset)
200201

202+
def visit_While(self, node: ast.While):
203+
self.check_for_110(node)
204+
self.generic_visit(node)
205+
206+
def check_for_110(self, node: ast.While):
207+
if (
208+
len(node.body) == 1
209+
and isinstance(node.body[0], ast.Expr)
210+
and isinstance(node.body[0].value, ast.Await)
211+
and get_trio_scope(node.body[0].value.value, "sleep", "sleep_until")
212+
):
213+
self.error(TRIO110, node.lineno, node.col_offset)
214+
201215

202216
def critical_except(node: ast.ExceptHandler) -> Optional[Tuple[int, int, str]]:
203217
def has_exception(node: Optional[ast.expr]) -> str:
@@ -639,3 +653,4 @@ def run(self) -> Iterable[Error]:
639653
TRIO107 = "TRIO107: Async functions must have at least one checkpoint on every code path, unless an exception is raised"
640654
TRIO108 = "TRIO108: Early return from async function must have at least one checkpoint on every code path before it."
641655
TRIO109 = "TRIO109: Async function definition with a `timeout` parameter - use `trio.[fail/move_on]_[after/at]` instead"
656+
TRIO110 = "TRIO110: `while <condition>: await trio.sleep()` should be replaced by a `trio.Event`."
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import trio
2+
import trio as noerror
3+
4+
5+
async def foo():
6+
# only trigger on while loop with body being exactly one sleep[_until] statement
7+
while ...: # error: 4
8+
await trio.sleep()
9+
10+
while ...: # error: 4
11+
await trio.sleep_until()
12+
13+
# nested
14+
15+
while ...: # safe
16+
while ...: # error: 8
17+
await trio.sleep()
18+
await trio.sleep()
19+
20+
while ...: # safe
21+
while ...: # error: 8
22+
await trio.sleep()
23+
24+
### the rest are all safe
25+
26+
# don't trigger on bodies with more than one statement
27+
while ...:
28+
await trio.sleep()
29+
await trio.sleep()
30+
31+
while ...: # safe
32+
...
33+
await trio.sleep()
34+
35+
while ...:
36+
await trio.sleep()
37+
await trio.sleep_until()
38+
39+
# check library name
40+
while ...:
41+
await noerror.sleep()
42+
43+
async def sleep():
44+
...
45+
46+
while ...:
47+
await sleep()
48+
49+
# check function name
50+
while ...:
51+
await trio.sleepies()
52+
53+
# don't trigger on [async] for
54+
for _ in "":
55+
await trio.sleep()
56+
57+
async for _ in trio.blah:
58+
await trio.sleep()
59+
60+
while ...:
61+
62+
async def blah():
63+
await trio.sleep()
64+
65+
while ...:
66+
if ...:
67+
await trio.sleep()
68+
69+
while await trio.sleep():
70+
...