Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add TRIO110 rule #8537

Merged
merged 5 commits into from
Nov 7, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions crates/ruff_linter/resources/test/fixtures/flake8_trio/TRIO110.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import trio

async def foo():
while True:
await trio.sleep(10)

async def foo():
while True:
await trio.sleep_until(10)

async def foo():
while True:
trio.sleep(10)
6 changes: 6 additions & 0 deletions crates/ruff_linter/src/checkers/ast/analyze/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1216,6 +1216,12 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
if checker.enabled(Rule::TryExceptInLoop) {
perflint::rules::try_except_in_loop(checker, body);
}
if checker.enabled(Rule::TryExceptInLoop) {
perflint::rules::try_except_in_loop(checker, body);
}
karpetrosyan marked this conversation as resolved.
Show resolved Hide resolved
if checker.enabled(Rule::TrioUnneededSleep) {
flake8_trio::rules::unneeded_sleep(checker, stmt, body);
}
}
Stmt::For(
for_stmt @ ast::StmtFor {
Expand Down
1 change: 1 addition & 0 deletions crates/ruff_linter/src/codes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
// flake8-trio
(Flake8Trio, "100") => (RuleGroup::Preview, rules::flake8_trio::rules::TrioTimeoutWithoutAwait),
(Flake8Trio, "105") => (RuleGroup::Preview, rules::flake8_trio::rules::TrioSyncCall),
(Flake8Trio, "110") => (RuleGroup::Preview, rules::flake8_trio::rules::TrioUnneededSleep),
(Flake8Trio, "115") => (RuleGroup::Preview, rules::flake8_trio::rules::TrioZeroSleepCall),

// flake8-builtins
Expand Down
1 change: 1 addition & 0 deletions crates/ruff_linter/src/rules/flake8_trio/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ mod tests {

#[test_case(Rule::TrioTimeoutWithoutAwait, Path::new("TRIO100.py"))]
#[test_case(Rule::TrioSyncCall, Path::new("TRIO105.py"))]
#[test_case(Rule::TrioUnneededSleep, Path::new("TRIO110.py"))]
#[test_case(Rule::TrioZeroSleepCall, Path::new("TRIO115.py"))]
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy());
Expand Down
2 changes: 2 additions & 0 deletions crates/ruff_linter/src/rules/flake8_trio/rules/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
pub(crate) use sync_call::*;
pub(crate) use timeout_without_await::*;
pub(crate) use unneeded_sleep::*;
pub(crate) use zero_sleep_call::*;

mod sync_call;
mod timeout_without_await;
mod unneeded_sleep;
mod zero_sleep_call;
74 changes: 74 additions & 0 deletions crates/ruff_linter/src/rules/flake8_trio/rules/unneeded_sleep.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::{Expr, ExprAwait, ExprCall, Stmt, StmtExpr};
use ruff_text_size::Ranged;

use crate::checkers::ast::Checker;

/// ## What it does
/// Checks for the while loop, which waits for the event.
///
/// ## Why is this bad?
/// Instead of sleeping in a loop waiting for a condition to be true,
/// it's preferable to use a trio. Event.
karpetrosyan marked this conversation as resolved.
Show resolved Hide resolved
///
/// ## Example
/// ```python
/// DONE = False
///
/// async def func():
/// while not DONE:
/// await trio.sleep(1)
/// ```
///
/// Use instead:
/// ```python
/// DONE = trio.Event()
///
/// async def func():
/// await DONE.wait()
/// ```
#[violation]
pub struct TrioUnneededSleep;

impl Violation for TrioUnneededSleep {
#[derive_message_formats]
fn message(&self) -> String {
format!("Use event instead of `while <condition>: await trio.sleep()`")
}
}

pub(crate) fn unneeded_sleep(checker: &mut Checker, stmt: &Stmt, body: &[Stmt]) {
if body.len() != 1 {
return;
}

let awaitable = {
if let Stmt::Expr(StmtExpr { range: _, value }) = &body[0] {
if let Expr::Await(ExprAwait { range: _, value }) = value.as_ref() {
Some(value.as_ref())
} else {
None
}
} else {
None
}
};

if let Some(Expr::Call(ExprCall {
range: _,
func,
arguments: _,
})) = awaitable
{
if checker
.semantic()
.resolve_call_path(func.as_ref())
.is_some_and(|path| matches!(path.as_slice(), ["trio", "sleep" | "sleep_until"]))
{
checker
.diagnostics
.push(Diagnostic::new(TrioUnneededSleep, stmt.range()));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
source: crates/ruff_linter/src/rules/flake8_trio/mod.rs
---
TRIO110.py:4:5: TRIO110 Use event instead of `while <condition>: await trio.sleep()`
|
3 | async def foo():
4 | while True:
| _____^
5 | | await trio.sleep(10)
| |____________________________^ TRIO110
6 |
7 | async def foo():
|

TRIO110.py:8:5: TRIO110 Use event instead of `while <condition>: await trio.sleep()`
|
7 | async def foo():
8 | while True:
| _____^
9 | | await trio.sleep_until(10)
| |__________________________________^ TRIO110
10 |
11 | async def foo():
|


1 change: 1 addition & 0 deletions ruff.schema.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading