diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_async/ASYNC116.py b/crates/ruff_linter/resources/test/fixtures/flake8_async/ASYNC116.py index 2009e311479c9..5cfab2eae142b 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_async/ASYNC116.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_async/ASYNC116.py @@ -55,3 +55,56 @@ async def import_from_trio(): # catch from import await sleep(86401) # error: 116, "async" + + +async def import_anyio(): + import anyio + + # These examples are probably not meant to ever wake up: + await anyio.sleep(100000) # error: 116, "async" + + # 'inf literal' overflow trick + await anyio.sleep(1e999) # error: 116, "async" + + await anyio.sleep(86399) + await anyio.sleep(86400) + await anyio.sleep(86400.01) # error: 116, "async" + await anyio.sleep(86401) # error: 116, "async" + + await anyio.sleep(-1) # will raise a runtime error + await anyio.sleep(0) # handled by different check + + # these ones _definitely_ never wake up (TODO) + await anyio.sleep(float("inf")) + await anyio.sleep(math.inf) + await anyio.sleep(inf) + + # don't require inf to be in math (TODO) + await anyio.sleep(np.inf) + + # don't evaluate expressions (TODO) + one_day = 86401 + await anyio.sleep(86400 + 1) + await anyio.sleep(60 * 60 * 24 + 1) + await anyio.sleep(foo()) + await anyio.sleep(one_day) + await anyio.sleep(86400 + foo()) + await anyio.sleep(86400 + ...) + await anyio.sleep("hello") + await anyio.sleep(...) + + +def not_async_fun(): + import anyio + + # does not require the call to be awaited, nor in an async fun + anyio.sleep(86401) # error: 116, "async" + # also checks that we don't break visit_Call + anyio.run(anyio.sleep(86401)) # error: 116, "async" + + +async def import_from_anyio(): + from anyio import sleep + + # catch from import + await sleep(86401) # error: 116, "async" diff --git a/crates/ruff_linter/src/checkers/ast/analyze/expression.rs b/crates/ruff_linter/src/checkers/ast/analyze/expression.rs index d6a9214a2fa49..fae75a22d131d 100644 --- a/crates/ruff_linter/src/checkers/ast/analyze/expression.rs +++ b/crates/ruff_linter/src/checkers/ast/analyze/expression.rs @@ -518,8 +518,8 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) { if checker.enabled(Rule::BlockingSleepInAsyncFunction) { flake8_async::rules::blocking_sleep(checker, call); } - if checker.enabled(Rule::SleepForeverCall) { - flake8_async::rules::sleep_forever_call(checker, call); + if checker.enabled(Rule::LongSleepNotForever) { + flake8_async::rules::long_sleep_not_forever(checker, call); } if checker.any_enabled(&[Rule::Print, Rule::PPrint]) { flake8_print::rules::print_call(checker, call); diff --git a/crates/ruff_linter/src/codes.rs b/crates/ruff_linter/src/codes.rs index 64b5b453bd80e..ce31b13908671 100644 --- a/crates/ruff_linter/src/codes.rs +++ b/crates/ruff_linter/src/codes.rs @@ -298,7 +298,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { (Flake8Async, "109") => (RuleGroup::Stable, rules::flake8_async::rules::AsyncFunctionWithTimeout), (Flake8Async, "110") => (RuleGroup::Stable, rules::flake8_async::rules::AsyncBusyWait), (Flake8Async, "115") => (RuleGroup::Stable, rules::flake8_async::rules::AsyncZeroSleep), - (Flake8Async, "116") => (RuleGroup::Preview, rules::flake8_async::rules::SleepForeverCall), + (Flake8Async, "116") => (RuleGroup::Preview, rules::flake8_async::rules::LongSleepNotForever), (Flake8Async, "210") => (RuleGroup::Stable, rules::flake8_async::rules::BlockingHttpCallInAsyncFunction), (Flake8Async, "220") => (RuleGroup::Stable, rules::flake8_async::rules::CreateSubprocessInAsyncFunction), (Flake8Async, "221") => (RuleGroup::Stable, rules::flake8_async::rules::RunProcessInAsyncFunction), diff --git a/crates/ruff_linter/src/rules/flake8_async/mod.rs b/crates/ruff_linter/src/rules/flake8_async/mod.rs index 07c12792cbadd..c6a7c9012037a 100644 --- a/crates/ruff_linter/src/rules/flake8_async/mod.rs +++ b/crates/ruff_linter/src/rules/flake8_async/mod.rs @@ -21,7 +21,7 @@ mod tests { #[test_case(Rule::AsyncFunctionWithTimeout, Path::new("ASYNC109_1.py"))] #[test_case(Rule::AsyncBusyWait, Path::new("ASYNC110.py"))] #[test_case(Rule::AsyncZeroSleep, Path::new("ASYNC115.py"))] - #[test_case(Rule::SleepForeverCall, Path::new("ASYNC116.py"))] + #[test_case(Rule::LongSleepNotForever, Path::new("ASYNC116.py"))] #[test_case(Rule::BlockingHttpCallInAsyncFunction, Path::new("ASYNC210.py"))] #[test_case(Rule::CreateSubprocessInAsyncFunction, Path::new("ASYNC22x.py"))] #[test_case(Rule::RunProcessInAsyncFunction, Path::new("ASYNC22x.py"))] @@ -43,6 +43,7 @@ mod tests { #[test_case(Rule::AsyncFunctionWithTimeout, Path::new("ASYNC109_1.py"))] #[test_case(Rule::AsyncBusyWait, Path::new("ASYNC110.py"))] #[test_case(Rule::AsyncZeroSleep, Path::new("ASYNC115.py"))] + #[test_case(Rule::LongSleepNotForever, Path::new("ASYNC116.py"))] fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> { let snapshot = format!( "preview__{}_{}", diff --git a/crates/ruff_linter/src/rules/flake8_async/rules/sleep_forever_call.rs b/crates/ruff_linter/src/rules/flake8_async/rules/long_sleep_not_forever.rs similarity index 58% rename from crates/ruff_linter/src/rules/flake8_async/rules/sleep_forever_call.rs rename to crates/ruff_linter/src/rules/flake8_async/rules/long_sleep_not_forever.rs index 885bda3341f76..9af0440d48b6a 100644 --- a/crates/ruff_linter/src/rules/flake8_async/rules/sleep_forever_call.rs +++ b/crates/ruff_linter/src/rules/flake8_async/rules/long_sleep_not_forever.rs @@ -4,15 +4,17 @@ use ruff_python_ast::{Expr, ExprCall, ExprNumberLiteral, Number}; use ruff_python_semantic::Modules; use ruff_text_size::Ranged; -use crate::{checkers::ast::Checker, importer::ImportRequest}; +use crate::checkers::ast::Checker; +use crate::importer::ImportRequest; +use crate::rules::flake8_async::helpers::AsyncModule; /// ## What it does -/// Checks for uses of `trio.sleep()` with an interval greater than 24 hours. +/// Checks for uses of `trio.sleep()` or `anyio.sleep()` with a delay greater than 24 hours. /// /// ## Why is this bad? -/// `trio.sleep()` with an interval greater than 24 hours is usually intended -/// to sleep indefinitely. Instead of using a large interval, -/// `trio.sleep_forever()` better conveys the intent. +/// Calling `sleep()` with a delay greater than 24 hours is usually intended +/// to sleep indefinitely. Instead of using a large delay, +/// `trio.sleep_forever()` or `anyio.sleep_forever()` better conveys the intent. /// /// /// ## Example @@ -33,23 +35,31 @@ use crate::{checkers::ast::Checker, importer::ImportRequest}; /// await trio.sleep_forever() /// ``` #[violation] -pub struct SleepForeverCall; +pub struct LongSleepNotForever { + module: AsyncModule, +} -impl Violation for SleepForeverCall { +impl Violation for LongSleepNotForever { const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes; #[derive_message_formats] fn message(&self) -> String { - format!("`trio.sleep()` with >24 hour interval should usually be `trio.sleep_forever()`") + let Self { module } = self; + format!( + "`{module}.sleep()` with >24 hour interval should usually be `{module}.sleep_forever()`" + ) } fn fix_title(&self) -> Option { - Some(format!("Replace with `trio.sleep_forever()`")) + let Self { module } = self; + Some(format!("Replace with `{module}.sleep_forever()`")) } } /// ASYNC116 -pub(crate) fn sleep_forever_call(checker: &mut Checker, call: &ExprCall) { - if !checker.semantic().seen_module(Modules::TRIO) { +pub(crate) fn long_sleep_not_forever(checker: &mut Checker, call: &ExprCall) { + if !(checker.semantic().seen_module(Modules::TRIO) + || checker.semantic().seen_module(Modules::ANYIO)) + { return; } @@ -61,14 +71,6 @@ pub(crate) fn sleep_forever_call(checker: &mut Checker, call: &ExprCall) { return; }; - if !checker - .semantic() - .resolve_qualified_name(call.func.as_ref()) - .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["trio", "sleep"])) - { - return; - } - let Expr::NumberLiteral(ExprNumberLiteral { value, .. }) = arg else { return; }; @@ -94,11 +96,34 @@ pub(crate) fn sleep_forever_call(checker: &mut Checker, call: &ExprCall) { Number::Complex { .. } => return, } - let mut diagnostic = Diagnostic::new(SleepForeverCall, call.range()); + let Some(qualified_name) = checker + .semantic() + .resolve_qualified_name(call.func.as_ref()) + else { + return; + }; + + let Some(module) = AsyncModule::try_from(&qualified_name) else { + return; + }; + + let is_relevant_module = if checker.settings.preview.is_enabled() { + matches!(module, AsyncModule::AnyIo | AsyncModule::Trio) + } else { + matches!(module, AsyncModule::Trio) + }; + + let is_sleep = is_relevant_module && matches!(qualified_name.segments(), [_, "sleep"]); + + if !is_sleep { + return; + } + + let mut diagnostic = Diagnostic::new(LongSleepNotForever { module }, call.range()); let replacement_function = "sleep_forever"; diagnostic.try_set_fix(|| { let (import_edit, binding) = checker.importer().get_or_import_symbol( - &ImportRequest::import_from("trio", replacement_function), + &ImportRequest::import_from(&module.to_string(), replacement_function), call.func.start(), checker.semantic(), )?; diff --git a/crates/ruff_linter/src/rules/flake8_async/rules/mod.rs b/crates/ruff_linter/src/rules/flake8_async/rules/mod.rs index 6937c4e5b10ed..1b115a3c8b255 100644 --- a/crates/ruff_linter/src/rules/flake8_async/rules/mod.rs +++ b/crates/ruff_linter/src/rules/flake8_async/rules/mod.rs @@ -6,7 +6,7 @@ pub(crate) use blocking_open_call::*; pub(crate) use blocking_process_invocation::*; pub(crate) use blocking_sleep::*; pub(crate) use cancel_scope_no_checkpoint::*; -pub(crate) use sleep_forever_call::*; +pub(crate) use long_sleep_not_forever::*; pub(crate) use sync_call::*; mod async_busy_wait; @@ -17,5 +17,5 @@ mod blocking_open_call; mod blocking_process_invocation; mod blocking_sleep; mod cancel_scope_no_checkpoint; -mod sleep_forever_call; +mod long_sleep_not_forever; mod sync_call; diff --git a/crates/ruff_linter/src/rules/flake8_async/snapshots/ruff_linter__rules__flake8_async__tests__ASYNC116_ASYNC116.py.snap b/crates/ruff_linter/src/rules/flake8_async/snapshots/ruff_linter__rules__flake8_async__tests__ASYNC116_ASYNC116.py.snap index 52507511583a6..83b6209e1dfd1 100644 --- a/crates/ruff_linter/src/rules/flake8_async/snapshots/ruff_linter__rules__flake8_async__tests__ASYNC116_ASYNC116.py.snap +++ b/crates/ruff_linter/src/rules/flake8_async/snapshots/ruff_linter__rules__flake8_async__tests__ASYNC116_ASYNC116.py.snap @@ -143,3 +143,6 @@ ASYNC116.py:57:11: ASYNC116 [*] `trio.sleep()` with >24 hour interval should usu 56 57 | # catch from import 57 |- await sleep(86401) # error: 116, "async" 58 |+ await sleep_forever() # error: 116, "async" +58 59 | +59 60 | +60 61 | async def import_anyio(): diff --git a/crates/ruff_linter/src/rules/flake8_async/snapshots/ruff_linter__rules__flake8_async__tests__preview__ASYNC116_ASYNC116.py.snap b/crates/ruff_linter/src/rules/flake8_async/snapshots/ruff_linter__rules__flake8_async__tests__preview__ASYNC116_ASYNC116.py.snap new file mode 100644 index 0000000000000..3421bd0105a7d --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_async/snapshots/ruff_linter__rules__flake8_async__tests__preview__ASYNC116_ASYNC116.py.snap @@ -0,0 +1,339 @@ +--- +source: crates/ruff_linter/src/rules/flake8_async/mod.rs +--- +ASYNC116.py:11:11: ASYNC116 [*] `trio.sleep()` with >24 hour interval should usually be `trio.sleep_forever()` + | +10 | # These examples are probably not meant to ever wake up: +11 | await trio.sleep(100000) # error: 116, "async" + | ^^^^^^^^^^^^^^^^^^ ASYNC116 +12 | +13 | # 'inf literal' overflow trick + | + = help: Replace with `trio.sleep_forever()` + +ℹ Unsafe fix +8 8 | import trio +9 9 | +10 10 | # These examples are probably not meant to ever wake up: +11 |- await trio.sleep(100000) # error: 116, "async" + 11 |+ await trio.sleep_forever() # error: 116, "async" +12 12 | +13 13 | # 'inf literal' overflow trick +14 14 | await trio.sleep(1e999) # error: 116, "async" + +ASYNC116.py:14:11: ASYNC116 [*] `trio.sleep()` with >24 hour interval should usually be `trio.sleep_forever()` + | +13 | # 'inf literal' overflow trick +14 | await trio.sleep(1e999) # error: 116, "async" + | ^^^^^^^^^^^^^^^^^ ASYNC116 +15 | +16 | await trio.sleep(86399) + | + = help: Replace with `trio.sleep_forever()` + +ℹ Unsafe fix +11 11 | await trio.sleep(100000) # error: 116, "async" +12 12 | +13 13 | # 'inf literal' overflow trick +14 |- await trio.sleep(1e999) # error: 116, "async" + 14 |+ await trio.sleep_forever() # error: 116, "async" +15 15 | +16 16 | await trio.sleep(86399) +17 17 | await trio.sleep(86400) + +ASYNC116.py:18:11: ASYNC116 [*] `trio.sleep()` with >24 hour interval should usually be `trio.sleep_forever()` + | +16 | await trio.sleep(86399) +17 | await trio.sleep(86400) +18 | await trio.sleep(86400.01) # error: 116, "async" + | ^^^^^^^^^^^^^^^^^^^^ ASYNC116 +19 | await trio.sleep(86401) # error: 116, "async" + | + = help: Replace with `trio.sleep_forever()` + +ℹ Unsafe fix +15 15 | +16 16 | await trio.sleep(86399) +17 17 | await trio.sleep(86400) +18 |- await trio.sleep(86400.01) # error: 116, "async" + 18 |+ await trio.sleep_forever() # error: 116, "async" +19 19 | await trio.sleep(86401) # error: 116, "async" +20 20 | +21 21 | await trio.sleep(-1) # will raise a runtime error + +ASYNC116.py:19:11: ASYNC116 [*] `trio.sleep()` with >24 hour interval should usually be `trio.sleep_forever()` + | +17 | await trio.sleep(86400) +18 | await trio.sleep(86400.01) # error: 116, "async" +19 | await trio.sleep(86401) # error: 116, "async" + | ^^^^^^^^^^^^^^^^^ ASYNC116 +20 | +21 | await trio.sleep(-1) # will raise a runtime error + | + = help: Replace with `trio.sleep_forever()` + +ℹ Unsafe fix +16 16 | await trio.sleep(86399) +17 17 | await trio.sleep(86400) +18 18 | await trio.sleep(86400.01) # error: 116, "async" +19 |- await trio.sleep(86401) # error: 116, "async" + 19 |+ await trio.sleep_forever() # error: 116, "async" +20 20 | +21 21 | await trio.sleep(-1) # will raise a runtime error +22 22 | await trio.sleep(0) # handled by different check + +ASYNC116.py:48:5: ASYNC116 [*] `trio.sleep()` with >24 hour interval should usually be `trio.sleep_forever()` + | +47 | # does not require the call to be awaited, nor in an async fun +48 | trio.sleep(86401) # error: 116, "async" + | ^^^^^^^^^^^^^^^^^ ASYNC116 +49 | # also checks that we don't break visit_Call +50 | trio.run(trio.sleep(86401)) # error: 116, "async" + | + = help: Replace with `trio.sleep_forever()` + +ℹ Unsafe fix +45 45 | import trio +46 46 | +47 47 | # does not require the call to be awaited, nor in an async fun +48 |- trio.sleep(86401) # error: 116, "async" + 48 |+ trio.sleep_forever() # error: 116, "async" +49 49 | # also checks that we don't break visit_Call +50 50 | trio.run(trio.sleep(86401)) # error: 116, "async" +51 51 | + +ASYNC116.py:50:14: ASYNC116 [*] `trio.sleep()` with >24 hour interval should usually be `trio.sleep_forever()` + | +48 | trio.sleep(86401) # error: 116, "async" +49 | # also checks that we don't break visit_Call +50 | trio.run(trio.sleep(86401)) # error: 116, "async" + | ^^^^^^^^^^^^^^^^^ ASYNC116 + | + = help: Replace with `trio.sleep_forever()` + +ℹ Unsafe fix +47 47 | # does not require the call to be awaited, nor in an async fun +48 48 | trio.sleep(86401) # error: 116, "async" +49 49 | # also checks that we don't break visit_Call +50 |- trio.run(trio.sleep(86401)) # error: 116, "async" + 50 |+ trio.run(trio.sleep_forever()) # error: 116, "async" +51 51 | +52 52 | +53 53 | async def import_from_trio(): + +ASYNC116.py:57:11: ASYNC116 [*] `trio.sleep()` with >24 hour interval should usually be `trio.sleep_forever()` + | +56 | # catch from import +57 | await sleep(86401) # error: 116, "async" + | ^^^^^^^^^^^^ ASYNC116 + | + = help: Replace with `trio.sleep_forever()` + +ℹ Unsafe fix +2 2 | # ASYNCIO_NO_ERROR - no asyncio.sleep_forever, so check intentionally doesn't trigger. +3 3 | import math +4 4 | from math import inf + 5 |+from trio import sleep_forever +5 6 | +6 7 | +7 8 | async def import_trio(): +-------------------------------------------------------------------------------- +54 55 | from trio import sleep +55 56 | +56 57 | # catch from import +57 |- await sleep(86401) # error: 116, "async" + 58 |+ await sleep_forever() # error: 116, "async" +58 59 | +59 60 | +60 61 | async def import_anyio(): + +ASYNC116.py:64:11: ASYNC116 [*] `asyncio.sleep()` with >24 hour interval should usually be `asyncio.sleep_forever()` + | +63 | # These examples are probably not meant to ever wake up: +64 | await anyio.sleep(100000) # error: 116, "async" + | ^^^^^^^^^^^^^^^^^^^ ASYNC116 +65 | +66 | # 'inf literal' overflow trick + | + = help: Replace with `asyncio.sleep_forever()` + +ℹ Unsafe fix +2 2 | # ASYNCIO_NO_ERROR - no asyncio.sleep_forever, so check intentionally doesn't trigger. +3 3 | import math +4 4 | from math import inf + 5 |+from asyncio import sleep_forever +5 6 | +6 7 | +7 8 | async def import_trio(): +-------------------------------------------------------------------------------- +61 62 | import anyio +62 63 | +63 64 | # These examples are probably not meant to ever wake up: +64 |- await anyio.sleep(100000) # error: 116, "async" + 65 |+ await sleep_forever() # error: 116, "async" +65 66 | +66 67 | # 'inf literal' overflow trick +67 68 | await anyio.sleep(1e999) # error: 116, "async" + +ASYNC116.py:67:11: ASYNC116 [*] `asyncio.sleep()` with >24 hour interval should usually be `asyncio.sleep_forever()` + | +66 | # 'inf literal' overflow trick +67 | await anyio.sleep(1e999) # error: 116, "async" + | ^^^^^^^^^^^^^^^^^^ ASYNC116 +68 | +69 | await anyio.sleep(86399) + | + = help: Replace with `asyncio.sleep_forever()` + +ℹ Unsafe fix +2 2 | # ASYNCIO_NO_ERROR - no asyncio.sleep_forever, so check intentionally doesn't trigger. +3 3 | import math +4 4 | from math import inf + 5 |+from asyncio import sleep_forever +5 6 | +6 7 | +7 8 | async def import_trio(): +-------------------------------------------------------------------------------- +64 65 | await anyio.sleep(100000) # error: 116, "async" +65 66 | +66 67 | # 'inf literal' overflow trick +67 |- await anyio.sleep(1e999) # error: 116, "async" + 68 |+ await sleep_forever() # error: 116, "async" +68 69 | +69 70 | await anyio.sleep(86399) +70 71 | await anyio.sleep(86400) + +ASYNC116.py:71:11: ASYNC116 [*] `asyncio.sleep()` with >24 hour interval should usually be `asyncio.sleep_forever()` + | +69 | await anyio.sleep(86399) +70 | await anyio.sleep(86400) +71 | await anyio.sleep(86400.01) # error: 116, "async" + | ^^^^^^^^^^^^^^^^^^^^^ ASYNC116 +72 | await anyio.sleep(86401) # error: 116, "async" + | + = help: Replace with `asyncio.sleep_forever()` + +ℹ Unsafe fix +2 2 | # ASYNCIO_NO_ERROR - no asyncio.sleep_forever, so check intentionally doesn't trigger. +3 3 | import math +4 4 | from math import inf + 5 |+from asyncio import sleep_forever +5 6 | +6 7 | +7 8 | async def import_trio(): +-------------------------------------------------------------------------------- +68 69 | +69 70 | await anyio.sleep(86399) +70 71 | await anyio.sleep(86400) +71 |- await anyio.sleep(86400.01) # error: 116, "async" + 72 |+ await sleep_forever() # error: 116, "async" +72 73 | await anyio.sleep(86401) # error: 116, "async" +73 74 | +74 75 | await anyio.sleep(-1) # will raise a runtime error + +ASYNC116.py:72:11: ASYNC116 [*] `asyncio.sleep()` with >24 hour interval should usually be `asyncio.sleep_forever()` + | +70 | await anyio.sleep(86400) +71 | await anyio.sleep(86400.01) # error: 116, "async" +72 | await anyio.sleep(86401) # error: 116, "async" + | ^^^^^^^^^^^^^^^^^^ ASYNC116 +73 | +74 | await anyio.sleep(-1) # will raise a runtime error + | + = help: Replace with `asyncio.sleep_forever()` + +ℹ Unsafe fix +2 2 | # ASYNCIO_NO_ERROR - no asyncio.sleep_forever, so check intentionally doesn't trigger. +3 3 | import math +4 4 | from math import inf + 5 |+from asyncio import sleep_forever +5 6 | +6 7 | +7 8 | async def import_trio(): +-------------------------------------------------------------------------------- +69 70 | await anyio.sleep(86399) +70 71 | await anyio.sleep(86400) +71 72 | await anyio.sleep(86400.01) # error: 116, "async" +72 |- await anyio.sleep(86401) # error: 116, "async" + 73 |+ await sleep_forever() # error: 116, "async" +73 74 | +74 75 | await anyio.sleep(-1) # will raise a runtime error +75 76 | await anyio.sleep(0) # handled by different check + +ASYNC116.py:101:5: ASYNC116 [*] `asyncio.sleep()` with >24 hour interval should usually be `asyncio.sleep_forever()` + | +100 | # does not require the call to be awaited, nor in an async fun +101 | anyio.sleep(86401) # error: 116, "async" + | ^^^^^^^^^^^^^^^^^^ ASYNC116 +102 | # also checks that we don't break visit_Call +103 | anyio.run(anyio.sleep(86401)) # error: 116, "async" + | + = help: Replace with `asyncio.sleep_forever()` + +ℹ Unsafe fix +2 2 | # ASYNCIO_NO_ERROR - no asyncio.sleep_forever, so check intentionally doesn't trigger. +3 3 | import math +4 4 | from math import inf + 5 |+from asyncio import sleep_forever +5 6 | +6 7 | +7 8 | async def import_trio(): +-------------------------------------------------------------------------------- +98 99 | import anyio +99 100 | +100 101 | # does not require the call to be awaited, nor in an async fun +101 |- anyio.sleep(86401) # error: 116, "async" + 102 |+ sleep_forever() # error: 116, "async" +102 103 | # also checks that we don't break visit_Call +103 104 | anyio.run(anyio.sleep(86401)) # error: 116, "async" +104 105 | + +ASYNC116.py:103:15: ASYNC116 [*] `asyncio.sleep()` with >24 hour interval should usually be `asyncio.sleep_forever()` + | +101 | anyio.sleep(86401) # error: 116, "async" +102 | # also checks that we don't break visit_Call +103 | anyio.run(anyio.sleep(86401)) # error: 116, "async" + | ^^^^^^^^^^^^^^^^^^ ASYNC116 + | + = help: Replace with `asyncio.sleep_forever()` + +ℹ Unsafe fix +2 2 | # ASYNCIO_NO_ERROR - no asyncio.sleep_forever, so check intentionally doesn't trigger. +3 3 | import math +4 4 | from math import inf + 5 |+from asyncio import sleep_forever +5 6 | +6 7 | +7 8 | async def import_trio(): +-------------------------------------------------------------------------------- +100 101 | # does not require the call to be awaited, nor in an async fun +101 102 | anyio.sleep(86401) # error: 116, "async" +102 103 | # also checks that we don't break visit_Call +103 |- anyio.run(anyio.sleep(86401)) # error: 116, "async" + 104 |+ anyio.run(sleep_forever()) # error: 116, "async" +104 105 | +105 106 | +106 107 | async def import_from_anyio(): + +ASYNC116.py:110:11: ASYNC116 [*] `asyncio.sleep()` with >24 hour interval should usually be `asyncio.sleep_forever()` + | +109 | # catch from import +110 | await sleep(86401) # error: 116, "async" + | ^^^^^^^^^^^^ ASYNC116 + | + = help: Replace with `asyncio.sleep_forever()` + +ℹ Unsafe fix +2 2 | # ASYNCIO_NO_ERROR - no asyncio.sleep_forever, so check intentionally doesn't trigger. +3 3 | import math +4 4 | from math import inf + 5 |+from asyncio import sleep_forever +5 6 | +6 7 | +7 8 | async def import_trio(): +-------------------------------------------------------------------------------- +107 108 | from anyio import sleep +108 109 | +109 110 | # catch from import +110 |- await sleep(86401) # error: 116, "async" + 111 |+ await sleep_forever() # error: 116, "async"