mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 21:35:58 +00:00
[TRIO] Add TRIO109 rule (#8534)
## Summary Adds TRIO109 from the [flake8-trio plugin](https://github.com/Zac-HD/flake8-trio). Relates to: https://github.com/astral-sh/ruff/issues/8451
This commit is contained in:
parent
621e98f452
commit
e2c7b1ece6
9 changed files with 98 additions and 0 deletions
10
crates/ruff_linter/resources/test/fixtures/flake8_trio/TRIO109.py
vendored
Normal file
10
crates/ruff_linter/resources/test/fixtures/flake8_trio/TRIO109.py
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
async def func():
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
async def func(timeout):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
async def func(timeout=10):
|
||||||
|
...
|
|
@ -356,6 +356,9 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||||
flake8_builtins::rules::builtin_variable_shadowing(checker, name, name.range());
|
flake8_builtins::rules::builtin_variable_shadowing(checker, name, name.range());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if checker.enabled(Rule::TrioAsyncFunctionWithTimeout) {
|
||||||
|
flake8_trio::rules::async_function_with_timeout(checker, function_def);
|
||||||
|
}
|
||||||
#[cfg(feature = "unreachable-code")]
|
#[cfg(feature = "unreachable-code")]
|
||||||
if checker.enabled(Rule::UnreachableCode) {
|
if checker.enabled(Rule::UnreachableCode) {
|
||||||
checker
|
checker
|
||||||
|
|
|
@ -293,6 +293,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||||
// flake8-trio
|
// flake8-trio
|
||||||
(Flake8Trio, "100") => (RuleGroup::Preview, rules::flake8_trio::rules::TrioTimeoutWithoutAwait),
|
(Flake8Trio, "100") => (RuleGroup::Preview, rules::flake8_trio::rules::TrioTimeoutWithoutAwait),
|
||||||
(Flake8Trio, "105") => (RuleGroup::Preview, rules::flake8_trio::rules::TrioSyncCall),
|
(Flake8Trio, "105") => (RuleGroup::Preview, rules::flake8_trio::rules::TrioSyncCall),
|
||||||
|
(Flake8Trio, "109") => (RuleGroup::Preview, rules::flake8_trio::rules::TrioAsyncFunctionWithTimeout),
|
||||||
(Flake8Trio, "110") => (RuleGroup::Preview, rules::flake8_trio::rules::TrioUnneededSleep),
|
(Flake8Trio, "110") => (RuleGroup::Preview, rules::flake8_trio::rules::TrioUnneededSleep),
|
||||||
(Flake8Trio, "115") => (RuleGroup::Preview, rules::flake8_trio::rules::TrioZeroSleepCall),
|
(Flake8Trio, "115") => (RuleGroup::Preview, rules::flake8_trio::rules::TrioZeroSleepCall),
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ mod tests {
|
||||||
|
|
||||||
#[test_case(Rule::TrioTimeoutWithoutAwait, Path::new("TRIO100.py"))]
|
#[test_case(Rule::TrioTimeoutWithoutAwait, Path::new("TRIO100.py"))]
|
||||||
#[test_case(Rule::TrioSyncCall, Path::new("TRIO105.py"))]
|
#[test_case(Rule::TrioSyncCall, Path::new("TRIO105.py"))]
|
||||||
|
#[test_case(Rule::TrioAsyncFunctionWithTimeout, Path::new("TRIO109.py"))]
|
||||||
#[test_case(Rule::TrioUnneededSleep, Path::new("TRIO110.py"))]
|
#[test_case(Rule::TrioUnneededSleep, Path::new("TRIO110.py"))]
|
||||||
#[test_case(Rule::TrioZeroSleepCall, Path::new("TRIO115.py"))]
|
#[test_case(Rule::TrioZeroSleepCall, Path::new("TRIO115.py"))]
|
||||||
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
|
use ruff_python_ast as ast;
|
||||||
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
|
use crate::checkers::ast::Checker;
|
||||||
|
|
||||||
|
/// ## What it does
|
||||||
|
/// Checks for `async` functions with a `timeout` argument.
|
||||||
|
///
|
||||||
|
/// ## Why is this bad?
|
||||||
|
/// Rather than implementing asynchronous timeout behavior manually, prefer
|
||||||
|
/// trio's built-in timeout functionality, available as `trio.fail_after`,
|
||||||
|
/// `trio.move_on_after`, `trio.fail_at`, and `trio.move_on_at`.
|
||||||
|
///
|
||||||
|
/// ## Example
|
||||||
|
/// ```python
|
||||||
|
/// async def func():
|
||||||
|
/// await long_running_task(timeout=2)
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Use instead:
|
||||||
|
/// ```python
|
||||||
|
/// async def func():
|
||||||
|
/// with trio.fail_after(2):
|
||||||
|
/// await long_running_task()
|
||||||
|
/// ```
|
||||||
|
#[violation]
|
||||||
|
pub struct TrioAsyncFunctionWithTimeout;
|
||||||
|
|
||||||
|
impl Violation for TrioAsyncFunctionWithTimeout {
|
||||||
|
#[derive_message_formats]
|
||||||
|
fn message(&self) -> String {
|
||||||
|
format!("Prefer `trio.fail_after` and `trio.move_on_after` over manual `async` timeout behavior")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TRIO109
|
||||||
|
pub(crate) fn async_function_with_timeout(
|
||||||
|
checker: &mut Checker,
|
||||||
|
function_def: &ast::StmtFunctionDef,
|
||||||
|
) {
|
||||||
|
if !function_def.is_async {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let Some(timeout) = function_def.parameters.find("timeout") else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
checker.diagnostics.push(Diagnostic::new(
|
||||||
|
TrioAsyncFunctionWithTimeout,
|
||||||
|
timeout.range(),
|
||||||
|
));
|
||||||
|
}
|
|
@ -1,8 +1,10 @@
|
||||||
|
pub(crate) use async_function_with_timeout::*;
|
||||||
pub(crate) use sync_call::*;
|
pub(crate) use sync_call::*;
|
||||||
pub(crate) use timeout_without_await::*;
|
pub(crate) use timeout_without_await::*;
|
||||||
pub(crate) use unneeded_sleep::*;
|
pub(crate) use unneeded_sleep::*;
|
||||||
pub(crate) use zero_sleep_call::*;
|
pub(crate) use zero_sleep_call::*;
|
||||||
|
|
||||||
|
mod async_function_with_timeout;
|
||||||
mod sync_call;
|
mod sync_call;
|
||||||
mod timeout_without_await;
|
mod timeout_without_await;
|
||||||
mod unneeded_sleep;
|
mod unneeded_sleep;
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff_linter/src/rules/flake8_trio/mod.rs
|
||||||
|
---
|
||||||
|
TRIO109.py:5:16: TRIO109 Prefer `trio.fail_after` and `trio.move_on_after` over manual `async` timeout behavior
|
||||||
|
|
|
||||||
|
5 | async def func(timeout):
|
||||||
|
| ^^^^^^^ TRIO109
|
||||||
|
6 | ...
|
||||||
|
|
|
||||||
|
|
||||||
|
TRIO109.py:9:16: TRIO109 Prefer `trio.fail_after` and `trio.move_on_after` over manual `async` timeout behavior
|
||||||
|
|
|
||||||
|
9 | async def func(timeout=10):
|
||||||
|
| ^^^^^^^^^^ TRIO109
|
||||||
|
10 | ...
|
||||||
|
|
|
||||||
|
|
||||||
|
|
|
@ -2281,6 +2281,15 @@ pub struct Parameters {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parameters {
|
impl Parameters {
|
||||||
|
/// Returns the [`ParameterWithDefault`] with the given name, or `None` if no such [`ParameterWithDefault`] exists.
|
||||||
|
pub fn find(&self, name: &str) -> Option<&ParameterWithDefault> {
|
||||||
|
self.posonlyargs
|
||||||
|
.iter()
|
||||||
|
.chain(&self.args)
|
||||||
|
.chain(&self.kwonlyargs)
|
||||||
|
.find(|arg| arg.parameter.name.as_str() == name)
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns `true` if a parameter with the given name included in this [`Parameters`].
|
/// Returns `true` if a parameter with the given name included in this [`Parameters`].
|
||||||
pub fn includes(&self, name: &str) -> bool {
|
pub fn includes(&self, name: &str) -> bool {
|
||||||
if self
|
if self
|
||||||
|
|
1
ruff.schema.json
generated
1
ruff.schema.json
generated
|
@ -3475,6 +3475,7 @@
|
||||||
"TRIO10",
|
"TRIO10",
|
||||||
"TRIO100",
|
"TRIO100",
|
||||||
"TRIO105",
|
"TRIO105",
|
||||||
|
"TRIO109",
|
||||||
"TRIO11",
|
"TRIO11",
|
||||||
"TRIO110",
|
"TRIO110",
|
||||||
"TRIO115",
|
"TRIO115",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue