mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-04 02:38:25 +00:00
Detect asyncio.get_running_loop
calls in RUF006 (#7562)
## Summary We can do a good enough job detecting this with our existing semantic model. Closes https://github.com/astral-sh/ruff/issues/3237.
This commit is contained in:
parent
b685ee4749
commit
87a0cd219f
6 changed files with 103 additions and 60 deletions
|
@ -63,11 +63,28 @@ def f():
|
|||
tasks = [asyncio.create_task(task) for task in tasks]
|
||||
|
||||
|
||||
# Ok (false negative)
|
||||
# OK (false negative)
|
||||
def f():
|
||||
task = asyncio.create_task(coordinator.ws_connect())
|
||||
|
||||
|
||||
# Ok (potential false negative)
|
||||
# OK (potential false negative)
|
||||
def f():
|
||||
do_nothing_with_the_task(asyncio.create_task(coordinator.ws_connect()))
|
||||
|
||||
|
||||
# Error
|
||||
def f():
|
||||
loop = asyncio.get_running_loop()
|
||||
loop.create_task(coordinator.ws_connect()) # Error
|
||||
|
||||
|
||||
# OK
|
||||
def f():
|
||||
loop.create_task(coordinator.ws_connect())
|
||||
|
||||
|
||||
# OK
|
||||
def f():
|
||||
loop = asyncio.get_running_loop()
|
||||
loop.do_thing(coordinator.ws_connect())
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::helpers::is_const_true;
|
||||
use ruff_python_ast::{Expr, ExprAttribute, ExprCall, Stmt, StmtAssign};
|
||||
use ruff_python_ast::{Expr, ExprAttribute, ExprCall};
|
||||
use ruff_python_semantic::analyze::typing;
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
@ -63,27 +64,11 @@ pub(crate) fn flask_debug_true(checker: &mut Checker, call: &ExprCall) {
|
|||
return;
|
||||
}
|
||||
|
||||
let Expr::Name(name) = value.as_ref() else {
|
||||
return;
|
||||
};
|
||||
|
||||
if let Some(binding_id) = checker.semantic().resolve_name(name) {
|
||||
if let Some(Stmt::Assign(StmtAssign { value, .. })) = checker
|
||||
.semantic()
|
||||
.binding(binding_id)
|
||||
.statement(checker.semantic())
|
||||
{
|
||||
if let Expr::Call(ExprCall { func, .. }) = value.as_ref() {
|
||||
if checker
|
||||
.semantic()
|
||||
.resolve_call_path(func)
|
||||
.is_some_and(|call_path| matches!(call_path.as_slice(), ["flask", "Flask"]))
|
||||
{
|
||||
checker
|
||||
.diagnostics
|
||||
.push(Diagnostic::new(FlaskDebugTrue, debug_argument.range()));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
if typing::resolve_assignment(value, checker.semantic())
|
||||
.is_some_and(|call_path| matches!(call_path.as_slice(), ["flask", "Flask"]))
|
||||
{
|
||||
checker
|
||||
.diagnostics
|
||||
.push(Diagnostic::new(FlaskDebugTrue, debug_argument.range()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::{Expr, ExprAttribute, ExprCall, Stmt, StmtAssign};
|
||||
use ruff_python_ast::{Expr, ExprAttribute, ExprCall};
|
||||
use ruff_python_semantic::analyze::typing;
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
@ -68,30 +69,12 @@ pub(crate) fn ssh_no_host_key_verification(checker: &mut Checker, call: &ExprCal
|
|||
return;
|
||||
}
|
||||
|
||||
let Expr::Name(name) = value.as_ref() else {
|
||||
return;
|
||||
};
|
||||
|
||||
if let Some(binding_id) = checker.semantic().resolve_name(name) {
|
||||
if let Some(Stmt::Assign(StmtAssign { value, .. })) = checker
|
||||
.semantic()
|
||||
.binding(binding_id)
|
||||
.statement(checker.semantic())
|
||||
{
|
||||
if let Expr::Call(ExprCall { func, .. }) = value.as_ref() {
|
||||
if checker
|
||||
.semantic()
|
||||
.resolve_call_path(func)
|
||||
.is_some_and(|call_path| {
|
||||
matches!(call_path.as_slice(), ["paramiko", "client", "SSHClient"])
|
||||
})
|
||||
{
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
SSHNoHostKeyVerification,
|
||||
policy_argument.range(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
if typing::resolve_assignment(value, checker.semantic()).is_some_and(|call_path| {
|
||||
matches!(call_path.as_slice(), ["paramiko", "client", "SSHClient"])
|
||||
}) {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
SSHNoHostKeyVerification,
|
||||
policy_argument.range(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ use ruff_python_ast::{self as ast, Expr};
|
|||
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_semantic::analyze::typing;
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
@ -70,7 +71,8 @@ pub(crate) fn asyncio_dangling_task(checker: &mut Checker, expr: &Expr) {
|
|||
return;
|
||||
};
|
||||
|
||||
let Some(method) = checker
|
||||
// Ex) `asyncio.create_task(...)`
|
||||
if let Some(method) = checker
|
||||
.semantic()
|
||||
.resolve_call_path(func)
|
||||
.and_then(|call_path| match call_path.as_slice() {
|
||||
|
@ -78,14 +80,29 @@ pub(crate) fn asyncio_dangling_task(checker: &mut Checker, expr: &Expr) {
|
|||
["asyncio", "ensure_future"] => Some(Method::EnsureFuture),
|
||||
_ => None,
|
||||
})
|
||||
else {
|
||||
{
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
AsyncioDanglingTask { method },
|
||||
expr.range(),
|
||||
));
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
AsyncioDanglingTask { method },
|
||||
expr.range(),
|
||||
));
|
||||
// Ex) `loop = asyncio.get_running_loop(); loop.create_task(...)`
|
||||
if let Expr::Attribute(ast::ExprAttribute { attr, value, .. }) = func.as_ref() {
|
||||
if attr == "create_task" {
|
||||
if typing::resolve_assignment(value, checker.semantic()).is_some_and(|call_path| {
|
||||
matches!(call_path.as_slice(), ["asyncio", "get_running_loop"])
|
||||
}) {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
AsyncioDanglingTask {
|
||||
method: Method::CreateTask,
|
||||
},
|
||||
expr.range(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||
|
|
|
@ -17,4 +17,12 @@ RUF006.py:11:5: RUF006 Store a reference to the return value of `asyncio.ensure_
|
|||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF006
|
||||
|
|
||||
|
||||
RUF006.py:79:5: RUF006 Store a reference to the return value of `asyncio.create_task`
|
||||
|
|
||||
77 | def f():
|
||||
78 | loop = asyncio.get_running_loop()
|
||||
79 | loop.create_task(coordinator.ws_connect()) # Error
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF006
|
||||
|
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue