Expand asyncio-dangling-task (RUF006) to include new_event_loop (#9976)

## Summary

Fixes #9974

## Test Plan

I added some new test cases.
This commit is contained in:
Asger Hautop Drewsen 2024-02-13 19:28:06 +01:00 committed by GitHub
parent 46db3f96ac
commit 3e9d761b13
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 62 additions and 7 deletions

View file

@ -162,3 +162,26 @@ async def f(x: bool):
T = asyncio.create_task(asyncio.sleep(1)) T = asyncio.create_task(asyncio.sleep(1))
else: else:
T = None T = None
# Error
def f():
loop = asyncio.new_event_loop()
loop.create_task(main()) # Error
# Error
def f():
loop = asyncio.get_event_loop()
loop.create_task(main()) # Error
# OK
def f():
global task
loop = asyncio.new_event_loop()
task = loop.create_task(main()) # Error
# OK
def f():
global task
loop = asyncio.get_event_loop()
task = loop.create_task(main()) # Error

View file

@ -3,6 +3,7 @@ use std::fmt;
use ast::Stmt; use ast::Stmt;
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::call_path::compose_call_path;
use ruff_python_ast::{self as ast, Expr}; use ruff_python_ast::{self as ast, Expr};
use ruff_python_semantic::{analyze::typing, Scope, SemanticModel}; use ruff_python_semantic::{analyze::typing, Scope, SemanticModel};
use ruff_text_size::Ranged; use ruff_text_size::Ranged;
@ -52,14 +53,15 @@ use ruff_text_size::Ranged;
/// - [The Python Standard Library](https://docs.python.org/3/library/asyncio-task.html#asyncio.create_task) /// - [The Python Standard Library](https://docs.python.org/3/library/asyncio-task.html#asyncio.create_task)
#[violation] #[violation]
pub struct AsyncioDanglingTask { pub struct AsyncioDanglingTask {
expr: String,
method: Method, method: Method,
} }
impl Violation for AsyncioDanglingTask { impl Violation for AsyncioDanglingTask {
#[derive_message_formats] #[derive_message_formats]
fn message(&self) -> String { fn message(&self) -> String {
let AsyncioDanglingTask { method } = self; let AsyncioDanglingTask { expr, method } = self;
format!("Store a reference to the return value of `asyncio.{method}`") format!("Store a reference to the return value of `{expr}.{method}`")
} }
} }
@ -80,19 +82,29 @@ pub(crate) fn asyncio_dangling_task(expr: &Expr, semantic: &SemanticModel) -> Op
}) })
{ {
return Some(Diagnostic::new( return Some(Diagnostic::new(
AsyncioDanglingTask { method }, AsyncioDanglingTask {
expr: "asyncio".to_string(),
method,
},
expr.range(), expr.range(),
)); ));
} }
// Ex) `loop = asyncio.get_running_loop(); loop.create_task(...)` // Ex) `loop = ...; loop.create_task(...)`
if let Expr::Attribute(ast::ExprAttribute { attr, value, .. }) = func.as_ref() { if let Expr::Attribute(ast::ExprAttribute { attr, value, .. }) = func.as_ref() {
if attr == "create_task" { if attr == "create_task" {
if typing::resolve_assignment(value, semantic).is_some_and(|call_path| { if typing::resolve_assignment(value, semantic).is_some_and(|call_path| {
matches!(call_path.as_slice(), ["asyncio", "get_running_loop"]) matches!(
call_path.as_slice(),
[
"asyncio",
"get_event_loop" | "get_running_loop" | "new_event_loop"
]
)
}) { }) {
return Some(Diagnostic::new( return Some(Diagnostic::new(
AsyncioDanglingTask { AsyncioDanglingTask {
expr: compose_call_path(value).unwrap_or_else(|| "asyncio".to_string()),
method: Method::CreateTask, method: Method::CreateTask,
}, },
expr.range(), expr.range(),

View file

@ -25,7 +25,7 @@ RUF006.py:68:12: RUF006 Store a reference to the return value of `asyncio.create
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF006 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF006
| |
RUF006.py:74:26: RUF006 Store a reference to the return value of `asyncio.create_task` RUF006.py:74:26: RUF006 Store a reference to the return value of `loop.create_task`
| |
72 | def f(): 72 | def f():
73 | loop = asyncio.get_running_loop() 73 | loop = asyncio.get_running_loop()
@ -33,7 +33,7 @@ RUF006.py:74:26: RUF006 Store a reference to the return value of `asyncio.create
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF006 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF006
| |
RUF006.py:97:5: RUF006 Store a reference to the return value of `asyncio.create_task` RUF006.py:97:5: RUF006 Store a reference to the return value of `loop.create_task`
| |
95 | def f(): 95 | def f():
96 | loop = asyncio.get_running_loop() 96 | loop = asyncio.get_running_loop()
@ -41,4 +41,24 @@ RUF006.py:97:5: RUF006 Store a reference to the return value of `asyncio.create_
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF006 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF006
| |
RUF006.py:170:5: RUF006 Store a reference to the return value of `loop.create_task`
|
168 | def f():
169 | loop = asyncio.new_event_loop()
170 | loop.create_task(main()) # Error
| ^^^^^^^^^^^^^^^^^^^^^^^^ RUF006
171 |
172 | # Error
|
RUF006.py:175:5: RUF006 Store a reference to the return value of `loop.create_task`
|
173 | def f():
174 | loop = asyncio.get_event_loop()
175 | loop.create_task(main()) # Error
| ^^^^^^^^^^^^^^^^^^^^^^^^ RUF006
176 |
177 | # OK
|