mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-03 15:14:42 +00:00
[flake8-async
] Implement blocking-input
rule (ASYNC250
) (#20122)
## Summary Adds new rule to catch use of builtins `input()` in async functions. Issue #8451 ## Test Plan New snapshosts in `ASYNC250.py` with `cargo insta test`.
This commit is contained in:
parent
166b63ad4d
commit
ca1f66a657
8 changed files with 111 additions and 0 deletions
22
crates/ruff_linter/resources/test/fixtures/flake8_async/ASYNC250.py
vendored
Normal file
22
crates/ruff_linter/resources/test/fixtures/flake8_async/ASYNC250.py
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
def foo():
|
||||
k = input() # Ok
|
||||
input("hello world") # Ok
|
||||
|
||||
|
||||
async def foo():
|
||||
k = input() # ASYNC250
|
||||
input("hello world") # ASYNC250
|
||||
|
||||
|
||||
import builtins
|
||||
|
||||
import fake
|
||||
|
||||
|
||||
def foo():
|
||||
builtins.input("testing") # Ok
|
||||
|
||||
|
||||
async def foo():
|
||||
builtins.input("testing") # ASYNC250
|
||||
fake.input("whatever") # Ok
|
|
@ -673,6 +673,9 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
|
|||
]) {
|
||||
flake8_async::rules::blocking_process_invocation(checker, call);
|
||||
}
|
||||
if checker.is_rule_enabled(Rule::BlockingInputInAsyncFunction) {
|
||||
flake8_async::rules::blocking_input(checker, call);
|
||||
}
|
||||
if checker.is_rule_enabled(Rule::BlockingSleepInAsyncFunction) {
|
||||
flake8_async::rules::blocking_sleep(checker, call);
|
||||
}
|
||||
|
|
|
@ -341,6 +341,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
|||
(Flake8Async, "221") => (RuleGroup::Stable, rules::flake8_async::rules::RunProcessInAsyncFunction),
|
||||
(Flake8Async, "222") => (RuleGroup::Stable, rules::flake8_async::rules::WaitForProcessInAsyncFunction),
|
||||
(Flake8Async, "230") => (RuleGroup::Stable, rules::flake8_async::rules::BlockingOpenCallInAsyncFunction),
|
||||
(Flake8Async, "250") => (RuleGroup::Preview, rules::flake8_async::rules::BlockingInputInAsyncFunction),
|
||||
(Flake8Async, "251") => (RuleGroup::Stable, rules::flake8_async::rules::BlockingSleepInAsyncFunction),
|
||||
|
||||
// flake8-builtins
|
||||
|
|
|
@ -28,6 +28,7 @@ mod tests {
|
|||
#[test_case(Rule::RunProcessInAsyncFunction, Path::new("ASYNC22x.py"))]
|
||||
#[test_case(Rule::WaitForProcessInAsyncFunction, Path::new("ASYNC22x.py"))]
|
||||
#[test_case(Rule::BlockingOpenCallInAsyncFunction, Path::new("ASYNC230.py"))]
|
||||
#[test_case(Rule::BlockingInputInAsyncFunction, Path::new("ASYNC250.py"))]
|
||||
#[test_case(Rule::BlockingSleepInAsyncFunction, Path::new("ASYNC251.py"))]
|
||||
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy());
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
use ruff_python_ast::ExprCall;
|
||||
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::Violation;
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks that async functions do not contain blocking usage of input from user.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Blocking an async function via a blocking input call will block the entire
|
||||
/// event loop, preventing it from executing other tasks while waiting for user
|
||||
/// input, negating the benefits of asynchronous programming.
|
||||
///
|
||||
/// Instead of making a blocking input call directly, wrap the input call in
|
||||
/// an executor to execute the blocking call on another thread.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// async def foo():
|
||||
/// username = input("Username:")
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// import asyncio
|
||||
///
|
||||
///
|
||||
/// async def foo():
|
||||
/// loop = asyncio.get_running_loop()
|
||||
/// username = await loop.run_in_executor(None, input, "Username:")
|
||||
/// ```
|
||||
#[derive(ViolationMetadata)]
|
||||
pub(crate) struct BlockingInputInAsyncFunction;
|
||||
|
||||
impl Violation for BlockingInputInAsyncFunction {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
"Blocking call to input() in async context".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
/// ASYNC250
|
||||
pub(crate) fn blocking_input(checker: &Checker, call: &ExprCall) {
|
||||
if checker.semantic().in_async_context() {
|
||||
if checker.semantic().match_builtin_expr(&call.func, "input") {
|
||||
checker.report_diagnostic(BlockingInputInAsyncFunction, call.func.range());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@ pub(crate) use async_function_with_timeout::*;
|
|||
pub(crate) use async_zero_sleep::*;
|
||||
pub(crate) use blocking_http_call::*;
|
||||
pub(crate) use blocking_http_call_httpx::*;
|
||||
pub(crate) use blocking_input::*;
|
||||
pub(crate) use blocking_open_call::*;
|
||||
pub(crate) use blocking_process_invocation::*;
|
||||
pub(crate) use blocking_sleep::*;
|
||||
|
@ -15,6 +16,7 @@ mod async_function_with_timeout;
|
|||
mod async_zero_sleep;
|
||||
mod blocking_http_call;
|
||||
mod blocking_http_call_httpx;
|
||||
mod blocking_input;
|
||||
mod blocking_open_call;
|
||||
mod blocking_process_invocation;
|
||||
mod blocking_sleep;
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_async/mod.rs
|
||||
---
|
||||
ASYNC250 Blocking call to input() in async context
|
||||
--> ASYNC250.py:7:9
|
||||
|
|
||||
6 | async def foo():
|
||||
7 | k = input() # ASYNC250
|
||||
| ^^^^^
|
||||
8 | input("hello world") # ASYNC250
|
||||
|
|
||||
|
||||
ASYNC250 Blocking call to input() in async context
|
||||
--> ASYNC250.py:8:5
|
||||
|
|
||||
6 | async def foo():
|
||||
7 | k = input() # ASYNC250
|
||||
8 | input("hello world") # ASYNC250
|
||||
| ^^^^^
|
||||
|
|
||||
|
||||
ASYNC250 Blocking call to input() in async context
|
||||
--> ASYNC250.py:21:5
|
||||
|
|
||||
20 | async def foo():
|
||||
21 | builtins.input("testing") # ASYNC250
|
||||
| ^^^^^^^^^^^^^^
|
||||
22 | fake.input("whatever") # Ok
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue