mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-02 22:55:08 +00:00
[flake8-async
] Update ASYNC100
to match upstream (#12221)
## Summary Update the name of `ASYNC100` to match [upstream](https://flake8-async.readthedocs.io/en/latest/rules.html). Also update to the functionality to match upstream by supporting additional context managers from asyncio and anyio. Matching this [list](https://flake8-async.readthedocs.io/en/latest/glossary.html#timeout-context). Part of #12039. ## Test Plan Added the new context managers to the fixture.
This commit is contained in:
parent
6fa4e32ad3
commit
88abc6aed8
9 changed files with 333 additions and 153 deletions
|
@ -1,3 +1,5 @@
|
||||||
|
import anyio
|
||||||
|
import asyncio
|
||||||
import trio
|
import trio
|
||||||
|
|
||||||
|
|
||||||
|
@ -25,3 +27,33 @@ async def func():
|
||||||
with trio.move_at():
|
with trio.move_at():
|
||||||
async with trio.open_nursery() as nursery:
|
async with trio.open_nursery() as nursery:
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
||||||
|
async def func():
|
||||||
|
with anyio.move_on_after():
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
async def func():
|
||||||
|
with anyio.fail_after():
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
async def func():
|
||||||
|
with anyio.CancelScope():
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
async def func():
|
||||||
|
with anyio.CancelScope():
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
async def func():
|
||||||
|
with asyncio.timeout():
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
async def func():
|
||||||
|
with asyncio.timeout_at():
|
||||||
|
...
|
||||||
|
|
|
@ -1313,8 +1313,8 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||||
if checker.enabled(Rule::UselessWithLock) {
|
if checker.enabled(Rule::UselessWithLock) {
|
||||||
pylint::rules::useless_with_lock(checker, with_stmt);
|
pylint::rules::useless_with_lock(checker, with_stmt);
|
||||||
}
|
}
|
||||||
if checker.enabled(Rule::TrioTimeoutWithoutAwait) {
|
if checker.enabled(Rule::CancelScopeNoCheckpoint) {
|
||||||
flake8_async::rules::timeout_without_await(checker, with_stmt, items);
|
flake8_async::rules::cancel_scope_no_checkpoint(checker, with_stmt, items);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Stmt::While(while_stmt @ ast::StmtWhile { body, orelse, .. }) => {
|
Stmt::While(while_stmt @ ast::StmtWhile { body, orelse, .. }) => {
|
||||||
|
|
|
@ -293,7 +293,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||||
(Pylint, "W3301") => (RuleGroup::Stable, rules::pylint::rules::NestedMinMax),
|
(Pylint, "W3301") => (RuleGroup::Stable, rules::pylint::rules::NestedMinMax),
|
||||||
|
|
||||||
// flake8-async
|
// flake8-async
|
||||||
(Flake8Async, "100") => (RuleGroup::Stable, rules::flake8_async::rules::TrioTimeoutWithoutAwait),
|
(Flake8Async, "100") => (RuleGroup::Stable, rules::flake8_async::rules::CancelScopeNoCheckpoint),
|
||||||
(Flake8Async, "105") => (RuleGroup::Stable, rules::flake8_async::rules::TrioSyncCall),
|
(Flake8Async, "105") => (RuleGroup::Stable, rules::flake8_async::rules::TrioSyncCall),
|
||||||
(Flake8Async, "109") => (RuleGroup::Stable, rules::flake8_async::rules::AsyncFunctionWithTimeout),
|
(Flake8Async, "109") => (RuleGroup::Stable, rules::flake8_async::rules::AsyncFunctionWithTimeout),
|
||||||
(Flake8Async, "110") => (RuleGroup::Stable, rules::flake8_async::rules::TrioUnneededSleep),
|
(Flake8Async, "110") => (RuleGroup::Stable, rules::flake8_async::rules::TrioUnneededSleep),
|
||||||
|
|
|
@ -12,69 +12,124 @@ pub(super) enum AsyncModule {
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
pub(super) enum MethodName {
|
pub(super) enum MethodName {
|
||||||
AcloseForcefully,
|
AsyncIoTimeout,
|
||||||
CancelScope,
|
AsyncIoTimeoutAt,
|
||||||
CancelShieldedCheckpoint,
|
AnyIoMoveOnAfter,
|
||||||
Checkpoint,
|
AnyIoFailAfter,
|
||||||
CheckpointIfCancelled,
|
AnyIoCancelScope,
|
||||||
FailAfter,
|
TrioAcloseForcefully,
|
||||||
FailAt,
|
TrioCancelScope,
|
||||||
MoveOnAfter,
|
TrioCancelShieldedCheckpoint,
|
||||||
MoveOnAt,
|
TrioCheckpoint,
|
||||||
OpenFile,
|
TrioCheckpointIfCancelled,
|
||||||
OpenProcess,
|
TrioFailAfter,
|
||||||
OpenSslOverTcpListeners,
|
TrioFailAt,
|
||||||
OpenSslOverTcpStream,
|
TrioMoveOnAfter,
|
||||||
OpenTcpListeners,
|
TrioMoveOnAt,
|
||||||
OpenTcpStream,
|
TrioOpenFile,
|
||||||
OpenUnixSocket,
|
TrioOpenProcess,
|
||||||
PermanentlyDetachCoroutineObject,
|
TrioOpenSslOverTcpListeners,
|
||||||
ReattachDetachedCoroutineObject,
|
TrioOpenSslOverTcpStream,
|
||||||
RunProcess,
|
TrioOpenTcpListeners,
|
||||||
ServeListeners,
|
TrioOpenTcpStream,
|
||||||
ServeSslOverTcp,
|
TrioOpenUnixSocket,
|
||||||
ServeTcp,
|
TrioPermanentlyDetachCoroutineObject,
|
||||||
Sleep,
|
TrioReattachDetachedCoroutineObject,
|
||||||
SleepForever,
|
TrioRunProcess,
|
||||||
TemporarilyDetachCoroutineObject,
|
TrioServeListeners,
|
||||||
WaitReadable,
|
TrioServeSslOverTcp,
|
||||||
WaitTaskRescheduled,
|
TrioServeTcp,
|
||||||
WaitWritable,
|
TrioSleep,
|
||||||
|
TrioSleepForever,
|
||||||
|
TrioTemporarilyDetachCoroutineObject,
|
||||||
|
TrioWaitReadable,
|
||||||
|
TrioWaitTaskRescheduled,
|
||||||
|
TrioWaitWritable,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MethodName {
|
impl MethodName {
|
||||||
/// Returns `true` if the method is async, `false` if it is sync.
|
/// Returns `true` if the method is async, `false` if it is sync.
|
||||||
pub(super) fn is_async(self) -> bool {
|
pub(super) fn is_async(self) -> bool {
|
||||||
match self {
|
matches!(
|
||||||
MethodName::AcloseForcefully
|
self,
|
||||||
| MethodName::CancelShieldedCheckpoint
|
MethodName::TrioAcloseForcefully
|
||||||
| MethodName::Checkpoint
|
| MethodName::TrioCancelShieldedCheckpoint
|
||||||
| MethodName::CheckpointIfCancelled
|
| MethodName::TrioCheckpoint
|
||||||
| MethodName::OpenFile
|
| MethodName::TrioCheckpointIfCancelled
|
||||||
| MethodName::OpenProcess
|
| MethodName::TrioOpenFile
|
||||||
| MethodName::OpenSslOverTcpListeners
|
| MethodName::TrioOpenProcess
|
||||||
| MethodName::OpenSslOverTcpStream
|
| MethodName::TrioOpenSslOverTcpListeners
|
||||||
| MethodName::OpenTcpListeners
|
| MethodName::TrioOpenSslOverTcpStream
|
||||||
| MethodName::OpenTcpStream
|
| MethodName::TrioOpenTcpListeners
|
||||||
| MethodName::OpenUnixSocket
|
| MethodName::TrioOpenTcpStream
|
||||||
| MethodName::PermanentlyDetachCoroutineObject
|
| MethodName::TrioOpenUnixSocket
|
||||||
| MethodName::ReattachDetachedCoroutineObject
|
| MethodName::TrioPermanentlyDetachCoroutineObject
|
||||||
| MethodName::RunProcess
|
| MethodName::TrioReattachDetachedCoroutineObject
|
||||||
| MethodName::ServeListeners
|
| MethodName::TrioRunProcess
|
||||||
| MethodName::ServeSslOverTcp
|
| MethodName::TrioServeListeners
|
||||||
| MethodName::ServeTcp
|
| MethodName::TrioServeSslOverTcp
|
||||||
| MethodName::Sleep
|
| MethodName::TrioServeTcp
|
||||||
| MethodName::SleepForever
|
| MethodName::TrioSleep
|
||||||
| MethodName::TemporarilyDetachCoroutineObject
|
| MethodName::TrioSleepForever
|
||||||
| MethodName::WaitReadable
|
| MethodName::TrioTemporarilyDetachCoroutineObject
|
||||||
| MethodName::WaitTaskRescheduled
|
| MethodName::TrioWaitReadable
|
||||||
| MethodName::WaitWritable => true,
|
| MethodName::TrioWaitTaskRescheduled
|
||||||
|
| MethodName::TrioWaitWritable
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
MethodName::MoveOnAfter
|
/// Returns `true` if the method a timeout context manager.
|
||||||
| MethodName::MoveOnAt
|
pub(super) fn is_timeout_context(self) -> bool {
|
||||||
| MethodName::FailAfter
|
matches!(
|
||||||
| MethodName::FailAt
|
self,
|
||||||
| MethodName::CancelScope => false,
|
MethodName::AsyncIoTimeout
|
||||||
|
| MethodName::AsyncIoTimeoutAt
|
||||||
|
| MethodName::AnyIoMoveOnAfter
|
||||||
|
| MethodName::AnyIoFailAfter
|
||||||
|
| MethodName::AnyIoCancelScope
|
||||||
|
| MethodName::TrioMoveOnAfter
|
||||||
|
| MethodName::TrioMoveOnAt
|
||||||
|
| MethodName::TrioFailAfter
|
||||||
|
| MethodName::TrioFailAt
|
||||||
|
| MethodName::TrioCancelScope
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns associated module
|
||||||
|
pub(super) fn module(self) -> AsyncModule {
|
||||||
|
match self {
|
||||||
|
MethodName::AsyncIoTimeout | MethodName::AsyncIoTimeoutAt => AsyncModule::AsyncIo,
|
||||||
|
MethodName::AnyIoMoveOnAfter
|
||||||
|
| MethodName::AnyIoFailAfter
|
||||||
|
| MethodName::AnyIoCancelScope => AsyncModule::AnyIo,
|
||||||
|
MethodName::TrioAcloseForcefully
|
||||||
|
| MethodName::TrioCancelScope
|
||||||
|
| MethodName::TrioCancelShieldedCheckpoint
|
||||||
|
| MethodName::TrioCheckpoint
|
||||||
|
| MethodName::TrioCheckpointIfCancelled
|
||||||
|
| MethodName::TrioFailAfter
|
||||||
|
| MethodName::TrioFailAt
|
||||||
|
| MethodName::TrioMoveOnAfter
|
||||||
|
| MethodName::TrioMoveOnAt
|
||||||
|
| MethodName::TrioOpenFile
|
||||||
|
| MethodName::TrioOpenProcess
|
||||||
|
| MethodName::TrioOpenSslOverTcpListeners
|
||||||
|
| MethodName::TrioOpenSslOverTcpStream
|
||||||
|
| MethodName::TrioOpenTcpListeners
|
||||||
|
| MethodName::TrioOpenTcpStream
|
||||||
|
| MethodName::TrioOpenUnixSocket
|
||||||
|
| MethodName::TrioPermanentlyDetachCoroutineObject
|
||||||
|
| MethodName::TrioReattachDetachedCoroutineObject
|
||||||
|
| MethodName::TrioRunProcess
|
||||||
|
| MethodName::TrioServeListeners
|
||||||
|
| MethodName::TrioServeSslOverTcp
|
||||||
|
| MethodName::TrioServeTcp
|
||||||
|
| MethodName::TrioSleep
|
||||||
|
| MethodName::TrioSleepForever
|
||||||
|
| MethodName::TrioTemporarilyDetachCoroutineObject
|
||||||
|
| MethodName::TrioWaitReadable
|
||||||
|
| MethodName::TrioWaitTaskRescheduled
|
||||||
|
| MethodName::TrioWaitWritable => AsyncModule::Trio,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,42 +137,49 @@ impl MethodName {
|
||||||
impl MethodName {
|
impl MethodName {
|
||||||
pub(super) fn try_from(qualified_name: &QualifiedName<'_>) -> Option<Self> {
|
pub(super) fn try_from(qualified_name: &QualifiedName<'_>) -> Option<Self> {
|
||||||
match qualified_name.segments() {
|
match qualified_name.segments() {
|
||||||
["trio", "CancelScope"] => Some(Self::CancelScope),
|
["asyncio", "timeout"] => Some(Self::AsyncIoTimeout),
|
||||||
["trio", "aclose_forcefully"] => Some(Self::AcloseForcefully),
|
["asyncio", "timeout_at"] => Some(Self::AsyncIoTimeoutAt),
|
||||||
["trio", "fail_after"] => Some(Self::FailAfter),
|
["anyio", "move_on_after"] => Some(Self::AnyIoMoveOnAfter),
|
||||||
["trio", "fail_at"] => Some(Self::FailAt),
|
["anyio", "fail_after"] => Some(Self::AnyIoFailAfter),
|
||||||
|
["anyio", "CancelScope"] => Some(Self::AnyIoCancelScope),
|
||||||
|
["trio", "CancelScope"] => Some(Self::TrioCancelScope),
|
||||||
|
["trio", "aclose_forcefully"] => Some(Self::TrioAcloseForcefully),
|
||||||
|
["trio", "fail_after"] => Some(Self::TrioFailAfter),
|
||||||
|
["trio", "fail_at"] => Some(Self::TrioFailAt),
|
||||||
["trio", "lowlevel", "cancel_shielded_checkpoint"] => {
|
["trio", "lowlevel", "cancel_shielded_checkpoint"] => {
|
||||||
Some(Self::CancelShieldedCheckpoint)
|
Some(Self::TrioCancelShieldedCheckpoint)
|
||||||
}
|
}
|
||||||
["trio", "lowlevel", "checkpoint"] => Some(Self::Checkpoint),
|
["trio", "lowlevel", "checkpoint"] => Some(Self::TrioCheckpoint),
|
||||||
["trio", "lowlevel", "checkpoint_if_cancelled"] => Some(Self::CheckpointIfCancelled),
|
["trio", "lowlevel", "checkpoint_if_cancelled"] => {
|
||||||
["trio", "lowlevel", "open_process"] => Some(Self::OpenProcess),
|
Some(Self::TrioCheckpointIfCancelled)
|
||||||
|
}
|
||||||
|
["trio", "lowlevel", "open_process"] => Some(Self::TrioOpenProcess),
|
||||||
["trio", "lowlevel", "permanently_detach_coroutine_object"] => {
|
["trio", "lowlevel", "permanently_detach_coroutine_object"] => {
|
||||||
Some(Self::PermanentlyDetachCoroutineObject)
|
Some(Self::TrioPermanentlyDetachCoroutineObject)
|
||||||
}
|
}
|
||||||
["trio", "lowlevel", "reattach_detached_coroutine_object"] => {
|
["trio", "lowlevel", "reattach_detached_coroutine_object"] => {
|
||||||
Some(Self::ReattachDetachedCoroutineObject)
|
Some(Self::TrioReattachDetachedCoroutineObject)
|
||||||
}
|
}
|
||||||
["trio", "lowlevel", "temporarily_detach_coroutine_object"] => {
|
["trio", "lowlevel", "temporarily_detach_coroutine_object"] => {
|
||||||
Some(Self::TemporarilyDetachCoroutineObject)
|
Some(Self::TrioTemporarilyDetachCoroutineObject)
|
||||||
}
|
}
|
||||||
["trio", "lowlevel", "wait_readable"] => Some(Self::WaitReadable),
|
["trio", "lowlevel", "wait_readable"] => Some(Self::TrioWaitReadable),
|
||||||
["trio", "lowlevel", "wait_task_rescheduled"] => Some(Self::WaitTaskRescheduled),
|
["trio", "lowlevel", "wait_task_rescheduled"] => Some(Self::TrioWaitTaskRescheduled),
|
||||||
["trio", "lowlevel", "wait_writable"] => Some(Self::WaitWritable),
|
["trio", "lowlevel", "wait_writable"] => Some(Self::TrioWaitWritable),
|
||||||
["trio", "move_on_after"] => Some(Self::MoveOnAfter),
|
["trio", "move_on_after"] => Some(Self::TrioMoveOnAfter),
|
||||||
["trio", "move_on_at"] => Some(Self::MoveOnAt),
|
["trio", "move_on_at"] => Some(Self::TrioMoveOnAt),
|
||||||
["trio", "open_file"] => Some(Self::OpenFile),
|
["trio", "open_file"] => Some(Self::TrioOpenFile),
|
||||||
["trio", "open_ssl_over_tcp_listeners"] => Some(Self::OpenSslOverTcpListeners),
|
["trio", "open_ssl_over_tcp_listeners"] => Some(Self::TrioOpenSslOverTcpListeners),
|
||||||
["trio", "open_ssl_over_tcp_stream"] => Some(Self::OpenSslOverTcpStream),
|
["trio", "open_ssl_over_tcp_stream"] => Some(Self::TrioOpenSslOverTcpStream),
|
||||||
["trio", "open_tcp_listeners"] => Some(Self::OpenTcpListeners),
|
["trio", "open_tcp_listeners"] => Some(Self::TrioOpenTcpListeners),
|
||||||
["trio", "open_tcp_stream"] => Some(Self::OpenTcpStream),
|
["trio", "open_tcp_stream"] => Some(Self::TrioOpenTcpStream),
|
||||||
["trio", "open_unix_socket"] => Some(Self::OpenUnixSocket),
|
["trio", "open_unix_socket"] => Some(Self::TrioOpenUnixSocket),
|
||||||
["trio", "run_process"] => Some(Self::RunProcess),
|
["trio", "run_process"] => Some(Self::TrioRunProcess),
|
||||||
["trio", "serve_listeners"] => Some(Self::ServeListeners),
|
["trio", "serve_listeners"] => Some(Self::TrioServeListeners),
|
||||||
["trio", "serve_ssl_over_tcp"] => Some(Self::ServeSslOverTcp),
|
["trio", "serve_ssl_over_tcp"] => Some(Self::TrioServeSslOverTcp),
|
||||||
["trio", "serve_tcp"] => Some(Self::ServeTcp),
|
["trio", "serve_tcp"] => Some(Self::TrioServeTcp),
|
||||||
["trio", "sleep"] => Some(Self::Sleep),
|
["trio", "sleep"] => Some(Self::TrioSleep),
|
||||||
["trio", "sleep_forever"] => Some(Self::SleepForever),
|
["trio", "sleep_forever"] => Some(Self::TrioSleepForever),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -126,42 +188,51 @@ impl MethodName {
|
||||||
impl std::fmt::Display for MethodName {
|
impl std::fmt::Display for MethodName {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
MethodName::AcloseForcefully => write!(f, "trio.aclose_forcefully"),
|
MethodName::AsyncIoTimeout => write!(f, "asyncio.timeout"),
|
||||||
MethodName::CancelScope => write!(f, "trio.CancelScope"),
|
MethodName::AsyncIoTimeoutAt => write!(f, "asyncio.timeout_at"),
|
||||||
MethodName::CancelShieldedCheckpoint => {
|
MethodName::AnyIoMoveOnAfter => write!(f, "anyio.move_on_after"),
|
||||||
|
MethodName::AnyIoFailAfter => write!(f, "anyio.fail_after"),
|
||||||
|
MethodName::AnyIoCancelScope => write!(f, "anyio.CancelScope"),
|
||||||
|
MethodName::TrioAcloseForcefully => write!(f, "trio.aclose_forcefully"),
|
||||||
|
MethodName::TrioCancelScope => write!(f, "trio.CancelScope"),
|
||||||
|
MethodName::TrioCancelShieldedCheckpoint => {
|
||||||
write!(f, "trio.lowlevel.cancel_shielded_checkpoint")
|
write!(f, "trio.lowlevel.cancel_shielded_checkpoint")
|
||||||
}
|
}
|
||||||
MethodName::Checkpoint => write!(f, "trio.lowlevel.checkpoint"),
|
MethodName::TrioCheckpoint => write!(f, "trio.lowlevel.checkpoint"),
|
||||||
MethodName::CheckpointIfCancelled => write!(f, "trio.lowlevel.checkpoint_if_cancelled"),
|
MethodName::TrioCheckpointIfCancelled => {
|
||||||
MethodName::FailAfter => write!(f, "trio.fail_after"),
|
write!(f, "trio.lowlevel.checkpoint_if_cancelled")
|
||||||
MethodName::FailAt => write!(f, "trio.fail_at"),
|
}
|
||||||
MethodName::MoveOnAfter => write!(f, "trio.move_on_after"),
|
MethodName::TrioFailAfter => write!(f, "trio.fail_after"),
|
||||||
MethodName::MoveOnAt => write!(f, "trio.move_on_at"),
|
MethodName::TrioFailAt => write!(f, "trio.fail_at"),
|
||||||
MethodName::OpenFile => write!(f, "trio.open_file"),
|
MethodName::TrioMoveOnAfter => write!(f, "trio.move_on_after"),
|
||||||
MethodName::OpenProcess => write!(f, "trio.lowlevel.open_process"),
|
MethodName::TrioMoveOnAt => write!(f, "trio.move_on_at"),
|
||||||
MethodName::OpenSslOverTcpListeners => write!(f, "trio.open_ssl_over_tcp_listeners"),
|
MethodName::TrioOpenFile => write!(f, "trio.open_file"),
|
||||||
MethodName::OpenSslOverTcpStream => write!(f, "trio.open_ssl_over_tcp_stream"),
|
MethodName::TrioOpenProcess => write!(f, "trio.lowlevel.open_process"),
|
||||||
MethodName::OpenTcpListeners => write!(f, "trio.open_tcp_listeners"),
|
MethodName::TrioOpenSslOverTcpListeners => {
|
||||||
MethodName::OpenTcpStream => write!(f, "trio.open_tcp_stream"),
|
write!(f, "trio.open_ssl_over_tcp_listeners")
|
||||||
MethodName::OpenUnixSocket => write!(f, "trio.open_unix_socket"),
|
}
|
||||||
MethodName::PermanentlyDetachCoroutineObject => {
|
MethodName::TrioOpenSslOverTcpStream => write!(f, "trio.open_ssl_over_tcp_stream"),
|
||||||
|
MethodName::TrioOpenTcpListeners => write!(f, "trio.open_tcp_listeners"),
|
||||||
|
MethodName::TrioOpenTcpStream => write!(f, "trio.open_tcp_stream"),
|
||||||
|
MethodName::TrioOpenUnixSocket => write!(f, "trio.open_unix_socket"),
|
||||||
|
MethodName::TrioPermanentlyDetachCoroutineObject => {
|
||||||
write!(f, "trio.lowlevel.permanently_detach_coroutine_object")
|
write!(f, "trio.lowlevel.permanently_detach_coroutine_object")
|
||||||
}
|
}
|
||||||
MethodName::ReattachDetachedCoroutineObject => {
|
MethodName::TrioReattachDetachedCoroutineObject => {
|
||||||
write!(f, "trio.lowlevel.reattach_detached_coroutine_object")
|
write!(f, "trio.lowlevel.reattach_detached_coroutine_object")
|
||||||
}
|
}
|
||||||
MethodName::RunProcess => write!(f, "trio.run_process"),
|
MethodName::TrioRunProcess => write!(f, "trio.run_process"),
|
||||||
MethodName::ServeListeners => write!(f, "trio.serve_listeners"),
|
MethodName::TrioServeListeners => write!(f, "trio.serve_listeners"),
|
||||||
MethodName::ServeSslOverTcp => write!(f, "trio.serve_ssl_over_tcp"),
|
MethodName::TrioServeSslOverTcp => write!(f, "trio.serve_ssl_over_tcp"),
|
||||||
MethodName::ServeTcp => write!(f, "trio.serve_tcp"),
|
MethodName::TrioServeTcp => write!(f, "trio.serve_tcp"),
|
||||||
MethodName::Sleep => write!(f, "trio.sleep"),
|
MethodName::TrioSleep => write!(f, "trio.sleep"),
|
||||||
MethodName::SleepForever => write!(f, "trio.sleep_forever"),
|
MethodName::TrioSleepForever => write!(f, "trio.sleep_forever"),
|
||||||
MethodName::TemporarilyDetachCoroutineObject => {
|
MethodName::TrioTemporarilyDetachCoroutineObject => {
|
||||||
write!(f, "trio.lowlevel.temporarily_detach_coroutine_object")
|
write!(f, "trio.lowlevel.temporarily_detach_coroutine_object")
|
||||||
}
|
}
|
||||||
MethodName::WaitReadable => write!(f, "trio.lowlevel.wait_readable"),
|
MethodName::TrioWaitReadable => write!(f, "trio.lowlevel.wait_readable"),
|
||||||
MethodName::WaitTaskRescheduled => write!(f, "trio.lowlevel.wait_task_rescheduled"),
|
MethodName::TrioWaitTaskRescheduled => write!(f, "trio.lowlevel.wait_task_rescheduled"),
|
||||||
MethodName::WaitWritable => write!(f, "trio.lowlevel.wait_writable"),
|
MethodName::TrioWaitWritable => write!(f, "trio.lowlevel.wait_writable"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ mod tests {
|
||||||
use crate::test::test_path;
|
use crate::test::test_path;
|
||||||
use crate::{assert_messages, settings};
|
use crate::{assert_messages, settings};
|
||||||
|
|
||||||
#[test_case(Rule::TrioTimeoutWithoutAwait, Path::new("ASYNC100.py"))]
|
#[test_case(Rule::CancelScopeNoCheckpoint, Path::new("ASYNC100.py"))]
|
||||||
#[test_case(Rule::TrioSyncCall, Path::new("ASYNC105.py"))]
|
#[test_case(Rule::TrioSyncCall, Path::new("ASYNC105.py"))]
|
||||||
#[test_case(Rule::AsyncFunctionWithTimeout, Path::new("ASYNC109_0.py"))]
|
#[test_case(Rule::AsyncFunctionWithTimeout, Path::new("ASYNC109_0.py"))]
|
||||||
#[test_case(Rule::AsyncFunctionWithTimeout, Path::new("ASYNC109_1.py"))]
|
#[test_case(Rule::AsyncFunctionWithTimeout, Path::new("ASYNC109_1.py"))]
|
||||||
|
@ -38,6 +38,7 @@ mod tests {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test_case(Rule::CancelScopeNoCheckpoint, Path::new("ASYNC100.py"))]
|
||||||
#[test_case(Rule::AsyncFunctionWithTimeout, Path::new("ASYNC109_0.py"))]
|
#[test_case(Rule::AsyncFunctionWithTimeout, Path::new("ASYNC109_0.py"))]
|
||||||
#[test_case(Rule::AsyncFunctionWithTimeout, Path::new("ASYNC109_1.py"))]
|
#[test_case(Rule::AsyncFunctionWithTimeout, Path::new("ASYNC109_1.py"))]
|
||||||
fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> {
|
fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||||
|
|
|
@ -3,40 +3,44 @@ use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::helpers::AwaitVisitor;
|
use ruff_python_ast::helpers::AwaitVisitor;
|
||||||
use ruff_python_ast::visitor::Visitor;
|
use ruff_python_ast::visitor::Visitor;
|
||||||
use ruff_python_ast::{StmtWith, WithItem};
|
use ruff_python_ast::{StmtWith, WithItem};
|
||||||
use ruff_python_semantic::Modules;
|
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
use crate::rules::flake8_async::helpers::MethodName;
|
use crate::rules::flake8_async::helpers::{AsyncModule, MethodName};
|
||||||
|
use crate::settings::types::PreviewMode;
|
||||||
|
|
||||||
/// ## What it does
|
/// ## What it does
|
||||||
/// Checks for trio functions that should contain await but don't.
|
/// Checks for timeout context managers which do not contain a checkpoint.
|
||||||
///
|
///
|
||||||
/// ## Why is this bad?
|
/// ## Why is this bad?
|
||||||
/// Some trio context managers, such as `trio.fail_after` and
|
/// Some asynchronous context managers, such as `asyncio.timeout` and
|
||||||
/// `trio.move_on_after`, have no effect unless they contain an `await`
|
/// `trio.move_on_after`, have no effect unless they contain an `await`
|
||||||
/// statement. The use of such functions without an `await` statement is
|
/// statement. The use of such context managers without an `await` statement is
|
||||||
/// likely a mistake.
|
/// likely a mistake.
|
||||||
///
|
///
|
||||||
/// ## Example
|
/// ## Example
|
||||||
/// ```python
|
/// ```python
|
||||||
/// async def func():
|
/// async def func():
|
||||||
/// with trio.move_on_after(2):
|
/// with asyncio.timeout(2):
|
||||||
/// do_something()
|
/// do_something()
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// Use instead:
|
/// Use instead:
|
||||||
/// ```python
|
/// ```python
|
||||||
/// async def func():
|
/// async def func():
|
||||||
/// with trio.move_on_after(2):
|
/// with asyncio.timeout(2):
|
||||||
/// do_something()
|
/// do_something()
|
||||||
/// await awaitable()
|
/// await awaitable()
|
||||||
/// ```
|
/// ```
|
||||||
|
///
|
||||||
|
/// [`asyncio` timeouts]: https://docs.python.org/3/library/asyncio-task.html#timeouts
|
||||||
|
/// [`anyio` timeouts]: https://anyio.readthedocs.io/en/stable/cancellation.html
|
||||||
|
/// [`trio` timeouts]: https://trio.readthedocs.io/en/stable/reference-core.html#cancellation-and-timeouts
|
||||||
#[violation]
|
#[violation]
|
||||||
pub struct TrioTimeoutWithoutAwait {
|
pub struct CancelScopeNoCheckpoint {
|
||||||
method_name: MethodName,
|
method_name: MethodName,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Violation for TrioTimeoutWithoutAwait {
|
impl Violation for CancelScopeNoCheckpoint {
|
||||||
#[derive_message_formats]
|
#[derive_message_formats]
|
||||||
fn message(&self) -> String {
|
fn message(&self) -> String {
|
||||||
let Self { method_name } = self;
|
let Self { method_name } = self;
|
||||||
|
@ -45,15 +49,11 @@ impl Violation for TrioTimeoutWithoutAwait {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ASYNC100
|
/// ASYNC100
|
||||||
pub(crate) fn timeout_without_await(
|
pub(crate) fn cancel_scope_no_checkpoint(
|
||||||
checker: &mut Checker,
|
checker: &mut Checker,
|
||||||
with_stmt: &StmtWith,
|
with_stmt: &StmtWith,
|
||||||
with_items: &[WithItem],
|
with_items: &[WithItem],
|
||||||
) {
|
) {
|
||||||
if !checker.semantic().seen_module(Modules::TRIO) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let Some(method_name) = with_items.iter().find_map(|item| {
|
let Some(method_name) = with_items.iter().find_map(|item| {
|
||||||
let call = item.context_expr.as_call_expr()?;
|
let call = item.context_expr.as_call_expr()?;
|
||||||
let qualified_name = checker
|
let qualified_name = checker
|
||||||
|
@ -64,14 +64,7 @@ pub(crate) fn timeout_without_await(
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if !matches!(
|
if !method_name.is_timeout_context() {
|
||||||
method_name,
|
|
||||||
MethodName::MoveOnAfter
|
|
||||||
| MethodName::MoveOnAt
|
|
||||||
| MethodName::FailAfter
|
|
||||||
| MethodName::FailAt
|
|
||||||
| MethodName::CancelScope
|
|
||||||
) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,8 +74,17 @@ pub(crate) fn timeout_without_await(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
checker.diagnostics.push(Diagnostic::new(
|
if matches!(checker.settings.preview, PreviewMode::Disabled) {
|
||||||
TrioTimeoutWithoutAwait { method_name },
|
if matches!(method_name.module(), AsyncModule::Trio) {
|
||||||
with_stmt.range,
|
checker.diagnostics.push(Diagnostic::new(
|
||||||
));
|
CancelScopeNoCheckpoint { method_name },
|
||||||
|
with_stmt.range,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
checker.diagnostics.push(Diagnostic::new(
|
||||||
|
CancelScopeNoCheckpoint { method_name },
|
||||||
|
with_stmt.range,
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -3,9 +3,9 @@ pub(crate) use blocking_http_call::*;
|
||||||
pub(crate) use blocking_open_call::*;
|
pub(crate) use blocking_open_call::*;
|
||||||
pub(crate) use blocking_process_invocation::*;
|
pub(crate) use blocking_process_invocation::*;
|
||||||
pub(crate) use blocking_sleep::*;
|
pub(crate) use blocking_sleep::*;
|
||||||
|
pub(crate) use cancel_scope_no_checkpoint::*;
|
||||||
pub(crate) use sleep_forever_call::*;
|
pub(crate) use sleep_forever_call::*;
|
||||||
pub(crate) use sync_call::*;
|
pub(crate) use sync_call::*;
|
||||||
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::*;
|
||||||
|
|
||||||
|
@ -14,8 +14,8 @@ mod blocking_http_call;
|
||||||
mod blocking_open_call;
|
mod blocking_open_call;
|
||||||
mod blocking_process_invocation;
|
mod blocking_process_invocation;
|
||||||
mod blocking_sleep;
|
mod blocking_sleep;
|
||||||
|
mod cancel_scope_no_checkpoint;
|
||||||
mod sleep_forever_call;
|
mod sleep_forever_call;
|
||||||
mod sync_call;
|
mod sync_call;
|
||||||
mod timeout_without_await;
|
|
||||||
mod unneeded_sleep;
|
mod unneeded_sleep;
|
||||||
mod zero_sleep_call;
|
mod zero_sleep_call;
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
---
|
---
|
||||||
source: crates/ruff_linter/src/rules/flake8_async/mod.rs
|
source: crates/ruff_linter/src/rules/flake8_async/mod.rs
|
||||||
---
|
---
|
||||||
ASYNC100.py:5:5: ASYNC100 A `with trio.fail_after(...):` context does not contain any `await` statements. This makes it pointless, as the timeout can only be triggered by a checkpoint.
|
ASYNC100.py:7:5: ASYNC100 A `with trio.fail_after(...):` context does not contain any `await` statements. This makes it pointless, as the timeout can only be triggered by a checkpoint.
|
||||||
|
|
|
|
||||||
4 | async def func():
|
6 | async def func():
|
||||||
5 | with trio.fail_after():
|
7 | with trio.fail_after():
|
||||||
| _____^
|
| _____^
|
||||||
6 | | ...
|
8 | | ...
|
||||||
| |___________^ ASYNC100
|
| |___________^ ASYNC100
|
||||||
|
|
|
|
||||||
|
|
||||||
ASYNC100.py:15:5: ASYNC100 A `with trio.move_on_after(...):` context does not contain any `await` statements. This makes it pointless, as the timeout can only be triggered by a checkpoint.
|
ASYNC100.py:17:5: ASYNC100 A `with trio.move_on_after(...):` context does not contain any `await` statements. This makes it pointless, as the timeout can only be triggered by a checkpoint.
|
||||||
|
|
|
|
||||||
14 | async def func():
|
16 | async def func():
|
||||||
15 | with trio.move_on_after():
|
17 | with trio.move_on_after():
|
||||||
| _____^
|
| _____^
|
||||||
16 | | ...
|
18 | | ...
|
||||||
| |___________^ ASYNC100
|
| |___________^ ASYNC100
|
||||||
|
|
|
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff_linter/src/rules/flake8_async/mod.rs
|
||||||
|
---
|
||||||
|
ASYNC100.py:7:5: ASYNC100 A `with trio.fail_after(...):` context does not contain any `await` statements. This makes it pointless, as the timeout can only be triggered by a checkpoint.
|
||||||
|
|
|
||||||
|
6 | async def func():
|
||||||
|
7 | with trio.fail_after():
|
||||||
|
| _____^
|
||||||
|
8 | | ...
|
||||||
|
| |___________^ ASYNC100
|
||||||
|
|
|
||||||
|
|
||||||
|
ASYNC100.py:17:5: ASYNC100 A `with trio.move_on_after(...):` context does not contain any `await` statements. This makes it pointless, as the timeout can only be triggered by a checkpoint.
|
||||||
|
|
|
||||||
|
16 | async def func():
|
||||||
|
17 | with trio.move_on_after():
|
||||||
|
| _____^
|
||||||
|
18 | | ...
|
||||||
|
| |___________^ ASYNC100
|
||||||
|
|
|
||||||
|
|
||||||
|
ASYNC100.py:33:5: ASYNC100 A `with anyio.move_on_after(...):` context does not contain any `await` statements. This makes it pointless, as the timeout can only be triggered by a checkpoint.
|
||||||
|
|
|
||||||
|
32 | async def func():
|
||||||
|
33 | with anyio.move_on_after():
|
||||||
|
| _____^
|
||||||
|
34 | | ...
|
||||||
|
| |___________^ ASYNC100
|
||||||
|
|
|
||||||
|
|
||||||
|
ASYNC100.py:38:5: ASYNC100 A `with anyio.fail_after(...):` context does not contain any `await` statements. This makes it pointless, as the timeout can only be triggered by a checkpoint.
|
||||||
|
|
|
||||||
|
37 | async def func():
|
||||||
|
38 | with anyio.fail_after():
|
||||||
|
| _____^
|
||||||
|
39 | | ...
|
||||||
|
| |___________^ ASYNC100
|
||||||
|
|
|
||||||
|
|
||||||
|
ASYNC100.py:43:5: ASYNC100 A `with anyio.CancelScope(...):` context does not contain any `await` statements. This makes it pointless, as the timeout can only be triggered by a checkpoint.
|
||||||
|
|
|
||||||
|
42 | async def func():
|
||||||
|
43 | with anyio.CancelScope():
|
||||||
|
| _____^
|
||||||
|
44 | | ...
|
||||||
|
| |___________^ ASYNC100
|
||||||
|
|
|
||||||
|
|
||||||
|
ASYNC100.py:48:5: ASYNC100 A `with anyio.CancelScope(...):` context does not contain any `await` statements. This makes it pointless, as the timeout can only be triggered by a checkpoint.
|
||||||
|
|
|
||||||
|
47 | async def func():
|
||||||
|
48 | with anyio.CancelScope():
|
||||||
|
| _____^
|
||||||
|
49 | | ...
|
||||||
|
| |___________^ ASYNC100
|
||||||
|
|
|
||||||
|
|
||||||
|
ASYNC100.py:53:5: ASYNC100 A `with asyncio.timeout(...):` context does not contain any `await` statements. This makes it pointless, as the timeout can only be triggered by a checkpoint.
|
||||||
|
|
|
||||||
|
52 | async def func():
|
||||||
|
53 | with asyncio.timeout():
|
||||||
|
| _____^
|
||||||
|
54 | | ...
|
||||||
|
| |___________^ ASYNC100
|
||||||
|
|
|
||||||
|
|
||||||
|
ASYNC100.py:58:5: ASYNC100 A `with asyncio.timeout_at(...):` context does not contain any `await` statements. This makes it pointless, as the timeout can only be triggered by a checkpoint.
|
||||||
|
|
|
||||||
|
57 | async def func():
|
||||||
|
58 | with asyncio.timeout_at():
|
||||||
|
| _____^
|
||||||
|
59 | | ...
|
||||||
|
| |___________^ ASYNC100
|
||||||
|
|
|
Loading…
Add table
Add a link
Reference in a new issue