Skip to content

Commit 0126f74

Browse files
authored
Add TRIO110 rule (#8537)
## Summary Adds TRIO110 from the [flake8-trio plugin](https://github.com/Zac-HD/flake8-trio). Relates to: #8451
1 parent fce9f63 commit 0126f74

File tree

8 files changed

+115
-1
lines changed

8 files changed

+115
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import trio
2+
3+
4+
async def func():
5+
while True:
6+
await trio.sleep(10)
7+
8+
9+
async def func():
10+
while True:
11+
await trio.sleep_until(10)
12+
13+
14+
async def func():
15+
while True:
16+
trio.sleep(10)

crates/ruff_linter/src/checkers/ast/analyze/statement.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -1206,7 +1206,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
12061206
flake8_trio::rules::timeout_without_await(checker, with_stmt, items);
12071207
}
12081208
}
1209-
Stmt::While(ast::StmtWhile { body, orelse, .. }) => {
1209+
Stmt::While(while_stmt @ ast::StmtWhile { body, orelse, .. }) => {
12101210
if checker.enabled(Rule::FunctionUsesLoopVariable) {
12111211
flake8_bugbear::rules::function_uses_loop_variable(checker, &Node::Stmt(stmt));
12121212
}
@@ -1216,6 +1216,9 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
12161216
if checker.enabled(Rule::TryExceptInLoop) {
12171217
perflint::rules::try_except_in_loop(checker, body);
12181218
}
1219+
if checker.enabled(Rule::TrioUnneededSleep) {
1220+
flake8_trio::rules::unneeded_sleep(checker, while_stmt);
1221+
}
12191222
}
12201223
Stmt::For(
12211224
for_stmt @ ast::StmtFor {

crates/ruff_linter/src/codes.rs

+1
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
293293
// flake8-trio
294294
(Flake8Trio, "100") => (RuleGroup::Preview, rules::flake8_trio::rules::TrioTimeoutWithoutAwait),
295295
(Flake8Trio, "105") => (RuleGroup::Preview, rules::flake8_trio::rules::TrioSyncCall),
296+
(Flake8Trio, "110") => (RuleGroup::Preview, rules::flake8_trio::rules::TrioUnneededSleep),
296297
(Flake8Trio, "115") => (RuleGroup::Preview, rules::flake8_trio::rules::TrioZeroSleepCall),
297298

298299
// flake8-builtins

crates/ruff_linter/src/rules/flake8_trio/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ mod tests {
1616

1717
#[test_case(Rule::TrioTimeoutWithoutAwait, Path::new("TRIO100.py"))]
1818
#[test_case(Rule::TrioSyncCall, Path::new("TRIO105.py"))]
19+
#[test_case(Rule::TrioUnneededSleep, Path::new("TRIO110.py"))]
1920
#[test_case(Rule::TrioZeroSleepCall, Path::new("TRIO115.py"))]
2021
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
2122
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy());
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
pub(crate) use sync_call::*;
22
pub(crate) use timeout_without_await::*;
3+
pub(crate) use unneeded_sleep::*;
34
pub(crate) use zero_sleep_call::*;
45

56
mod sync_call;
67
mod timeout_without_await;
8+
mod unneeded_sleep;
79
mod zero_sleep_call;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
use ruff_diagnostics::{Diagnostic, Violation};
2+
use ruff_macros::{derive_message_formats, violation};
3+
use ruff_python_ast::{self as ast, Expr, Stmt};
4+
use ruff_text_size::Ranged;
5+
6+
use crate::checkers::ast::Checker;
7+
8+
/// ## What it does
9+
/// Checks for the use of `trio.sleep` in a `while` loop.
10+
///
11+
/// ## Why is this bad?
12+
/// Instead of sleeping in a `while` loop, and waiting for a condition
13+
/// to become true, it's preferable to `wait()` on a `trio.Event`.
14+
///
15+
/// ## Example
16+
/// ```python
17+
/// DONE = False
18+
///
19+
///
20+
/// async def func():
21+
/// while not DONE:
22+
/// await trio.sleep(1)
23+
/// ```
24+
///
25+
/// Use instead:
26+
/// ```python
27+
/// DONE = trio.Event()
28+
///
29+
///
30+
/// async def func():
31+
/// await DONE.wait()
32+
/// ```
33+
#[violation]
34+
pub struct TrioUnneededSleep;
35+
36+
impl Violation for TrioUnneededSleep {
37+
#[derive_message_formats]
38+
fn message(&self) -> String {
39+
format!("Use `trio.Event` instead of awaiting `trio.sleep` in a `while` loop")
40+
}
41+
}
42+
43+
/// TRIO110
44+
pub(crate) fn unneeded_sleep(checker: &mut Checker, while_stmt: &ast::StmtWhile) {
45+
// The body should be a single `await` call.
46+
let [stmt] = while_stmt.body.as_slice() else {
47+
return;
48+
};
49+
let Stmt::Expr(ast::StmtExpr { value, .. }) = stmt else {
50+
return;
51+
};
52+
let Expr::Await(ast::ExprAwait { value, .. }) = value.as_ref() else {
53+
return;
54+
};
55+
let Expr::Call(ast::ExprCall { func, .. }) = value.as_ref() else {
56+
return;
57+
};
58+
59+
if checker
60+
.semantic()
61+
.resolve_call_path(func.as_ref())
62+
.is_some_and(|path| matches!(path.as_slice(), ["trio", "sleep" | "sleep_until"]))
63+
{
64+
checker
65+
.diagnostics
66+
.push(Diagnostic::new(TrioUnneededSleep, while_stmt.range()));
67+
}
68+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
source: crates/ruff_linter/src/rules/flake8_trio/mod.rs
3+
---
4+
TRIO110.py:5:5: TRIO110 Use `trio.Event` instead of awaiting `trio.sleep` in a `while` loop
5+
|
6+
4 | async def func():
7+
5 | while True:
8+
| _____^
9+
6 | | await trio.sleep(10)
10+
| |____________________________^ TRIO110
11+
|
12+
13+
TRIO110.py:10:5: TRIO110 Use `trio.Event` instead of awaiting `trio.sleep` in a `while` loop
14+
|
15+
9 | async def func():
16+
10 | while True:
17+
| _____^
18+
11 | | await trio.sleep_until(10)
19+
| |__________________________________^ TRIO110
20+
|
21+
22+

ruff.schema.json

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)