diff --git a/README.md b/README.md index 854d16a150..ed891050da 100644 --- a/README.md +++ b/README.md @@ -334,7 +334,6 @@ quality tools, including: - [flake8-super](https://pypi.org/project/flake8-super/) - [flake8-tidy-imports](https://pypi.org/project/flake8-tidy-imports/) - [flake8-todos](https://pypi.org/project/flake8-todos/) -- [flake8-trio](https://pypi.org/project/flake8-trio/) - [flake8-type-checking](https://pypi.org/project/flake8-type-checking/) - [flake8-use-pathlib](https://pypi.org/project/flake8-use-pathlib/) - [flynt](https://pypi.org/project/flynt/) ([#2102](https://github.com/astral-sh/ruff/issues/2102)) diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_async/ASYNC100.py b/crates/ruff_linter/resources/test/fixtures/flake8_async/ASYNC100.py index 532273a7b4..4499657cc2 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_async/ASYNC100.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_async/ASYNC100.py @@ -1,23 +1,27 @@ -import urllib.request -import requests -import httpx +import trio -async def foo(): - urllib.request.urlopen("http://example.com/foo/bar").read() +async def func(): + with trio.fail_after(): + ... -async def foo(): - requests.get() +async def func(): + with trio.fail_at(): + await ... -async def foo(): - httpx.get() +async def func(): + with trio.move_on_after(): + ... -async def foo(): - requests.post() +async def func(): + with trio.move_at(): + await ... -async def foo(): - httpx.post() +async def func(): + with trio.move_at(): + async with trio.open_nursery() as nursery: + ... diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_async/ASYNC102.py b/crates/ruff_linter/resources/test/fixtures/flake8_async/ASYNC102.py deleted file mode 100644 index 7912bcc9de..0000000000 --- a/crates/ruff_linter/resources/test/fixtures/flake8_async/ASYNC102.py +++ /dev/null @@ -1,13 +0,0 @@ -import os - - -async def foo(): - os.popen() - - -async def foo(): - os.spawnl() - - -async def foo(): - os.fspath("foo") diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_trio/TRIO105.py b/crates/ruff_linter/resources/test/fixtures/flake8_async/ASYNC105.py similarity index 95% rename from crates/ruff_linter/resources/test/fixtures/flake8_trio/TRIO105.py rename to crates/ruff_linter/resources/test/fixtures/flake8_async/ASYNC105.py index 4668d114c9..69dafa01e6 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_trio/TRIO105.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_async/ASYNC105.py @@ -26,7 +26,7 @@ async def func() -> None: await trio.lowlevel.wait_task_rescheduled(foo) await trio.lowlevel.wait_writable(foo) - # TRIO105 + # ASYNC105 trio.aclose_forcefully(foo) trio.open_file(foo) trio.open_ssl_over_tcp_listeners(foo, foo) @@ -55,10 +55,10 @@ async def func() -> None: async with await trio.open_file(foo): # Ok pass - async with trio.open_file(foo): # TRIO105 + async with trio.open_file(foo): # ASYNC105 pass def func() -> None: - # TRIO105 (without fix) + # ASYNC105 (without fix) trio.open_file(foo) diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_trio/TRIO109.py b/crates/ruff_linter/resources/test/fixtures/flake8_async/ASYNC109.py similarity index 100% rename from crates/ruff_linter/resources/test/fixtures/flake8_trio/TRIO109.py rename to crates/ruff_linter/resources/test/fixtures/flake8_async/ASYNC109.py diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_trio/TRIO110.py b/crates/ruff_linter/resources/test/fixtures/flake8_async/ASYNC110.py similarity index 100% rename from crates/ruff_linter/resources/test/fixtures/flake8_trio/TRIO110.py rename to crates/ruff_linter/resources/test/fixtures/flake8_async/ASYNC110.py diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_trio/TRIO115.py b/crates/ruff_linter/resources/test/fixtures/flake8_async/ASYNC115.py similarity index 85% rename from crates/ruff_linter/resources/test/fixtures/flake8_trio/TRIO115.py rename to crates/ruff_linter/resources/test/fixtures/flake8_async/ASYNC115.py index bd89567dc1..fd4f42d156 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_trio/TRIO115.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_async/ASYNC115.py @@ -2,19 +2,19 @@ async def func(): import trio from trio import sleep - await trio.sleep(0) # TRIO115 + await trio.sleep(0) # ASYNC115 await trio.sleep(1) # OK await trio.sleep(0, 1) # OK await trio.sleep(...) # OK await trio.sleep() # OK - trio.sleep(0) # TRIO115 + trio.sleep(0) # ASYNC115 foo = 0 trio.sleep(foo) # OK trio.sleep(1) # OK time.sleep(0) # OK - sleep(0) # TRIO115 + sleep(0) # ASYNC115 bar = "bar" trio.sleep(bar) @@ -45,18 +45,18 @@ async def func(): def func(): import trio - trio.run(trio.sleep(0)) # TRIO115 + trio.run(trio.sleep(0)) # ASYNC115 from trio import Event, sleep def func(): - sleep(0) # TRIO115 + sleep(0) # ASYNC115 async def func(): - await sleep(seconds=0) # TRIO115 + await sleep(seconds=0) # ASYNC115 def func(): diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_async/ASYNC210.py b/crates/ruff_linter/resources/test/fixtures/flake8_async/ASYNC210.py new file mode 100644 index 0000000000..4a006e2ca3 --- /dev/null +++ b/crates/ruff_linter/resources/test/fixtures/flake8_async/ASYNC210.py @@ -0,0 +1,69 @@ +import urllib +import requests +import httpx +import urllib3 + + +async def foo(): + urllib.request.urlopen("http://example.com/foo/bar").read() # ASYNC210 + + +async def foo(): + requests.get() # ASYNC210 + + +async def foo(): + httpx.get() # ASYNC210 + + +async def foo(): + requests.post() # ASYNC210 + + +async def foo(): + httpx.post() # ASYNC210 + + +async def foo(): + requests.get() # ASYNC210 + requests.get(...) # ASYNC210 + requests.get # Ok + print(requests.get()) # ASYNC210 + print(requests.get(requests.get())) # ASYNC210 + + requests.options() # ASYNC210 + requests.head() # ASYNC210 + requests.post() # ASYNC210 + requests.put() # ASYNC210 + requests.patch() # ASYNC210 + requests.delete() # ASYNC210 + requests.foo() + + httpx.options("") # ASYNC210 + httpx.head("") # ASYNC210 + httpx.post("") # ASYNC210 + httpx.put("") # ASYNC210 + httpx.patch("") # ASYNC210 + httpx.delete("") # ASYNC210 + httpx.foo() # Ok + + urllib3.request() # ASYNC210 + urllib3.request(...) # ASYNC210 + + urllib.request.urlopen("") # ASYNC210 + + r = {} + r.get("not a sync http client") # Ok + + +async def bar(): + + def request(): + pass + + request() # Ok + + def urlopen(): + pass + + urlopen() # Ok diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_async/ASYNC22x.py b/crates/ruff_linter/resources/test/fixtures/flake8_async/ASYNC22x.py new file mode 100644 index 0000000000..580e4439d0 --- /dev/null +++ b/crates/ruff_linter/resources/test/fixtures/flake8_async/ASYNC22x.py @@ -0,0 +1,98 @@ +import os +import subprocess + +# Violation cases: + + +async def func(): + subprocess.run("foo") # ASYNC221 + + +async def func(): + subprocess.call("foo") # ASYNC221 + + +async def func(): + subprocess.foo(0) # OK + + +async def func(): + os.wait4(10) # ASYNC222 + + +async def func(): + os.wait(12) # ASYNC222 + + +async def foo(): + await async_fun( + subprocess.getoutput() # ASYNC221 + ) + subprocess.Popen() # ASYNC220 + os.system() # ASYNC221 + + system() + os.system.anything() + os.anything() + + subprocess.run() # ASYNC221 + subprocess.call() # ASYNC221 + subprocess.check_call() # ASYNC221 + subprocess.check_output() # ASYNC221 + subprocess.getoutput() # ASYNC221 + subprocess.getstatusoutput() # ASYNC221 + + await async_fun( + subprocess.getoutput() # ASYNC221 + ) + + subprocess.anything() + subprocess.foo() + subprocess.bar.foo() + subprocess() + + os.posix_spawn() # ASYNC221 + os.posix_spawnp() # ASYNC221 + + os.spawn() + os.spawn + os.spawnllll() + + os.spawnl() # ASYNC221 + os.spawnle() # ASYNC221 + os.spawnlp() # ASYNC221 + os.spawnlpe() # ASYNC221 + os.spawnv() # ASYNC221 + os.spawnve() # ASYNC221 + os.spawnvp() # ASYNC221 + os.spawnvpe() # ASYNC221 + + P_NOWAIT = os.P_NOWAIT + + # if mode is given, and is not os.P_WAIT: ASYNC220 + os.spawnl(os.P_NOWAIT) # ASYNC220 + os.spawnl(P_NOWAIT) # ASYNC220 + os.spawnl(mode=os.P_NOWAIT) # ASYNC220 + os.spawnl(mode=P_NOWAIT) # ASYNC220 + + P_WAIT = os.P_WAIT + + # if it is P_WAIT, ASYNC221 + os.spawnl(P_WAIT) # ASYNC221 + os.spawnl(mode=os.P_WAIT) # ASYNC221 + os.spawnl(mode=P_WAIT) # ASYNC221 + + # other weird cases: ASYNC220 + os.spawnl(0) # ASYNC220 + os.spawnl(1) # ASYNC220 + os.spawnl(foo()) # ASYNC220 + + # ASYNC222 + os.wait() # ASYNC222 + os.wait3() # ASYNC222 + os.wait4() # ASYNC222 + os.waitid() # ASYNC222 + os.waitpid() # ASYNC222 + + os.waitpi() + os.waiti() diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_async/ASYNC101.py b/crates/ruff_linter/resources/test/fixtures/flake8_async/ASYNC230.py similarity index 53% rename from crates/ruff_linter/resources/test/fixtures/flake8_async/ASYNC101.py rename to crates/ruff_linter/resources/test/fixtures/flake8_async/ASYNC230.py index 4c4f78bd45..e38dc8df43 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_async/ASYNC101.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_async/ASYNC230.py @@ -1,53 +1,48 @@ -import os -import subprocess -import time +import io from pathlib import Path + +async def foo(): + open("") # ASYNC230 + io.open_code("") # ASYNC230 + + with open(""): # ASYNC230 + ... + + with open("") as f: # ASYNC230 + ... + + with foo(), open(""): # ASYNC230 + ... + + async with open(""): # ASYNC230 + ... + + +def foo_sync(): + open("") + # Violation cases: async def func(): - open("foo") - - -async def func(): - time.sleep(1) - - -async def func(): - subprocess.run("foo") - - -async def func(): - subprocess.call("foo") - - -async def func(): - subprocess.foo(0) - - -async def func(): - os.wait4(10) - - -async def func(): - os.wait(12) + open("foo") # ASYNC230 # Violation cases for pathlib: async def func(): - Path("foo").open() # ASYNC101 + Path("foo").open() # ASYNC230 async def func(): p = Path("foo") - p.open() # ASYNC101 + p.open() # ASYNC230 async def func(): - with Path("foo").open() as f: # ASYNC101 + with Path("foo").open() as f: # ASYNC230 pass @@ -55,13 +50,13 @@ async def func() -> None: p = Path("foo") async def bar(): - p.open() # ASYNC101 + p.open() # ASYNC230 async def func() -> None: (p1, p2) = (Path("foo"), Path("bar")) - p1.open() # ASYNC101 + p1.open() # ASYNC230 # Non-violation cases for pathlib: diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_async/ASYNC251.py b/crates/ruff_linter/resources/test/fixtures/flake8_async/ASYNC251.py new file mode 100644 index 0000000000..adc93ff8e3 --- /dev/null +++ b/crates/ruff_linter/resources/test/fixtures/flake8_async/ASYNC251.py @@ -0,0 +1,14 @@ +import time +import asyncio + + +async def func(): + time.sleep(1) # ASYNC251 + + +def func(): + time.sleep(1) # OK + + +async def func(): + asyncio.sleep(1) # OK diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_trio/TRIO100.py b/crates/ruff_linter/resources/test/fixtures/flake8_trio/TRIO100.py deleted file mode 100644 index 4499657cc2..0000000000 --- a/crates/ruff_linter/resources/test/fixtures/flake8_trio/TRIO100.py +++ /dev/null @@ -1,27 +0,0 @@ -import trio - - -async def func(): - with trio.fail_after(): - ... - - -async def func(): - with trio.fail_at(): - await ... - - -async def func(): - with trio.move_on_after(): - ... - - -async def func(): - with trio.move_at(): - await ... - - -async def func(): - with trio.move_at(): - async with trio.open_nursery() as nursery: - ... diff --git a/crates/ruff_linter/src/checkers/ast/analyze/expression.rs b/crates/ruff_linter/src/checkers/ast/analyze/expression.rs index 3b23e7cae9..a86fdc2bb1 100644 --- a/crates/ruff_linter/src/checkers/ast/analyze/expression.rs +++ b/crates/ruff_linter/src/checkers/ast/analyze/expression.rs @@ -15,8 +15,8 @@ use crate::rules::{ flake8_comprehensions, flake8_datetimez, flake8_debugger, flake8_django, flake8_future_annotations, flake8_gettext, flake8_implicit_str_concat, flake8_logging, flake8_logging_format, flake8_pie, flake8_print, flake8_pyi, flake8_pytest_style, flake8_self, - flake8_simplify, flake8_tidy_imports, flake8_trio, flake8_type_checking, flake8_use_pathlib, - flynt, numpy, pandas_vet, pep8_naming, pycodestyle, pyflakes, pylint, pyupgrade, refurb, ruff, + flake8_simplify, flake8_tidy_imports, flake8_type_checking, flake8_use_pathlib, flynt, numpy, + pandas_vet, pep8_naming, pycodestyle, pyflakes, pylint, pyupgrade, refurb, ruff, }; use crate::settings::types::PythonVersion; @@ -505,11 +505,18 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) { if checker.enabled(Rule::BlockingHttpCallInAsyncFunction) { flake8_async::rules::blocking_http_call(checker, call); } - if checker.enabled(Rule::OpenSleepOrSubprocessInAsyncFunction) { - flake8_async::rules::open_sleep_or_subprocess_call(checker, call); + if checker.enabled(Rule::BlockingOpenCallInAsyncFunction) { + flake8_async::rules::blocking_open_call(checker, call); } - if checker.enabled(Rule::BlockingOsCallInAsyncFunction) { - flake8_async::rules::blocking_os_call(checker, call); + if checker.any_enabled(&[ + Rule::CreateSubprocessInAsyncFunction, + Rule::RunProcessInAsyncFunction, + Rule::WaitForProcessInAsyncFunction, + ]) { + flake8_async::rules::blocking_process_invocation(checker, call); + } + 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); @@ -963,10 +970,10 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) { refurb::rules::no_implicit_cwd(checker, call); } if checker.enabled(Rule::TrioSyncCall) { - flake8_trio::rules::sync_call(checker, call); + flake8_async::rules::sync_call(checker, call); } if checker.enabled(Rule::TrioZeroSleepCall) { - flake8_trio::rules::zero_sleep_call(checker, call); + flake8_async::rules::zero_sleep_call(checker, call); } if checker.enabled(Rule::UnnecessaryDunderCall) { pylint::rules::unnecessary_dunder_call(checker, call); diff --git a/crates/ruff_linter/src/checkers/ast/analyze/statement.rs b/crates/ruff_linter/src/checkers/ast/analyze/statement.rs index fdb27a664c..030cfe2449 100644 --- a/crates/ruff_linter/src/checkers/ast/analyze/statement.rs +++ b/crates/ruff_linter/src/checkers/ast/analyze/statement.rs @@ -8,11 +8,11 @@ use ruff_text_size::Ranged; use crate::checkers::ast::Checker; use crate::registry::Rule; use crate::rules::{ - airflow, flake8_bandit, flake8_boolean_trap, flake8_bugbear, flake8_builtins, flake8_debugger, - flake8_django, flake8_errmsg, flake8_import_conventions, flake8_pie, flake8_pyi, - flake8_pytest_style, flake8_raise, flake8_return, flake8_simplify, flake8_slots, - flake8_tidy_imports, flake8_trio, flake8_type_checking, mccabe, pandas_vet, pep8_naming, - perflint, pycodestyle, pyflakes, pygrep_hooks, pylint, pyupgrade, refurb, ruff, tryceratops, + airflow, flake8_async, flake8_bandit, flake8_boolean_trap, flake8_bugbear, flake8_builtins, + flake8_debugger, flake8_django, flake8_errmsg, flake8_import_conventions, flake8_pie, + flake8_pyi, flake8_pytest_style, flake8_raise, flake8_return, flake8_simplify, flake8_slots, + flake8_tidy_imports, flake8_type_checking, mccabe, pandas_vet, pep8_naming, perflint, + pycodestyle, pyflakes, pygrep_hooks, pylint, pyupgrade, refurb, ruff, tryceratops, }; use crate::settings::types::PythonVersion; @@ -357,7 +357,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { } } if checker.enabled(Rule::TrioAsyncFunctionWithTimeout) { - flake8_trio::rules::async_function_with_timeout(checker, function_def); + flake8_async::rules::async_function_with_timeout(checker, function_def); } if checker.enabled(Rule::ReimplementedOperator) { refurb::rules::reimplemented_operator(checker, &function_def.into()); @@ -1303,7 +1303,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { pylint::rules::useless_with_lock(checker, with_stmt); } if checker.enabled(Rule::TrioTimeoutWithoutAwait) { - flake8_trio::rules::timeout_without_await(checker, with_stmt, items); + flake8_async::rules::timeout_without_await(checker, with_stmt, items); } } Stmt::While(while_stmt @ ast::StmtWhile { body, orelse, .. }) => { @@ -1320,7 +1320,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { perflint::rules::try_except_in_loop(checker, body); } if checker.enabled(Rule::TrioUnneededSleep) { - flake8_trio::rules::unneeded_sleep(checker, while_stmt); + flake8_async::rules::unneeded_sleep(checker, while_stmt); } } Stmt::For( diff --git a/crates/ruff_linter/src/codes.rs b/crates/ruff_linter/src/codes.rs index 3274a4cdb5..61e81adb37 100644 --- a/crates/ruff_linter/src/codes.rs +++ b/crates/ruff_linter/src/codes.rs @@ -292,17 +292,18 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { (Pylint, "W3301") => (RuleGroup::Stable, rules::pylint::rules::NestedMinMax), // flake8-async - (Flake8Async, "100") => (RuleGroup::Stable, rules::flake8_async::rules::BlockingHttpCallInAsyncFunction), - (Flake8Async, "101") => (RuleGroup::Stable, rules::flake8_async::rules::OpenSleepOrSubprocessInAsyncFunction), - (Flake8Async, "102") => (RuleGroup::Stable, rules::flake8_async::rules::BlockingOsCallInAsyncFunction), + (Flake8Async, "100") => (RuleGroup::Stable, rules::flake8_async::rules::TrioTimeoutWithoutAwait), + (Flake8Async, "105") => (RuleGroup::Stable, rules::flake8_async::rules::TrioSyncCall), + (Flake8Async, "109") => (RuleGroup::Stable, rules::flake8_async::rules::TrioAsyncFunctionWithTimeout), + (Flake8Async, "110") => (RuleGroup::Stable, rules::flake8_async::rules::TrioUnneededSleep), + (Flake8Async, "115") => (RuleGroup::Stable, rules::flake8_async::rules::TrioZeroSleepCall), (Flake8Async, "116") => (RuleGroup::Preview, rules::flake8_async::rules::SleepForeverCall), - - // flake8-trio - (Flake8Trio, "100") => (RuleGroup::Stable, rules::flake8_trio::rules::TrioTimeoutWithoutAwait), - (Flake8Trio, "105") => (RuleGroup::Stable, rules::flake8_trio::rules::TrioSyncCall), - (Flake8Trio, "109") => (RuleGroup::Stable, rules::flake8_trio::rules::TrioAsyncFunctionWithTimeout), - (Flake8Trio, "110") => (RuleGroup::Stable, rules::flake8_trio::rules::TrioUnneededSleep), - (Flake8Trio, "115") => (RuleGroup::Stable, rules::flake8_trio::rules::TrioZeroSleepCall), + (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), + (Flake8Async, "222") => (RuleGroup::Stable, rules::flake8_async::rules::WaitForProcessInAsyncFunction), + (Flake8Async, "230") => (RuleGroup::Stable, rules::flake8_async::rules::BlockingOpenCallInAsyncFunction), + (Flake8Async, "251") => (RuleGroup::Stable, rules::flake8_async::rules::BlockingSleepInAsyncFunction), // flake8-builtins (Flake8Builtins, "001") => (RuleGroup::Stable, rules::flake8_builtins::rules::BuiltinVariableShadowing), diff --git a/crates/ruff_linter/src/registry.rs b/crates/ruff_linter/src/registry.rs index fa6bdee458..6cb5b39c92 100644 --- a/crates/ruff_linter/src/registry.rs +++ b/crates/ruff_linter/src/registry.rs @@ -64,9 +64,6 @@ pub enum Linter { /// [flake8-async](https://pypi.org/project/flake8-async/) #[prefix = "ASYNC"] Flake8Async, - /// [flake8-trio](https://pypi.org/project/flake8-trio/) - #[prefix = "TRIO"] - Flake8Trio, /// [flake8-bandit](https://pypi.org/project/flake8-bandit/) #[prefix = "S"] Flake8Bandit, diff --git a/crates/ruff_linter/src/rule_redirects.rs b/crates/ruff_linter/src/rule_redirects.rs index 4e74fca893..2f174e80d0 100644 --- a/crates/ruff_linter/src/rule_redirects.rs +++ b/crates/ruff_linter/src/rule_redirects.rs @@ -103,6 +103,16 @@ static REDIRECTS: Lazy> = Lazy::new(|| { ("TRY200", "B904"), ("PGH001", "S307"), ("PGH002", "G010"), + // flake8-trio and flake8-async merged with name flake8-async + ("TRIO", "ASYNC1"), + ("TRIO1", "ASYNC1"), + ("TRIO10", "ASYNC10"), + ("TRIO100", "ASYNC100"), + ("TRIO105", "ASYNC105"), + ("TRIO109", "ASYNC109"), + ("TRIO11", "ASYNC11"), + ("TRIO110", "ASYNC110"), + ("TRIO115", "ASYNC115"), // Removed in v0.5 ("PLR1701", "SIM101"), // Test redirect by exact code diff --git a/crates/ruff_linter/src/rules/flake8_trio/method_name.rs b/crates/ruff_linter/src/rules/flake8_async/helpers.rs similarity index 100% rename from crates/ruff_linter/src/rules/flake8_trio/method_name.rs rename to crates/ruff_linter/src/rules/flake8_async/helpers.rs diff --git a/crates/ruff_linter/src/rules/flake8_async/mod.rs b/crates/ruff_linter/src/rules/flake8_async/mod.rs index dfbf3dab1f..7009204247 100644 --- a/crates/ruff_linter/src/rules/flake8_async/mod.rs +++ b/crates/ruff_linter/src/rules/flake8_async/mod.rs @@ -1,4 +1,5 @@ //! Rules from [flake8-async](https://pypi.org/project/flake8-async/). +mod helpers; pub(crate) mod rules; #[cfg(test)] @@ -13,10 +14,18 @@ mod tests { use crate::settings::LinterSettings; use crate::test::test_path; - #[test_case(Rule::BlockingHttpCallInAsyncFunction, Path::new("ASYNC100.py"))] - #[test_case(Rule::OpenSleepOrSubprocessInAsyncFunction, Path::new("ASYNC101.py"))] - #[test_case(Rule::BlockingOsCallInAsyncFunction, Path::new("ASYNC102.py"))] + #[test_case(Rule::TrioTimeoutWithoutAwait, Path::new("ASYNC100.py"))] + #[test_case(Rule::TrioSyncCall, Path::new("ASYNC105.py"))] + #[test_case(Rule::TrioAsyncFunctionWithTimeout, Path::new("ASYNC109.py"))] + #[test_case(Rule::TrioUnneededSleep, Path::new("ASYNC110.py"))] + #[test_case(Rule::TrioZeroSleepCall, Path::new("ASYNC115.py"))] #[test_case(Rule::SleepForeverCall, 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"))] + #[test_case(Rule::WaitForProcessInAsyncFunction, Path::new("ASYNC22x.py"))] + #[test_case(Rule::BlockingOpenCallInAsyncFunction, Path::new("ASYNC230.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()); let diagnostics = test_path( diff --git a/crates/ruff_linter/src/rules/flake8_trio/rules/async_function_with_timeout.rs b/crates/ruff_linter/src/rules/flake8_async/rules/async_function_with_timeout.rs similarity index 99% rename from crates/ruff_linter/src/rules/flake8_trio/rules/async_function_with_timeout.rs rename to crates/ruff_linter/src/rules/flake8_async/rules/async_function_with_timeout.rs index c73c51acca..3b7ae6f738 100644 --- a/crates/ruff_linter/src/rules/flake8_trio/rules/async_function_with_timeout.rs +++ b/crates/ruff_linter/src/rules/flake8_async/rules/async_function_with_timeout.rs @@ -40,7 +40,7 @@ impl Violation for TrioAsyncFunctionWithTimeout { } } -/// TRIO109 +/// ASYNC109 pub(crate) fn async_function_with_timeout( checker: &mut Checker, function_def: &ast::StmtFunctionDef, diff --git a/crates/ruff_linter/src/rules/flake8_async/rules/blocking_http_call.rs b/crates/ruff_linter/src/rules/flake8_async/rules/blocking_http_call.rs index 1318d4cbd7..dc80120217 100644 --- a/crates/ruff_linter/src/rules/flake8_async/rules/blocking_http_call.rs +++ b/crates/ruff_linter/src/rules/flake8_async/rules/blocking_http_call.rs @@ -45,6 +45,7 @@ fn is_blocking_http_call(qualified_name: &QualifiedName) -> bool { matches!( qualified_name.segments(), ["urllib", "request", "urlopen"] + | ["urllib3", "request"] | [ "httpx" | "requests", "get" @@ -60,7 +61,7 @@ fn is_blocking_http_call(qualified_name: &QualifiedName) -> bool { ) } -/// ASYNC100 +/// ASYNC210 pub(crate) fn blocking_http_call(checker: &mut Checker, call: &ExprCall) { if checker.semantic().in_async_context() { if checker diff --git a/crates/ruff_linter/src/rules/flake8_async/rules/open_sleep_or_subprocess_call.rs b/crates/ruff_linter/src/rules/flake8_async/rules/blocking_open_call.rs similarity index 67% rename from crates/ruff_linter/src/rules/flake8_async/rules/open_sleep_or_subprocess_call.rs rename to crates/ruff_linter/src/rules/flake8_async/rules/blocking_open_call.rs index 389a39d370..bfdbbae473 100644 --- a/crates/ruff_linter/src/rules/flake8_async/rules/open_sleep_or_subprocess_call.rs +++ b/crates/ruff_linter/src/rules/flake8_async/rules/blocking_open_call.rs @@ -7,8 +7,7 @@ use ruff_text_size::Ranged; use crate::checkers::ast::Checker; /// ## What it does -/// Checks that async functions do not contain calls to `open`, `time.sleep`, -/// or `subprocess` methods. +/// Checks that async functions do not open files with blocking methods like `open`. /// /// ## Why is this bad? /// Blocking an async function via a blocking call will block the entire @@ -21,61 +20,53 @@ use crate::checkers::ast::Checker; /// ## Example /// ```python /// async def foo(): -/// time.sleep(1000) +/// with open("bar.txt") as f: +/// contents = f.read() /// ``` /// /// Use instead: /// ```python +/// import anyio +/// +/// /// async def foo(): -/// await asyncio.sleep(1000) +/// async with await anyio.open_file("bar.txt") as f: +/// contents = await f.read() /// ``` #[violation] -pub struct OpenSleepOrSubprocessInAsyncFunction; +pub struct BlockingOpenCallInAsyncFunction; -impl Violation for OpenSleepOrSubprocessInAsyncFunction { +impl Violation for BlockingOpenCallInAsyncFunction { #[derive_message_formats] fn message(&self) -> String { - format!("Async functions should not call `open`, `time.sleep`, or `subprocess` methods") + format!("Async functions should not open files with blocking methods like `open`") } } -/// ASYNC101 -pub(crate) fn open_sleep_or_subprocess_call(checker: &mut Checker, call: &ast::ExprCall) { +/// ASYNC230 +pub(crate) fn blocking_open_call(checker: &mut Checker, call: &ast::ExprCall) { if !checker.semantic().in_async_context() { return; } - if is_open_sleep_or_subprocess_call(&call.func, checker.semantic()) + if is_open_call(&call.func, checker.semantic()) || is_open_call_from_pathlib(call.func.as_ref(), checker.semantic()) { checker.diagnostics.push(Diagnostic::new( - OpenSleepOrSubprocessInAsyncFunction, + BlockingOpenCallInAsyncFunction, call.func.range(), )); } } -/// Returns `true` if the expression resolves to a blocking call, like `time.sleep` or -/// `subprocess.run`. -fn is_open_sleep_or_subprocess_call(func: &Expr, semantic: &SemanticModel) -> bool { +/// Returns `true` if the expression resolves to a blocking open call, like `open` or `Path().open()`. +fn is_open_call(func: &Expr, semantic: &SemanticModel) -> bool { semantic .resolve_qualified_name(func) .is_some_and(|qualified_name| { matches!( qualified_name.segments(), - ["" | "builtins", "open"] - | ["time", "sleep"] - | [ - "subprocess", - "run" - | "Popen" - | "call" - | "check_call" - | "check_output" - | "getoutput" - | "getstatusoutput" - ] - | ["os", "wait" | "wait3" | "wait4" | "waitid" | "waitpid"] + ["" | "io", "open"] | ["io", "open_code"] ) }) } diff --git a/crates/ruff_linter/src/rules/flake8_async/rules/blocking_os_call.rs b/crates/ruff_linter/src/rules/flake8_async/rules/blocking_os_call.rs deleted file mode 100644 index 59848aeb7d..0000000000 --- a/crates/ruff_linter/src/rules/flake8_async/rules/blocking_os_call.rs +++ /dev/null @@ -1,82 +0,0 @@ -use ruff_python_ast::ExprCall; - -use ruff_diagnostics::{Diagnostic, Violation}; -use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::name::QualifiedName; -use ruff_python_semantic::Modules; -use ruff_text_size::Ranged; - -use crate::checkers::ast::Checker; - -/// ## What it does -/// Checks that async functions do not contain calls to blocking synchronous -/// process calls via the `os` module. -/// -/// ## Why is this bad? -/// Blocking an async function via a blocking call will block the entire -/// event loop, preventing it from executing other tasks while waiting for the -/// call to complete, negating the benefits of asynchronous programming. -/// -/// Instead of making a blocking call, use an equivalent asynchronous library -/// or function. -/// -/// ## Example -/// ```python -/// async def foo(): -/// os.popen() -/// ``` -/// -/// Use instead: -/// ```python -/// def foo(): -/// os.popen() -/// ``` -#[violation] -pub struct BlockingOsCallInAsyncFunction; - -impl Violation for BlockingOsCallInAsyncFunction { - #[derive_message_formats] - fn message(&self) -> String { - format!("Async functions should not call synchronous `os` methods") - } -} - -/// ASYNC102 -pub(crate) fn blocking_os_call(checker: &mut Checker, call: &ExprCall) { - if checker.semantic().seen_module(Modules::OS) { - if checker.semantic().in_async_context() { - if checker - .semantic() - .resolve_qualified_name(call.func.as_ref()) - .as_ref() - .is_some_and(is_unsafe_os_method) - { - checker.diagnostics.push(Diagnostic::new( - BlockingOsCallInAsyncFunction, - call.func.range(), - )); - } - } - } -} - -fn is_unsafe_os_method(qualified_name: &QualifiedName) -> bool { - matches!( - qualified_name.segments(), - [ - "os", - "popen" - | "posix_spawn" - | "posix_spawnp" - | "spawnl" - | "spawnle" - | "spawnlp" - | "spawnlpe" - | "spawnv" - | "spawnve" - | "spawnvp" - | "spawnvpe" - | "system" - ] - ) -} diff --git a/crates/ruff_linter/src/rules/flake8_async/rules/blocking_process_invocation.rs b/crates/ruff_linter/src/rules/flake8_async/rules/blocking_process_invocation.rs new file mode 100644 index 0000000000..bec9bf816c --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_async/rules/blocking_process_invocation.rs @@ -0,0 +1,166 @@ +use ruff_diagnostics::{Diagnostic, DiagnosticKind, Violation}; +use ruff_macros::{derive_message_formats, violation}; +use ruff_python_ast::{self as ast, Expr}; +use ruff_python_semantic::analyze::typing::find_assigned_value; +use ruff_python_semantic::SemanticModel; +use ruff_text_size::Ranged; + +use crate::checkers::ast::Checker; +use crate::registry::AsRule; + +/// ## What it does +/// Checks that async functions do not create subprocesses with blocking methods. +/// +/// ## Why is this bad? +/// Blocking an async function via a blocking call will block the entire +/// event loop, preventing it from executing other tasks while waiting for the +/// call to complete, negating the benefits of asynchronous programming. +/// +/// Instead of making a blocking call, use an equivalent asynchronous library +/// or function. +/// +/// ## Example +/// ```python +/// async def foo(): +/// os.popen(cmd) +/// ``` +/// +/// Use instead: +/// ```python +/// async def foo(): +/// asyncio.create_subprocess_shell(cmd) +/// ``` +#[violation] +pub struct CreateSubprocessInAsyncFunction; + +impl Violation for CreateSubprocessInAsyncFunction { + #[derive_message_formats] + fn message(&self) -> String { + format!("Async functions should not create subprocesses with blocking methods") + } +} + +/// ## What it does +/// Checks that async functions do not run processes with blocking methods. +/// +/// ## Why is this bad? +/// Blocking an async function via a blocking call will block the entire +/// event loop, preventing it from executing other tasks while waiting for the +/// call to complete, negating the benefits of asynchronous programming. +/// +/// Instead of making a blocking call, use an equivalent asynchronous library +/// or function. +/// +/// ## Example +/// ```python +/// async def foo(): +/// subprocess.run(cmd) +/// ``` +/// +/// Use instead: +/// ```python +/// async def foo(): +/// asyncio.create_subprocess_shell(cmd) +/// ``` +#[violation] +pub struct RunProcessInAsyncFunction; + +impl Violation for RunProcessInAsyncFunction { + #[derive_message_formats] + fn message(&self) -> String { + format!("Async functions should not run processes with blocking methods") + } +} + +/// ## What it does +/// Checks that async functions do not wait on processes with blocking methods. +/// +/// ## Why is this bad? +/// Blocking an async function via a blocking call will block the entire +/// event loop, preventing it from executing other tasks while waiting for the +/// call to complete, negating the benefits of asynchronous programming. +/// +/// Instead of making a blocking call, use an equivalent asynchronous library +/// or function. +/// +/// ## Example +/// ```python +/// async def foo(): +/// os.waitpid(0) +/// ``` +/// +/// Use instead: +/// ```python +/// def wait_for_process(): +/// os.waitpid(0) +/// +/// +/// async def foo(): +/// await asyncio.loop.run_in_executor(None, wait_for_process) +/// ``` +#[violation] +pub struct WaitForProcessInAsyncFunction; + +impl Violation for WaitForProcessInAsyncFunction { + #[derive_message_formats] + fn message(&self) -> String { + format!("Async functions should not wait on processes with blocking methods") + } +} + +/// ASYNC220, ASYNC221, ASYNC222 +pub(crate) fn blocking_process_invocation(checker: &mut Checker, call: &ast::ExprCall) { + if !checker.semantic().in_async_context() { + return; + } + + let Some(diagnostic_kind) = + checker + .semantic() + .resolve_qualified_name(call.func.as_ref()) + .and_then(|qualified_name| match qualified_name.segments() { + ["subprocess", "Popen"] | ["os", "popen"] => { + Some(CreateSubprocessInAsyncFunction.into()) + } + ["os", "system" | "posix_spawn" | "posix_spawnp"] + | ["subprocess", "run" | "call" | "check_call" | "check_output" | "getoutput" + | "getstatusoutput"] => Some(RunProcessInAsyncFunction.into()), + ["os", "wait" | "wait3" | "wait4" | "waitid" | "waitpid"] => { + Some(WaitForProcessInAsyncFunction.into()) + } + ["os", "spawnl" | "spawnle" | "spawnlp" | "spawnlpe" | "spawnv" | "spawnve" + | "spawnvp" | "spawnvpe"] => { + if is_p_wait(call, checker.semantic()) { + Some(RunProcessInAsyncFunction.into()) + } else { + Some(CreateSubprocessInAsyncFunction.into()) + } + } + _ => None, + }) + else { + return; + }; + let diagnostic = Diagnostic::new::(diagnostic_kind, call.func.range()); + if checker.enabled(diagnostic.kind.rule()) { + checker.diagnostics.push(diagnostic); + } +} + +fn is_p_wait(call: &ast::ExprCall, semantic: &SemanticModel) -> bool { + let Some(arg) = call.arguments.find_argument("mode", 0) else { + return true; + }; + + if let Some(qualified_name) = semantic.resolve_qualified_name(arg) { + return matches!(qualified_name.segments(), ["os", "P_WAIT"]); + } else if let Expr::Name(ast::ExprName { id, .. }) = arg { + let Some(value) = find_assigned_value(id, semantic) else { + return false; + }; + if let Some(qualified_name) = semantic.resolve_qualified_name(value) { + return matches!(qualified_name.segments(), ["os", "P_WAIT"]); + } + } + false +} diff --git a/crates/ruff_linter/src/rules/flake8_async/rules/blocking_sleep.rs b/crates/ruff_linter/src/rules/flake8_async/rules/blocking_sleep.rs new file mode 100644 index 0000000000..e1a118ee21 --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_async/rules/blocking_sleep.rs @@ -0,0 +1,60 @@ +use ruff_python_ast::ExprCall; + +use ruff_diagnostics::{Diagnostic, Violation}; +use ruff_macros::{derive_message_formats, violation}; +use ruff_python_ast::name::QualifiedName; +use ruff_text_size::Ranged; + +use crate::checkers::ast::Checker; + +/// ## What it does +/// Checks that async functions do not call `time.sleep`. +/// +/// ## Why is this bad? +/// Blocking an async function via a `time.sleep` call will block the entire +/// event loop, preventing it from executing other tasks while waiting for the +/// `time.sleep`, negating the benefits of asynchronous programming. +/// +/// Instead of `time.sleep`, use `asyncio.sleep`. +/// +/// ## Example +/// ```python +/// async def fetch(): +/// time.sleep(1) +/// ``` +/// +/// Use instead: +/// ```python +/// async def fetch(): +/// await asyncio.sleep(1) +/// ``` +#[violation] +pub struct BlockingSleepInAsyncFunction; + +impl Violation for BlockingSleepInAsyncFunction { + #[derive_message_formats] + fn message(&self) -> String { + format!("Async functions should not call `time.sleep`") + } +} + +fn is_blocking_sleep(qualified_name: &QualifiedName) -> bool { + matches!(qualified_name.segments(), ["time", "sleep"]) +} + +/// ASYNC251 +pub(crate) fn blocking_sleep(checker: &mut Checker, call: &ExprCall) { + if checker.semantic().in_async_context() { + if checker + .semantic() + .resolve_qualified_name(call.func.as_ref()) + .as_ref() + .is_some_and(is_blocking_sleep) + { + checker.diagnostics.push(Diagnostic::new( + BlockingSleepInAsyncFunction, + call.func.range(), + )); + } + } +} 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 2ae3723f49..1a1950c21a 100644 --- a/crates/ruff_linter/src/rules/flake8_async/rules/mod.rs +++ b/crates/ruff_linter/src/rules/flake8_async/rules/mod.rs @@ -1,9 +1,21 @@ +pub(crate) use async_function_with_timeout::*; pub(crate) use blocking_http_call::*; -pub(crate) use blocking_os_call::*; -pub(crate) use open_sleep_or_subprocess_call::*; +pub(crate) use blocking_open_call::*; +pub(crate) use blocking_process_invocation::*; +pub(crate) use blocking_sleep::*; pub(crate) use sleep_forever_call::*; +pub(crate) use sync_call::*; +pub(crate) use timeout_without_await::*; +pub(crate) use unneeded_sleep::*; +pub(crate) use zero_sleep_call::*; +mod async_function_with_timeout; mod blocking_http_call; -mod blocking_os_call; -mod open_sleep_or_subprocess_call; +mod blocking_open_call; +mod blocking_process_invocation; +mod blocking_sleep; mod sleep_forever_call; +mod sync_call; +mod timeout_without_await; +mod unneeded_sleep; +mod zero_sleep_call; diff --git a/crates/ruff_linter/src/rules/flake8_trio/rules/sync_call.rs b/crates/ruff_linter/src/rules/flake8_async/rules/sync_call.rs similarity index 97% rename from crates/ruff_linter/src/rules/flake8_trio/rules/sync_call.rs rename to crates/ruff_linter/src/rules/flake8_async/rules/sync_call.rs index 2f824ab41a..cccf7fc20b 100644 --- a/crates/ruff_linter/src/rules/flake8_trio/rules/sync_call.rs +++ b/crates/ruff_linter/src/rules/flake8_async/rules/sync_call.rs @@ -6,7 +6,7 @@ use ruff_text_size::{Ranged, TextRange}; use crate::checkers::ast::Checker; use crate::fix::edits::pad; -use crate::rules::flake8_trio::method_name::MethodName; +use crate::rules::flake8_async::helpers::MethodName; /// ## What it does /// Checks for calls to trio functions that are not immediately awaited. @@ -50,7 +50,7 @@ impl Violation for TrioSyncCall { } } -/// TRIO105 +/// ASYNC105 pub(crate) fn sync_call(checker: &mut Checker, call: &ExprCall) { if !checker.semantic().seen_module(Modules::TRIO) { return; diff --git a/crates/ruff_linter/src/rules/flake8_trio/rules/timeout_without_await.rs b/crates/ruff_linter/src/rules/flake8_async/rules/timeout_without_await.rs similarity index 97% rename from crates/ruff_linter/src/rules/flake8_trio/rules/timeout_without_await.rs rename to crates/ruff_linter/src/rules/flake8_async/rules/timeout_without_await.rs index d0707d32bc..f60b2002d4 100644 --- a/crates/ruff_linter/src/rules/flake8_trio/rules/timeout_without_await.rs +++ b/crates/ruff_linter/src/rules/flake8_async/rules/timeout_without_await.rs @@ -6,7 +6,7 @@ use ruff_python_ast::{StmtWith, WithItem}; use ruff_python_semantic::Modules; use crate::checkers::ast::Checker; -use crate::rules::flake8_trio::method_name::MethodName; +use crate::rules::flake8_async::helpers::MethodName; /// ## What it does /// Checks for trio functions that should contain await but don't. @@ -44,7 +44,7 @@ impl Violation for TrioTimeoutWithoutAwait { } } -/// TRIO100 +/// ASYNC100 pub(crate) fn timeout_without_await( checker: &mut Checker, with_stmt: &StmtWith, diff --git a/crates/ruff_linter/src/rules/flake8_trio/rules/unneeded_sleep.rs b/crates/ruff_linter/src/rules/flake8_async/rules/unneeded_sleep.rs similarity index 99% rename from crates/ruff_linter/src/rules/flake8_trio/rules/unneeded_sleep.rs rename to crates/ruff_linter/src/rules/flake8_async/rules/unneeded_sleep.rs index 921e0adaa9..aded4e23d1 100644 --- a/crates/ruff_linter/src/rules/flake8_trio/rules/unneeded_sleep.rs +++ b/crates/ruff_linter/src/rules/flake8_async/rules/unneeded_sleep.rs @@ -41,7 +41,7 @@ impl Violation for TrioUnneededSleep { } } -/// TRIO110 +/// ASYNC110 pub(crate) fn unneeded_sleep(checker: &mut Checker, while_stmt: &ast::StmtWhile) { if !checker.semantic().seen_module(Modules::TRIO) { return; diff --git a/crates/ruff_linter/src/rules/flake8_trio/rules/zero_sleep_call.rs b/crates/ruff_linter/src/rules/flake8_async/rules/zero_sleep_call.rs similarity index 99% rename from crates/ruff_linter/src/rules/flake8_trio/rules/zero_sleep_call.rs rename to crates/ruff_linter/src/rules/flake8_async/rules/zero_sleep_call.rs index f8ddca3364..f1d23f618e 100644 --- a/crates/ruff_linter/src/rules/flake8_trio/rules/zero_sleep_call.rs +++ b/crates/ruff_linter/src/rules/flake8_async/rules/zero_sleep_call.rs @@ -45,7 +45,7 @@ impl AlwaysFixableViolation for TrioZeroSleepCall { } } -/// TRIO115 +/// ASYNC115 pub(crate) fn zero_sleep_call(checker: &mut Checker, call: &ExprCall) { if !checker.semantic().seen_module(Modules::TRIO) { return; diff --git a/crates/ruff_linter/src/rules/flake8_async/snapshots/ruff_linter__rules__flake8_async__tests__ASYNC100_ASYNC100.py.snap b/crates/ruff_linter/src/rules/flake8_async/snapshots/ruff_linter__rules__flake8_async__tests__ASYNC100_ASYNC100.py.snap index b7612ca1bc..fe22d6a3c3 100644 --- a/crates/ruff_linter/src/rules/flake8_async/snapshots/ruff_linter__rules__flake8_async__tests__ASYNC100_ASYNC100.py.snap +++ b/crates/ruff_linter/src/rules/flake8_async/snapshots/ruff_linter__rules__flake8_async__tests__ASYNC100_ASYNC100.py.snap @@ -1,39 +1,20 @@ --- source: crates/ruff_linter/src/rules/flake8_async/mod.rs --- -ASYNC100.py:7:5: ASYNC100 Async functions should not call blocking HTTP methods +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. | -6 | async def foo(): -7 | urllib.request.urlopen("http://example.com/foo/bar").read() - | ^^^^^^^^^^^^^^^^^^^^^^ ASYNC100 +4 | async def func(): +5 | with trio.fail_after(): + | _____^ +6 | | ... + | |___________^ ASYNC100 | -ASYNC100.py:11:5: ASYNC100 Async functions should not call blocking HTTP methods +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. | -10 | async def foo(): -11 | requests.get() - | ^^^^^^^^^^^^ ASYNC100 +14 | async def func(): +15 | with trio.move_on_after(): + | _____^ +16 | | ... + | |___________^ ASYNC100 | - -ASYNC100.py:15:5: ASYNC100 Async functions should not call blocking HTTP methods - | -14 | async def foo(): -15 | httpx.get() - | ^^^^^^^^^ ASYNC100 - | - -ASYNC100.py:19:5: ASYNC100 Async functions should not call blocking HTTP methods - | -18 | async def foo(): -19 | requests.post() - | ^^^^^^^^^^^^^ ASYNC100 - | - -ASYNC100.py:23:5: ASYNC100 Async functions should not call blocking HTTP methods - | -22 | async def foo(): -23 | httpx.post() - | ^^^^^^^^^^ ASYNC100 - | - - diff --git a/crates/ruff_linter/src/rules/flake8_async/snapshots/ruff_linter__rules__flake8_async__tests__ASYNC101_ASYNC101.py.snap b/crates/ruff_linter/src/rules/flake8_async/snapshots/ruff_linter__rules__flake8_async__tests__ASYNC101_ASYNC101.py.snap deleted file mode 100644 index 969e9ec1f4..0000000000 --- a/crates/ruff_linter/src/rules/flake8_async/snapshots/ruff_linter__rules__flake8_async__tests__ASYNC101_ASYNC101.py.snap +++ /dev/null @@ -1,84 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/flake8_async/mod.rs ---- -ASYNC101.py:10:5: ASYNC101 Async functions should not call `open`, `time.sleep`, or `subprocess` methods - | - 9 | async def func(): -10 | open("foo") - | ^^^^ ASYNC101 - | - -ASYNC101.py:14:5: ASYNC101 Async functions should not call `open`, `time.sleep`, or `subprocess` methods - | -13 | async def func(): -14 | time.sleep(1) - | ^^^^^^^^^^ ASYNC101 - | - -ASYNC101.py:18:5: ASYNC101 Async functions should not call `open`, `time.sleep`, or `subprocess` methods - | -17 | async def func(): -18 | subprocess.run("foo") - | ^^^^^^^^^^^^^^ ASYNC101 - | - -ASYNC101.py:22:5: ASYNC101 Async functions should not call `open`, `time.sleep`, or `subprocess` methods - | -21 | async def func(): -22 | subprocess.call("foo") - | ^^^^^^^^^^^^^^^ ASYNC101 - | - -ASYNC101.py:30:5: ASYNC101 Async functions should not call `open`, `time.sleep`, or `subprocess` methods - | -29 | async def func(): -30 | os.wait4(10) - | ^^^^^^^^ ASYNC101 - | - -ASYNC101.py:34:5: ASYNC101 Async functions should not call `open`, `time.sleep`, or `subprocess` methods - | -33 | async def func(): -34 | os.wait(12) - | ^^^^^^^ ASYNC101 - | - -ASYNC101.py:41:5: ASYNC101 Async functions should not call `open`, `time.sleep`, or `subprocess` methods - | -40 | async def func(): -41 | Path("foo").open() # ASYNC101 - | ^^^^^^^^^^^^^^^^ ASYNC101 - | - -ASYNC101.py:46:5: ASYNC101 Async functions should not call `open`, `time.sleep`, or `subprocess` methods - | -44 | async def func(): -45 | p = Path("foo") -46 | p.open() # ASYNC101 - | ^^^^^^ ASYNC101 - | - -ASYNC101.py:50:10: ASYNC101 Async functions should not call `open`, `time.sleep`, or `subprocess` methods - | -49 | async def func(): -50 | with Path("foo").open() as f: # ASYNC101 - | ^^^^^^^^^^^^^^^^ ASYNC101 -51 | pass - | - -ASYNC101.py:58:9: ASYNC101 Async functions should not call `open`, `time.sleep`, or `subprocess` methods - | -57 | async def bar(): -58 | p.open() # ASYNC101 - | ^^^^^^ ASYNC101 - | - -ASYNC101.py:64:5: ASYNC101 Async functions should not call `open`, `time.sleep`, or `subprocess` methods - | -62 | (p1, p2) = (Path("foo"), Path("bar")) -63 | -64 | p1.open() # ASYNC101 - | ^^^^^^^ ASYNC101 - | - - diff --git a/crates/ruff_linter/src/rules/flake8_async/snapshots/ruff_linter__rules__flake8_async__tests__ASYNC102_ASYNC102.py.snap b/crates/ruff_linter/src/rules/flake8_async/snapshots/ruff_linter__rules__flake8_async__tests__ASYNC102_ASYNC102.py.snap deleted file mode 100644 index d97b6da81c..0000000000 --- a/crates/ruff_linter/src/rules/flake8_async/snapshots/ruff_linter__rules__flake8_async__tests__ASYNC102_ASYNC102.py.snap +++ /dev/null @@ -1,18 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/flake8_async/mod.rs ---- -ASYNC102.py:5:5: ASYNC102 Async functions should not call synchronous `os` methods - | -4 | async def foo(): -5 | os.popen() - | ^^^^^^^^ ASYNC102 - | - -ASYNC102.py:9:5: ASYNC102 Async functions should not call synchronous `os` methods - | -8 | async def foo(): -9 | os.spawnl() - | ^^^^^^^^^ ASYNC102 - | - - diff --git a/crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO105_TRIO105.py.snap b/crates/ruff_linter/src/rules/flake8_async/snapshots/ruff_linter__rules__flake8_async__tests__ASYNC105_ASYNC105.py.snap similarity index 80% rename from crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO105_TRIO105.py.snap rename to crates/ruff_linter/src/rules/flake8_async/snapshots/ruff_linter__rules__flake8_async__tests__ASYNC105_ASYNC105.py.snap index 67312f78a4..1595cdc008 100644 --- a/crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO105_TRIO105.py.snap +++ b/crates/ruff_linter/src/rules/flake8_async/snapshots/ruff_linter__rules__flake8_async__tests__ASYNC105_ASYNC105.py.snap @@ -1,11 +1,11 @@ --- -source: crates/ruff_linter/src/rules/flake8_trio/mod.rs +source: crates/ruff_linter/src/rules/flake8_async/mod.rs --- -TRIO105.py:30:5: TRIO105 [*] Call to `trio.aclose_forcefully` is not immediately awaited +ASYNC105.py:30:5: ASYNC105 [*] Call to `trio.aclose_forcefully` is not immediately awaited | -29 | # TRIO105 +29 | # ASYNC105 30 | trio.aclose_forcefully(foo) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ TRIO105 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ASYNC105 31 | trio.open_file(foo) 32 | trio.open_ssl_over_tcp_listeners(foo, foo) | @@ -14,19 +14,19 @@ TRIO105.py:30:5: TRIO105 [*] Call to `trio.aclose_forcefully` is not immediately ℹ Unsafe fix 27 27 | await trio.lowlevel.wait_writable(foo) 28 28 | -29 29 | # TRIO105 +29 29 | # ASYNC105 30 |- trio.aclose_forcefully(foo) 30 |+ await trio.aclose_forcefully(foo) 31 31 | trio.open_file(foo) 32 32 | trio.open_ssl_over_tcp_listeners(foo, foo) 33 33 | trio.open_ssl_over_tcp_stream(foo, foo) -TRIO105.py:31:5: TRIO105 [*] Call to `trio.open_file` is not immediately awaited +ASYNC105.py:31:5: ASYNC105 [*] Call to `trio.open_file` is not immediately awaited | -29 | # TRIO105 +29 | # ASYNC105 30 | trio.aclose_forcefully(foo) 31 | trio.open_file(foo) - | ^^^^^^^^^^^^^^^^^^^ TRIO105 + | ^^^^^^^^^^^^^^^^^^^ ASYNC105 32 | trio.open_ssl_over_tcp_listeners(foo, foo) 33 | trio.open_ssl_over_tcp_stream(foo, foo) | @@ -34,7 +34,7 @@ TRIO105.py:31:5: TRIO105 [*] Call to `trio.open_file` is not immediately awaited ℹ Unsafe fix 28 28 | -29 29 | # TRIO105 +29 29 | # ASYNC105 30 30 | trio.aclose_forcefully(foo) 31 |- trio.open_file(foo) 31 |+ await trio.open_file(foo) @@ -42,19 +42,19 @@ TRIO105.py:31:5: TRIO105 [*] Call to `trio.open_file` is not immediately awaited 33 33 | trio.open_ssl_over_tcp_stream(foo, foo) 34 34 | trio.open_tcp_listeners(foo) -TRIO105.py:32:5: TRIO105 [*] Call to `trio.open_ssl_over_tcp_listeners` is not immediately awaited +ASYNC105.py:32:5: ASYNC105 [*] Call to `trio.open_ssl_over_tcp_listeners` is not immediately awaited | 30 | trio.aclose_forcefully(foo) 31 | trio.open_file(foo) 32 | trio.open_ssl_over_tcp_listeners(foo, foo) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ TRIO105 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ASYNC105 33 | trio.open_ssl_over_tcp_stream(foo, foo) 34 | trio.open_tcp_listeners(foo) | = help: Add `await` ℹ Unsafe fix -29 29 | # TRIO105 +29 29 | # ASYNC105 30 30 | trio.aclose_forcefully(foo) 31 31 | trio.open_file(foo) 32 |- trio.open_ssl_over_tcp_listeners(foo, foo) @@ -63,12 +63,12 @@ TRIO105.py:32:5: TRIO105 [*] Call to `trio.open_ssl_over_tcp_listeners` is not i 34 34 | trio.open_tcp_listeners(foo) 35 35 | trio.open_tcp_stream(foo, foo) -TRIO105.py:33:5: TRIO105 [*] Call to `trio.open_ssl_over_tcp_stream` is not immediately awaited +ASYNC105.py:33:5: ASYNC105 [*] Call to `trio.open_ssl_over_tcp_stream` is not immediately awaited | 31 | trio.open_file(foo) 32 | trio.open_ssl_over_tcp_listeners(foo, foo) 33 | trio.open_ssl_over_tcp_stream(foo, foo) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ TRIO105 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ASYNC105 34 | trio.open_tcp_listeners(foo) 35 | trio.open_tcp_stream(foo, foo) | @@ -84,12 +84,12 @@ TRIO105.py:33:5: TRIO105 [*] Call to `trio.open_ssl_over_tcp_stream` is not imme 35 35 | trio.open_tcp_stream(foo, foo) 36 36 | trio.open_unix_socket(foo) -TRIO105.py:34:5: TRIO105 [*] Call to `trio.open_tcp_listeners` is not immediately awaited +ASYNC105.py:34:5: ASYNC105 [*] Call to `trio.open_tcp_listeners` is not immediately awaited | 32 | trio.open_ssl_over_tcp_listeners(foo, foo) 33 | trio.open_ssl_over_tcp_stream(foo, foo) 34 | trio.open_tcp_listeners(foo) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ TRIO105 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ASYNC105 35 | trio.open_tcp_stream(foo, foo) 36 | trio.open_unix_socket(foo) | @@ -105,12 +105,12 @@ TRIO105.py:34:5: TRIO105 [*] Call to `trio.open_tcp_listeners` is not immediatel 36 36 | trio.open_unix_socket(foo) 37 37 | trio.run_process(foo) -TRIO105.py:35:5: TRIO105 [*] Call to `trio.open_tcp_stream` is not immediately awaited +ASYNC105.py:35:5: ASYNC105 [*] Call to `trio.open_tcp_stream` is not immediately awaited | 33 | trio.open_ssl_over_tcp_stream(foo, foo) 34 | trio.open_tcp_listeners(foo) 35 | trio.open_tcp_stream(foo, foo) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ TRIO105 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ASYNC105 36 | trio.open_unix_socket(foo) 37 | trio.run_process(foo) | @@ -126,12 +126,12 @@ TRIO105.py:35:5: TRIO105 [*] Call to `trio.open_tcp_stream` is not immediately a 37 37 | trio.run_process(foo) 38 38 | trio.serve_listeners(foo, foo) -TRIO105.py:36:5: TRIO105 [*] Call to `trio.open_unix_socket` is not immediately awaited +ASYNC105.py:36:5: ASYNC105 [*] Call to `trio.open_unix_socket` is not immediately awaited | 34 | trio.open_tcp_listeners(foo) 35 | trio.open_tcp_stream(foo, foo) 36 | trio.open_unix_socket(foo) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ TRIO105 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ ASYNC105 37 | trio.run_process(foo) 38 | trio.serve_listeners(foo, foo) | @@ -147,12 +147,12 @@ TRIO105.py:36:5: TRIO105 [*] Call to `trio.open_unix_socket` is not immediately 38 38 | trio.serve_listeners(foo, foo) 39 39 | trio.serve_ssl_over_tcp(foo, foo, foo) -TRIO105.py:37:5: TRIO105 [*] Call to `trio.run_process` is not immediately awaited +ASYNC105.py:37:5: ASYNC105 [*] Call to `trio.run_process` is not immediately awaited | 35 | trio.open_tcp_stream(foo, foo) 36 | trio.open_unix_socket(foo) 37 | trio.run_process(foo) - | ^^^^^^^^^^^^^^^^^^^^^ TRIO105 + | ^^^^^^^^^^^^^^^^^^^^^ ASYNC105 38 | trio.serve_listeners(foo, foo) 39 | trio.serve_ssl_over_tcp(foo, foo, foo) | @@ -168,12 +168,12 @@ TRIO105.py:37:5: TRIO105 [*] Call to `trio.run_process` is not immediately await 39 39 | trio.serve_ssl_over_tcp(foo, foo, foo) 40 40 | trio.serve_tcp(foo, foo) -TRIO105.py:38:5: TRIO105 [*] Call to `trio.serve_listeners` is not immediately awaited +ASYNC105.py:38:5: ASYNC105 [*] Call to `trio.serve_listeners` is not immediately awaited | 36 | trio.open_unix_socket(foo) 37 | trio.run_process(foo) 38 | trio.serve_listeners(foo, foo) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ TRIO105 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ASYNC105 39 | trio.serve_ssl_over_tcp(foo, foo, foo) 40 | trio.serve_tcp(foo, foo) | @@ -189,12 +189,12 @@ TRIO105.py:38:5: TRIO105 [*] Call to `trio.serve_listeners` is not immediately a 40 40 | trio.serve_tcp(foo, foo) 41 41 | trio.sleep(foo) -TRIO105.py:39:5: TRIO105 [*] Call to `trio.serve_ssl_over_tcp` is not immediately awaited +ASYNC105.py:39:5: ASYNC105 [*] Call to `trio.serve_ssl_over_tcp` is not immediately awaited | 37 | trio.run_process(foo) 38 | trio.serve_listeners(foo, foo) 39 | trio.serve_ssl_over_tcp(foo, foo, foo) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ TRIO105 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ASYNC105 40 | trio.serve_tcp(foo, foo) 41 | trio.sleep(foo) | @@ -210,12 +210,12 @@ TRIO105.py:39:5: TRIO105 [*] Call to `trio.serve_ssl_over_tcp` is not immediatel 41 41 | trio.sleep(foo) 42 42 | trio.sleep_forever() -TRIO105.py:40:5: TRIO105 [*] Call to `trio.serve_tcp` is not immediately awaited +ASYNC105.py:40:5: ASYNC105 [*] Call to `trio.serve_tcp` is not immediately awaited | 38 | trio.serve_listeners(foo, foo) 39 | trio.serve_ssl_over_tcp(foo, foo, foo) 40 | trio.serve_tcp(foo, foo) - | ^^^^^^^^^^^^^^^^^^^^^^^^ TRIO105 + | ^^^^^^^^^^^^^^^^^^^^^^^^ ASYNC105 41 | trio.sleep(foo) 42 | trio.sleep_forever() | @@ -231,12 +231,12 @@ TRIO105.py:40:5: TRIO105 [*] Call to `trio.serve_tcp` is not immediately awaited 42 42 | trio.sleep_forever() 43 43 | trio.sleep_until(foo) -TRIO105.py:41:5: TRIO105 [*] Call to `trio.sleep` is not immediately awaited +ASYNC105.py:41:5: ASYNC105 [*] Call to `trio.sleep` is not immediately awaited | 39 | trio.serve_ssl_over_tcp(foo, foo, foo) 40 | trio.serve_tcp(foo, foo) 41 | trio.sleep(foo) - | ^^^^^^^^^^^^^^^ TRIO105 + | ^^^^^^^^^^^^^^^ ASYNC105 42 | trio.sleep_forever() 43 | trio.sleep_until(foo) | @@ -252,12 +252,12 @@ TRIO105.py:41:5: TRIO105 [*] Call to `trio.sleep` is not immediately awaited 43 43 | trio.sleep_until(foo) 44 44 | trio.lowlevel.cancel_shielded_checkpoint() -TRIO105.py:42:5: TRIO105 [*] Call to `trio.sleep_forever` is not immediately awaited +ASYNC105.py:42:5: ASYNC105 [*] Call to `trio.sleep_forever` is not immediately awaited | 40 | trio.serve_tcp(foo, foo) 41 | trio.sleep(foo) 42 | trio.sleep_forever() - | ^^^^^^^^^^^^^^^^^^^^ TRIO105 + | ^^^^^^^^^^^^^^^^^^^^ ASYNC105 43 | trio.sleep_until(foo) 44 | trio.lowlevel.cancel_shielded_checkpoint() | @@ -273,12 +273,12 @@ TRIO105.py:42:5: TRIO105 [*] Call to `trio.sleep_forever` is not immediately awa 44 44 | trio.lowlevel.cancel_shielded_checkpoint() 45 45 | trio.lowlevel.checkpoint() -TRIO105.py:44:5: TRIO105 [*] Call to `trio.lowlevel.cancel_shielded_checkpoint` is not immediately awaited +ASYNC105.py:44:5: ASYNC105 [*] Call to `trio.lowlevel.cancel_shielded_checkpoint` is not immediately awaited | 42 | trio.sleep_forever() 43 | trio.sleep_until(foo) 44 | trio.lowlevel.cancel_shielded_checkpoint() - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ TRIO105 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ASYNC105 45 | trio.lowlevel.checkpoint() 46 | trio.lowlevel.checkpoint_if_cancelled() | @@ -294,12 +294,12 @@ TRIO105.py:44:5: TRIO105 [*] Call to `trio.lowlevel.cancel_shielded_checkpoint` 46 46 | trio.lowlevel.checkpoint_if_cancelled() 47 47 | trio.lowlevel.open_process() -TRIO105.py:45:5: TRIO105 [*] Call to `trio.lowlevel.checkpoint` is not immediately awaited +ASYNC105.py:45:5: ASYNC105 [*] Call to `trio.lowlevel.checkpoint` is not immediately awaited | 43 | trio.sleep_until(foo) 44 | trio.lowlevel.cancel_shielded_checkpoint() 45 | trio.lowlevel.checkpoint() - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ TRIO105 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ ASYNC105 46 | trio.lowlevel.checkpoint_if_cancelled() 47 | trio.lowlevel.open_process() | @@ -315,12 +315,12 @@ TRIO105.py:45:5: TRIO105 [*] Call to `trio.lowlevel.checkpoint` is not immediate 47 47 | trio.lowlevel.open_process() 48 48 | trio.lowlevel.permanently_detach_coroutine_object(foo) -TRIO105.py:46:5: TRIO105 [*] Call to `trio.lowlevel.checkpoint_if_cancelled` is not immediately awaited +ASYNC105.py:46:5: ASYNC105 [*] Call to `trio.lowlevel.checkpoint_if_cancelled` is not immediately awaited | 44 | trio.lowlevel.cancel_shielded_checkpoint() 45 | trio.lowlevel.checkpoint() 46 | trio.lowlevel.checkpoint_if_cancelled() - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ TRIO105 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ASYNC105 47 | trio.lowlevel.open_process() 48 | trio.lowlevel.permanently_detach_coroutine_object(foo) | @@ -336,12 +336,12 @@ TRIO105.py:46:5: TRIO105 [*] Call to `trio.lowlevel.checkpoint_if_cancelled` is 48 48 | trio.lowlevel.permanently_detach_coroutine_object(foo) 49 49 | trio.lowlevel.reattach_detached_coroutine_object(foo, foo) -TRIO105.py:47:5: TRIO105 [*] Call to `trio.lowlevel.open_process` is not immediately awaited +ASYNC105.py:47:5: ASYNC105 [*] Call to `trio.lowlevel.open_process` is not immediately awaited | 45 | trio.lowlevel.checkpoint() 46 | trio.lowlevel.checkpoint_if_cancelled() 47 | trio.lowlevel.open_process() - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ TRIO105 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ASYNC105 48 | trio.lowlevel.permanently_detach_coroutine_object(foo) 49 | trio.lowlevel.reattach_detached_coroutine_object(foo, foo) | @@ -357,12 +357,12 @@ TRIO105.py:47:5: TRIO105 [*] Call to `trio.lowlevel.open_process` is not immedia 49 49 | trio.lowlevel.reattach_detached_coroutine_object(foo, foo) 50 50 | trio.lowlevel.temporarily_detach_coroutine_object(foo) -TRIO105.py:48:5: TRIO105 [*] Call to `trio.lowlevel.permanently_detach_coroutine_object` is not immediately awaited +ASYNC105.py:48:5: ASYNC105 [*] Call to `trio.lowlevel.permanently_detach_coroutine_object` is not immediately awaited | 46 | trio.lowlevel.checkpoint_if_cancelled() 47 | trio.lowlevel.open_process() 48 | trio.lowlevel.permanently_detach_coroutine_object(foo) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ TRIO105 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ASYNC105 49 | trio.lowlevel.reattach_detached_coroutine_object(foo, foo) 50 | trio.lowlevel.temporarily_detach_coroutine_object(foo) | @@ -378,12 +378,12 @@ TRIO105.py:48:5: TRIO105 [*] Call to `trio.lowlevel.permanently_detach_coroutine 50 50 | trio.lowlevel.temporarily_detach_coroutine_object(foo) 51 51 | trio.lowlevel.wait_readable(foo) -TRIO105.py:49:5: TRIO105 [*] Call to `trio.lowlevel.reattach_detached_coroutine_object` is not immediately awaited +ASYNC105.py:49:5: ASYNC105 [*] Call to `trio.lowlevel.reattach_detached_coroutine_object` is not immediately awaited | 47 | trio.lowlevel.open_process() 48 | trio.lowlevel.permanently_detach_coroutine_object(foo) 49 | trio.lowlevel.reattach_detached_coroutine_object(foo, foo) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ TRIO105 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ASYNC105 50 | trio.lowlevel.temporarily_detach_coroutine_object(foo) 51 | trio.lowlevel.wait_readable(foo) | @@ -399,12 +399,12 @@ TRIO105.py:49:5: TRIO105 [*] Call to `trio.lowlevel.reattach_detached_coroutine_ 51 51 | trio.lowlevel.wait_readable(foo) 52 52 | trio.lowlevel.wait_task_rescheduled(foo) -TRIO105.py:50:5: TRIO105 [*] Call to `trio.lowlevel.temporarily_detach_coroutine_object` is not immediately awaited +ASYNC105.py:50:5: ASYNC105 [*] Call to `trio.lowlevel.temporarily_detach_coroutine_object` is not immediately awaited | 48 | trio.lowlevel.permanently_detach_coroutine_object(foo) 49 | trio.lowlevel.reattach_detached_coroutine_object(foo, foo) 50 | trio.lowlevel.temporarily_detach_coroutine_object(foo) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ TRIO105 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ASYNC105 51 | trio.lowlevel.wait_readable(foo) 52 | trio.lowlevel.wait_task_rescheduled(foo) | @@ -420,12 +420,12 @@ TRIO105.py:50:5: TRIO105 [*] Call to `trio.lowlevel.temporarily_detach_coroutine 52 52 | trio.lowlevel.wait_task_rescheduled(foo) 53 53 | trio.lowlevel.wait_writable(foo) -TRIO105.py:51:5: TRIO105 [*] Call to `trio.lowlevel.wait_readable` is not immediately awaited +ASYNC105.py:51:5: ASYNC105 [*] Call to `trio.lowlevel.wait_readable` is not immediately awaited | 49 | trio.lowlevel.reattach_detached_coroutine_object(foo, foo) 50 | trio.lowlevel.temporarily_detach_coroutine_object(foo) 51 | trio.lowlevel.wait_readable(foo) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ TRIO105 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ASYNC105 52 | trio.lowlevel.wait_task_rescheduled(foo) 53 | trio.lowlevel.wait_writable(foo) | @@ -441,12 +441,12 @@ TRIO105.py:51:5: TRIO105 [*] Call to `trio.lowlevel.wait_readable` is not immedi 53 53 | trio.lowlevel.wait_writable(foo) 54 54 | -TRIO105.py:52:5: TRIO105 [*] Call to `trio.lowlevel.wait_task_rescheduled` is not immediately awaited +ASYNC105.py:52:5: ASYNC105 [*] Call to `trio.lowlevel.wait_task_rescheduled` is not immediately awaited | 50 | trio.lowlevel.temporarily_detach_coroutine_object(foo) 51 | trio.lowlevel.wait_readable(foo) 52 | trio.lowlevel.wait_task_rescheduled(foo) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ TRIO105 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ASYNC105 53 | trio.lowlevel.wait_writable(foo) | = help: Add `await` @@ -461,12 +461,12 @@ TRIO105.py:52:5: TRIO105 [*] Call to `trio.lowlevel.wait_task_rescheduled` is no 54 54 | 55 55 | async with await trio.open_file(foo): # Ok -TRIO105.py:53:5: TRIO105 [*] Call to `trio.lowlevel.wait_writable` is not immediately awaited +ASYNC105.py:53:5: ASYNC105 [*] Call to `trio.lowlevel.wait_writable` is not immediately awaited | 51 | trio.lowlevel.wait_readable(foo) 52 | trio.lowlevel.wait_task_rescheduled(foo) 53 | trio.lowlevel.wait_writable(foo) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ TRIO105 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ASYNC105 54 | 55 | async with await trio.open_file(foo): # Ok | @@ -482,12 +482,12 @@ TRIO105.py:53:5: TRIO105 [*] Call to `trio.lowlevel.wait_writable` is not immedi 55 55 | async with await trio.open_file(foo): # Ok 56 56 | pass -TRIO105.py:58:16: TRIO105 [*] Call to `trio.open_file` is not immediately awaited +ASYNC105.py:58:16: ASYNC105 [*] Call to `trio.open_file` is not immediately awaited | 56 | pass 57 | -58 | async with trio.open_file(foo): # TRIO105 - | ^^^^^^^^^^^^^^^^^^^ TRIO105 +58 | async with trio.open_file(foo): # ASYNC105 + | ^^^^^^^^^^^^^^^^^^^ ASYNC105 59 | pass | = help: Add `await` @@ -496,19 +496,17 @@ TRIO105.py:58:16: TRIO105 [*] Call to `trio.open_file` is not immediately awaite 55 55 | async with await trio.open_file(foo): # Ok 56 56 | pass 57 57 | -58 |- async with trio.open_file(foo): # TRIO105 - 58 |+ async with await trio.open_file(foo): # TRIO105 +58 |- async with trio.open_file(foo): # ASYNC105 + 58 |+ async with await trio.open_file(foo): # ASYNC105 59 59 | pass 60 60 | 61 61 | -TRIO105.py:64:5: TRIO105 Call to `trio.open_file` is not immediately awaited +ASYNC105.py:64:5: ASYNC105 Call to `trio.open_file` is not immediately awaited | 62 | def func() -> None: -63 | # TRIO105 (without fix) +63 | # ASYNC105 (without fix) 64 | trio.open_file(foo) - | ^^^^^^^^^^^^^^^^^^^ TRIO105 + | ^^^^^^^^^^^^^^^^^^^ ASYNC105 | = help: Add `await` - - diff --git a/crates/ruff_linter/src/rules/flake8_async/snapshots/ruff_linter__rules__flake8_async__tests__ASYNC109_ASYNC109.py.snap b/crates/ruff_linter/src/rules/flake8_async/snapshots/ruff_linter__rules__flake8_async__tests__ASYNC109_ASYNC109.py.snap new file mode 100644 index 0000000000..c196c1af9d --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_async/snapshots/ruff_linter__rules__flake8_async__tests__ASYNC109_ASYNC109.py.snap @@ -0,0 +1,16 @@ +--- +source: crates/ruff_linter/src/rules/flake8_async/mod.rs +--- +ASYNC109.py:8:16: ASYNC109 Prefer `trio.fail_after` and `trio.move_on_after` over manual `async` timeout behavior + | +8 | async def func(timeout): + | ^^^^^^^ ASYNC109 +9 | ... + | + +ASYNC109.py:12:16: ASYNC109 Prefer `trio.fail_after` and `trio.move_on_after` over manual `async` timeout behavior + | +12 | async def func(timeout=10): + | ^^^^^^^^^^ ASYNC109 +13 | ... + | diff --git a/crates/ruff_linter/src/rules/flake8_async/snapshots/ruff_linter__rules__flake8_async__tests__ASYNC110_ASYNC110.py.snap b/crates/ruff_linter/src/rules/flake8_async/snapshots/ruff_linter__rules__flake8_async__tests__ASYNC110_ASYNC110.py.snap new file mode 100644 index 0000000000..fe99c8f822 --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_async/snapshots/ruff_linter__rules__flake8_async__tests__ASYNC110_ASYNC110.py.snap @@ -0,0 +1,20 @@ +--- +source: crates/ruff_linter/src/rules/flake8_async/mod.rs +--- +ASYNC110.py:5:5: ASYNC110 Use `trio.Event` instead of awaiting `trio.sleep` in a `while` loop + | +4 | async def func(): +5 | while True: + | _____^ +6 | | await trio.sleep(10) + | |____________________________^ ASYNC110 + | + +ASYNC110.py:10:5: ASYNC110 Use `trio.Event` instead of awaiting `trio.sleep` in a `while` loop + | + 9 | async def func(): +10 | while True: + | _____^ +11 | | await trio.sleep_until(10) + | |__________________________________^ ASYNC110 + | diff --git a/crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO115_TRIO115.py.snap b/crates/ruff_linter/src/rules/flake8_async/snapshots/ruff_linter__rules__flake8_async__tests__ASYNC115_ASYNC115.py.snap similarity index 52% rename from crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO115_TRIO115.py.snap rename to crates/ruff_linter/src/rules/flake8_async/snapshots/ruff_linter__rules__flake8_async__tests__ASYNC115_ASYNC115.py.snap index 3de63ea470..71d341d400 100644 --- a/crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO115_TRIO115.py.snap +++ b/crates/ruff_linter/src/rules/flake8_async/snapshots/ruff_linter__rules__flake8_async__tests__ASYNC115_ASYNC115.py.snap @@ -1,12 +1,12 @@ --- -source: crates/ruff_linter/src/rules/flake8_trio/mod.rs +source: crates/ruff_linter/src/rules/flake8_async/mod.rs --- -TRIO115.py:5:11: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` +ASYNC115.py:5:11: ASYNC115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` | 3 | from trio import sleep 4 | -5 | await trio.sleep(0) # TRIO115 - | ^^^^^^^^^^^^^ TRIO115 +5 | await trio.sleep(0) # ASYNC115 + | ^^^^^^^^^^^^^ ASYNC115 6 | await trio.sleep(1) # OK 7 | await trio.sleep(0, 1) # OK | @@ -16,18 +16,18 @@ TRIO115.py:5:11: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.s 2 2 | import trio 3 3 | from trio import sleep 4 4 | -5 |- await trio.sleep(0) # TRIO115 - 5 |+ await trio.lowlevel.checkpoint() # TRIO115 +5 |- await trio.sleep(0) # ASYNC115 + 5 |+ await trio.lowlevel.checkpoint() # ASYNC115 6 6 | await trio.sleep(1) # OK 7 7 | await trio.sleep(0, 1) # OK 8 8 | await trio.sleep(...) # OK -TRIO115.py:11:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` +ASYNC115.py:11:5: ASYNC115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` | 9 | await trio.sleep() # OK 10 | -11 | trio.sleep(0) # TRIO115 - | ^^^^^^^^^^^^^ TRIO115 +11 | trio.sleep(0) # ASYNC115 + | ^^^^^^^^^^^^^ ASYNC115 12 | foo = 0 13 | trio.sleep(foo) # OK | @@ -37,18 +37,18 @@ TRIO115.py:11:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.s 8 8 | await trio.sleep(...) # OK 9 9 | await trio.sleep() # OK 10 10 | -11 |- trio.sleep(0) # TRIO115 - 11 |+ trio.lowlevel.checkpoint() # TRIO115 +11 |- trio.sleep(0) # ASYNC115 + 11 |+ trio.lowlevel.checkpoint() # ASYNC115 12 12 | foo = 0 13 13 | trio.sleep(foo) # OK 14 14 | trio.sleep(1) # OK -TRIO115.py:17:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` +ASYNC115.py:17:5: ASYNC115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` | 15 | time.sleep(0) # OK 16 | -17 | sleep(0) # TRIO115 - | ^^^^^^^^ TRIO115 +17 | sleep(0) # ASYNC115 + | ^^^^^^^^ ASYNC115 18 | 19 | bar = "bar" | @@ -58,18 +58,18 @@ TRIO115.py:17:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.s 14 14 | trio.sleep(1) # OK 15 15 | time.sleep(0) # OK 16 16 | -17 |- sleep(0) # TRIO115 - 17 |+ trio.lowlevel.checkpoint() # TRIO115 +17 |- sleep(0) # ASYNC115 + 17 |+ trio.lowlevel.checkpoint() # ASYNC115 18 18 | 19 19 | bar = "bar" 20 20 | trio.sleep(bar) -TRIO115.py:48:14: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` +ASYNC115.py:48:14: ASYNC115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` | 46 | import trio 47 | -48 | trio.run(trio.sleep(0)) # TRIO115 - | ^^^^^^^^^^^^^ TRIO115 +48 | trio.run(trio.sleep(0)) # ASYNC115 + | ^^^^^^^^^^^^^ ASYNC115 | = help: Replace with `trio.lowlevel.checkpoint()` @@ -77,22 +77,22 @@ TRIO115.py:48:14: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio. 45 45 | def func(): 46 46 | import trio 47 47 | -48 |- trio.run(trio.sleep(0)) # TRIO115 - 48 |+ trio.run(trio.lowlevel.checkpoint()) # TRIO115 +48 |- trio.run(trio.sleep(0)) # ASYNC115 + 48 |+ trio.run(trio.lowlevel.checkpoint()) # ASYNC115 49 49 | 50 50 | 51 51 | from trio import Event, sleep -TRIO115.py:55:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` +ASYNC115.py:55:5: ASYNC115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` | 54 | def func(): -55 | sleep(0) # TRIO115 - | ^^^^^^^^ TRIO115 +55 | sleep(0) # ASYNC115 + | ^^^^^^^^ ASYNC115 | = help: Replace with `trio.lowlevel.checkpoint()` ℹ Safe fix -48 48 | trio.run(trio.sleep(0)) # TRIO115 +48 48 | trio.run(trio.sleep(0)) # ASYNC115 49 49 | 50 50 | 51 |-from trio import Event, sleep @@ -100,22 +100,22 @@ TRIO115.py:55:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.s 52 52 | 53 53 | 54 54 | def func(): -55 |- sleep(0) # TRIO115 - 55 |+ lowlevel.checkpoint() # TRIO115 +55 |- sleep(0) # ASYNC115 + 55 |+ lowlevel.checkpoint() # ASYNC115 56 56 | 57 57 | 58 58 | async def func(): -TRIO115.py:59:11: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` +ASYNC115.py:59:11: ASYNC115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` | 58 | async def func(): -59 | await sleep(seconds=0) # TRIO115 - | ^^^^^^^^^^^^^^^^ TRIO115 +59 | await sleep(seconds=0) # ASYNC115 + | ^^^^^^^^^^^^^^^^ ASYNC115 | = help: Replace with `trio.lowlevel.checkpoint()` ℹ Safe fix -48 48 | trio.run(trio.sleep(0)) # TRIO115 +48 48 | trio.run(trio.sleep(0)) # ASYNC115 49 49 | 50 50 | 51 |-from trio import Event, sleep @@ -127,8 +127,8 @@ TRIO115.py:59:11: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio. 56 56 | 57 57 | 58 58 | async def func(): -59 |- await sleep(seconds=0) # TRIO115 - 59 |+ await lowlevel.checkpoint() # TRIO115 +59 |- await sleep(seconds=0) # ASYNC115 + 59 |+ await lowlevel.checkpoint() # ASYNC115 60 60 | 61 61 | 62 62 | def func(): diff --git a/crates/ruff_linter/src/rules/flake8_async/snapshots/ruff_linter__rules__flake8_async__tests__ASYNC210_ASYNC210.py.snap b/crates/ruff_linter/src/rules/flake8_async/snapshots/ruff_linter__rules__flake8_async__tests__ASYNC210_ASYNC210.py.snap new file mode 100644 index 0000000000..8ca70a9387 --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_async/snapshots/ruff_linter__rules__flake8_async__tests__ASYNC210_ASYNC210.py.snap @@ -0,0 +1,229 @@ +--- +source: crates/ruff_linter/src/rules/flake8_async/mod.rs +--- +ASYNC210.py:8:5: ASYNC210 Async functions should not call blocking HTTP methods + | +7 | async def foo(): +8 | urllib.request.urlopen("http://example.com/foo/bar").read() # ASYNC210 + | ^^^^^^^^^^^^^^^^^^^^^^ ASYNC210 + | + +ASYNC210.py:12:5: ASYNC210 Async functions should not call blocking HTTP methods + | +11 | async def foo(): +12 | requests.get() # ASYNC210 + | ^^^^^^^^^^^^ ASYNC210 + | + +ASYNC210.py:16:5: ASYNC210 Async functions should not call blocking HTTP methods + | +15 | async def foo(): +16 | httpx.get() # ASYNC210 + | ^^^^^^^^^ ASYNC210 + | + +ASYNC210.py:20:5: ASYNC210 Async functions should not call blocking HTTP methods + | +19 | async def foo(): +20 | requests.post() # ASYNC210 + | ^^^^^^^^^^^^^ ASYNC210 + | + +ASYNC210.py:24:5: ASYNC210 Async functions should not call blocking HTTP methods + | +23 | async def foo(): +24 | httpx.post() # ASYNC210 + | ^^^^^^^^^^ ASYNC210 + | + +ASYNC210.py:28:5: ASYNC210 Async functions should not call blocking HTTP methods + | +27 | async def foo(): +28 | requests.get() # ASYNC210 + | ^^^^^^^^^^^^ ASYNC210 +29 | requests.get(...) # ASYNC210 +30 | requests.get # Ok + | + +ASYNC210.py:29:5: ASYNC210 Async functions should not call blocking HTTP methods + | +27 | async def foo(): +28 | requests.get() # ASYNC210 +29 | requests.get(...) # ASYNC210 + | ^^^^^^^^^^^^ ASYNC210 +30 | requests.get # Ok +31 | print(requests.get()) # ASYNC210 + | + +ASYNC210.py:31:11: ASYNC210 Async functions should not call blocking HTTP methods + | +29 | requests.get(...) # ASYNC210 +30 | requests.get # Ok +31 | print(requests.get()) # ASYNC210 + | ^^^^^^^^^^^^ ASYNC210 +32 | print(requests.get(requests.get())) # ASYNC210 + | + +ASYNC210.py:32:11: ASYNC210 Async functions should not call blocking HTTP methods + | +30 | requests.get # Ok +31 | print(requests.get()) # ASYNC210 +32 | print(requests.get(requests.get())) # ASYNC210 + | ^^^^^^^^^^^^ ASYNC210 +33 | +34 | requests.options() # ASYNC210 + | + +ASYNC210.py:32:24: ASYNC210 Async functions should not call blocking HTTP methods + | +30 | requests.get # Ok +31 | print(requests.get()) # ASYNC210 +32 | print(requests.get(requests.get())) # ASYNC210 + | ^^^^^^^^^^^^ ASYNC210 +33 | +34 | requests.options() # ASYNC210 + | + +ASYNC210.py:34:5: ASYNC210 Async functions should not call blocking HTTP methods + | +32 | print(requests.get(requests.get())) # ASYNC210 +33 | +34 | requests.options() # ASYNC210 + | ^^^^^^^^^^^^^^^^ ASYNC210 +35 | requests.head() # ASYNC210 +36 | requests.post() # ASYNC210 + | + +ASYNC210.py:35:5: ASYNC210 Async functions should not call blocking HTTP methods + | +34 | requests.options() # ASYNC210 +35 | requests.head() # ASYNC210 + | ^^^^^^^^^^^^^ ASYNC210 +36 | requests.post() # ASYNC210 +37 | requests.put() # ASYNC210 + | + +ASYNC210.py:36:5: ASYNC210 Async functions should not call blocking HTTP methods + | +34 | requests.options() # ASYNC210 +35 | requests.head() # ASYNC210 +36 | requests.post() # ASYNC210 + | ^^^^^^^^^^^^^ ASYNC210 +37 | requests.put() # ASYNC210 +38 | requests.patch() # ASYNC210 + | + +ASYNC210.py:37:5: ASYNC210 Async functions should not call blocking HTTP methods + | +35 | requests.head() # ASYNC210 +36 | requests.post() # ASYNC210 +37 | requests.put() # ASYNC210 + | ^^^^^^^^^^^^ ASYNC210 +38 | requests.patch() # ASYNC210 +39 | requests.delete() # ASYNC210 + | + +ASYNC210.py:38:5: ASYNC210 Async functions should not call blocking HTTP methods + | +36 | requests.post() # ASYNC210 +37 | requests.put() # ASYNC210 +38 | requests.patch() # ASYNC210 + | ^^^^^^^^^^^^^^ ASYNC210 +39 | requests.delete() # ASYNC210 +40 | requests.foo() + | + +ASYNC210.py:39:5: ASYNC210 Async functions should not call blocking HTTP methods + | +37 | requests.put() # ASYNC210 +38 | requests.patch() # ASYNC210 +39 | requests.delete() # ASYNC210 + | ^^^^^^^^^^^^^^^ ASYNC210 +40 | requests.foo() + | + +ASYNC210.py:42:5: ASYNC210 Async functions should not call blocking HTTP methods + | +40 | requests.foo() +41 | +42 | httpx.options("") # ASYNC210 + | ^^^^^^^^^^^^^ ASYNC210 +43 | httpx.head("") # ASYNC210 +44 | httpx.post("") # ASYNC210 + | + +ASYNC210.py:43:5: ASYNC210 Async functions should not call blocking HTTP methods + | +42 | httpx.options("") # ASYNC210 +43 | httpx.head("") # ASYNC210 + | ^^^^^^^^^^ ASYNC210 +44 | httpx.post("") # ASYNC210 +45 | httpx.put("") # ASYNC210 + | + +ASYNC210.py:44:5: ASYNC210 Async functions should not call blocking HTTP methods + | +42 | httpx.options("") # ASYNC210 +43 | httpx.head("") # ASYNC210 +44 | httpx.post("") # ASYNC210 + | ^^^^^^^^^^ ASYNC210 +45 | httpx.put("") # ASYNC210 +46 | httpx.patch("") # ASYNC210 + | + +ASYNC210.py:45:5: ASYNC210 Async functions should not call blocking HTTP methods + | +43 | httpx.head("") # ASYNC210 +44 | httpx.post("") # ASYNC210 +45 | httpx.put("") # ASYNC210 + | ^^^^^^^^^ ASYNC210 +46 | httpx.patch("") # ASYNC210 +47 | httpx.delete("") # ASYNC210 + | + +ASYNC210.py:46:5: ASYNC210 Async functions should not call blocking HTTP methods + | +44 | httpx.post("") # ASYNC210 +45 | httpx.put("") # ASYNC210 +46 | httpx.patch("") # ASYNC210 + | ^^^^^^^^^^^ ASYNC210 +47 | httpx.delete("") # ASYNC210 +48 | httpx.foo() # Ok + | + +ASYNC210.py:47:5: ASYNC210 Async functions should not call blocking HTTP methods + | +45 | httpx.put("") # ASYNC210 +46 | httpx.patch("") # ASYNC210 +47 | httpx.delete("") # ASYNC210 + | ^^^^^^^^^^^^ ASYNC210 +48 | httpx.foo() # Ok + | + +ASYNC210.py:50:5: ASYNC210 Async functions should not call blocking HTTP methods + | +48 | httpx.foo() # Ok +49 | +50 | urllib3.request() # ASYNC210 + | ^^^^^^^^^^^^^^^ ASYNC210 +51 | urllib3.request(...) # ASYNC210 + | + +ASYNC210.py:51:5: ASYNC210 Async functions should not call blocking HTTP methods + | +50 | urllib3.request() # ASYNC210 +51 | urllib3.request(...) # ASYNC210 + | ^^^^^^^^^^^^^^^ ASYNC210 +52 | +53 | urllib.request.urlopen("") # ASYNC210 + | + +ASYNC210.py:53:5: ASYNC210 Async functions should not call blocking HTTP methods + | +51 | urllib3.request(...) # ASYNC210 +52 | +53 | urllib.request.urlopen("") # ASYNC210 + | ^^^^^^^^^^^^^^^^^^^^^^ ASYNC210 +54 | +55 | r = {} + | diff --git a/crates/ruff_linter/src/rules/flake8_async/snapshots/ruff_linter__rules__flake8_async__tests__ASYNC220_ASYNC22x.py.snap b/crates/ruff_linter/src/rules/flake8_async/snapshots/ruff_linter__rules__flake8_async__tests__ASYNC220_ASYNC22x.py.snap new file mode 100644 index 0000000000..e7db488fa6 --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_async/snapshots/ruff_linter__rules__flake8_async__tests__ASYNC220_ASYNC22x.py.snap @@ -0,0 +1,77 @@ +--- +source: crates/ruff_linter/src/rules/flake8_async/mod.rs +--- +ASYNC22x.py:31:5: ASYNC220 Async functions should not create subprocesses with blocking methods + | +29 | subprocess.getoutput() # ASYNC221 +30 | ) +31 | subprocess.Popen() # ASYNC220 + | ^^^^^^^^^^^^^^^^ ASYNC220 +32 | os.system() # ASYNC221 + | + +ASYNC22x.py:73:5: ASYNC220 Async functions should not create subprocesses with blocking methods + | +72 | # if mode is given, and is not os.P_WAIT: ASYNC220 +73 | os.spawnl(os.P_NOWAIT) # ASYNC220 + | ^^^^^^^^^ ASYNC220 +74 | os.spawnl(P_NOWAIT) # ASYNC220 +75 | os.spawnl(mode=os.P_NOWAIT) # ASYNC220 + | + +ASYNC22x.py:74:5: ASYNC220 Async functions should not create subprocesses with blocking methods + | +72 | # if mode is given, and is not os.P_WAIT: ASYNC220 +73 | os.spawnl(os.P_NOWAIT) # ASYNC220 +74 | os.spawnl(P_NOWAIT) # ASYNC220 + | ^^^^^^^^^ ASYNC220 +75 | os.spawnl(mode=os.P_NOWAIT) # ASYNC220 +76 | os.spawnl(mode=P_NOWAIT) # ASYNC220 + | + +ASYNC22x.py:75:5: ASYNC220 Async functions should not create subprocesses with blocking methods + | +73 | os.spawnl(os.P_NOWAIT) # ASYNC220 +74 | os.spawnl(P_NOWAIT) # ASYNC220 +75 | os.spawnl(mode=os.P_NOWAIT) # ASYNC220 + | ^^^^^^^^^ ASYNC220 +76 | os.spawnl(mode=P_NOWAIT) # ASYNC220 + | + +ASYNC22x.py:76:5: ASYNC220 Async functions should not create subprocesses with blocking methods + | +74 | os.spawnl(P_NOWAIT) # ASYNC220 +75 | os.spawnl(mode=os.P_NOWAIT) # ASYNC220 +76 | os.spawnl(mode=P_NOWAIT) # ASYNC220 + | ^^^^^^^^^ ASYNC220 +77 | +78 | P_WAIT = os.P_WAIT + | + +ASYNC22x.py:86:5: ASYNC220 Async functions should not create subprocesses with blocking methods + | +85 | # other weird cases: ASYNC220 +86 | os.spawnl(0) # ASYNC220 + | ^^^^^^^^^ ASYNC220 +87 | os.spawnl(1) # ASYNC220 +88 | os.spawnl(foo()) # ASYNC220 + | + +ASYNC22x.py:87:5: ASYNC220 Async functions should not create subprocesses with blocking methods + | +85 | # other weird cases: ASYNC220 +86 | os.spawnl(0) # ASYNC220 +87 | os.spawnl(1) # ASYNC220 + | ^^^^^^^^^ ASYNC220 +88 | os.spawnl(foo()) # ASYNC220 + | + +ASYNC22x.py:88:5: ASYNC220 Async functions should not create subprocesses with blocking methods + | +86 | os.spawnl(0) # ASYNC220 +87 | os.spawnl(1) # ASYNC220 +88 | os.spawnl(foo()) # ASYNC220 + | ^^^^^^^^^ ASYNC220 +89 | +90 | # ASYNC222 + | diff --git a/crates/ruff_linter/src/rules/flake8_async/snapshots/ruff_linter__rules__flake8_async__tests__ASYNC221_ASYNC22x.py.snap b/crates/ruff_linter/src/rules/flake8_async/snapshots/ruff_linter__rules__flake8_async__tests__ASYNC221_ASYNC22x.py.snap new file mode 100644 index 0000000000..59fa094dc3 --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_async/snapshots/ruff_linter__rules__flake8_async__tests__ASYNC221_ASYNC22x.py.snap @@ -0,0 +1,226 @@ +--- +source: crates/ruff_linter/src/rules/flake8_async/mod.rs +--- +ASYNC22x.py:8:5: ASYNC221 Async functions should not run processes with blocking methods + | +7 | async def func(): +8 | subprocess.run("foo") # ASYNC221 + | ^^^^^^^^^^^^^^ ASYNC221 + | + +ASYNC22x.py:12:5: ASYNC221 Async functions should not run processes with blocking methods + | +11 | async def func(): +12 | subprocess.call("foo") # ASYNC221 + | ^^^^^^^^^^^^^^^ ASYNC221 + | + +ASYNC22x.py:29:9: ASYNC221 Async functions should not run processes with blocking methods + | +27 | async def foo(): +28 | await async_fun( +29 | subprocess.getoutput() # ASYNC221 + | ^^^^^^^^^^^^^^^^^^^^ ASYNC221 +30 | ) +31 | subprocess.Popen() # ASYNC220 + | + +ASYNC22x.py:32:5: ASYNC221 Async functions should not run processes with blocking methods + | +30 | ) +31 | subprocess.Popen() # ASYNC220 +32 | os.system() # ASYNC221 + | ^^^^^^^^^ ASYNC221 +33 | +34 | system() + | + +ASYNC22x.py:38:5: ASYNC221 Async functions should not run processes with blocking methods + | +36 | os.anything() +37 | +38 | subprocess.run() # ASYNC221 + | ^^^^^^^^^^^^^^ ASYNC221 +39 | subprocess.call() # ASYNC221 +40 | subprocess.check_call() # ASYNC221 + | + +ASYNC22x.py:39:5: ASYNC221 Async functions should not run processes with blocking methods + | +38 | subprocess.run() # ASYNC221 +39 | subprocess.call() # ASYNC221 + | ^^^^^^^^^^^^^^^ ASYNC221 +40 | subprocess.check_call() # ASYNC221 +41 | subprocess.check_output() # ASYNC221 + | + +ASYNC22x.py:40:5: ASYNC221 Async functions should not run processes with blocking methods + | +38 | subprocess.run() # ASYNC221 +39 | subprocess.call() # ASYNC221 +40 | subprocess.check_call() # ASYNC221 + | ^^^^^^^^^^^^^^^^^^^^^ ASYNC221 +41 | subprocess.check_output() # ASYNC221 +42 | subprocess.getoutput() # ASYNC221 + | + +ASYNC22x.py:41:5: ASYNC221 Async functions should not run processes with blocking methods + | +39 | subprocess.call() # ASYNC221 +40 | subprocess.check_call() # ASYNC221 +41 | subprocess.check_output() # ASYNC221 + | ^^^^^^^^^^^^^^^^^^^^^^^ ASYNC221 +42 | subprocess.getoutput() # ASYNC221 +43 | subprocess.getstatusoutput() # ASYNC221 + | + +ASYNC22x.py:42:5: ASYNC221 Async functions should not run processes with blocking methods + | +40 | subprocess.check_call() # ASYNC221 +41 | subprocess.check_output() # ASYNC221 +42 | subprocess.getoutput() # ASYNC221 + | ^^^^^^^^^^^^^^^^^^^^ ASYNC221 +43 | subprocess.getstatusoutput() # ASYNC221 + | + +ASYNC22x.py:43:5: ASYNC221 Async functions should not run processes with blocking methods + | +41 | subprocess.check_output() # ASYNC221 +42 | subprocess.getoutput() # ASYNC221 +43 | subprocess.getstatusoutput() # ASYNC221 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ ASYNC221 +44 | +45 | await async_fun( + | + +ASYNC22x.py:46:9: ASYNC221 Async functions should not run processes with blocking methods + | +45 | await async_fun( +46 | subprocess.getoutput() # ASYNC221 + | ^^^^^^^^^^^^^^^^^^^^ ASYNC221 +47 | ) + | + +ASYNC22x.py:54:5: ASYNC221 Async functions should not run processes with blocking methods + | +52 | subprocess() +53 | +54 | os.posix_spawn() # ASYNC221 + | ^^^^^^^^^^^^^^ ASYNC221 +55 | os.posix_spawnp() # ASYNC221 + | + +ASYNC22x.py:55:5: ASYNC221 Async functions should not run processes with blocking methods + | +54 | os.posix_spawn() # ASYNC221 +55 | os.posix_spawnp() # ASYNC221 + | ^^^^^^^^^^^^^^^ ASYNC221 +56 | +57 | os.spawn() + | + +ASYNC22x.py:61:5: ASYNC221 Async functions should not run processes with blocking methods + | +59 | os.spawnllll() +60 | +61 | os.spawnl() # ASYNC221 + | ^^^^^^^^^ ASYNC221 +62 | os.spawnle() # ASYNC221 +63 | os.spawnlp() # ASYNC221 + | + +ASYNC22x.py:62:5: ASYNC221 Async functions should not run processes with blocking methods + | +61 | os.spawnl() # ASYNC221 +62 | os.spawnle() # ASYNC221 + | ^^^^^^^^^^ ASYNC221 +63 | os.spawnlp() # ASYNC221 +64 | os.spawnlpe() # ASYNC221 + | + +ASYNC22x.py:63:5: ASYNC221 Async functions should not run processes with blocking methods + | +61 | os.spawnl() # ASYNC221 +62 | os.spawnle() # ASYNC221 +63 | os.spawnlp() # ASYNC221 + | ^^^^^^^^^^ ASYNC221 +64 | os.spawnlpe() # ASYNC221 +65 | os.spawnv() # ASYNC221 + | + +ASYNC22x.py:64:5: ASYNC221 Async functions should not run processes with blocking methods + | +62 | os.spawnle() # ASYNC221 +63 | os.spawnlp() # ASYNC221 +64 | os.spawnlpe() # ASYNC221 + | ^^^^^^^^^^^ ASYNC221 +65 | os.spawnv() # ASYNC221 +66 | os.spawnve() # ASYNC221 + | + +ASYNC22x.py:65:5: ASYNC221 Async functions should not run processes with blocking methods + | +63 | os.spawnlp() # ASYNC221 +64 | os.spawnlpe() # ASYNC221 +65 | os.spawnv() # ASYNC221 + | ^^^^^^^^^ ASYNC221 +66 | os.spawnve() # ASYNC221 +67 | os.spawnvp() # ASYNC221 + | + +ASYNC22x.py:66:5: ASYNC221 Async functions should not run processes with blocking methods + | +64 | os.spawnlpe() # ASYNC221 +65 | os.spawnv() # ASYNC221 +66 | os.spawnve() # ASYNC221 + | ^^^^^^^^^^ ASYNC221 +67 | os.spawnvp() # ASYNC221 +68 | os.spawnvpe() # ASYNC221 + | + +ASYNC22x.py:67:5: ASYNC221 Async functions should not run processes with blocking methods + | +65 | os.spawnv() # ASYNC221 +66 | os.spawnve() # ASYNC221 +67 | os.spawnvp() # ASYNC221 + | ^^^^^^^^^^ ASYNC221 +68 | os.spawnvpe() # ASYNC221 + | + +ASYNC22x.py:68:5: ASYNC221 Async functions should not run processes with blocking methods + | +66 | os.spawnve() # ASYNC221 +67 | os.spawnvp() # ASYNC221 +68 | os.spawnvpe() # ASYNC221 + | ^^^^^^^^^^^ ASYNC221 +69 | +70 | P_NOWAIT = os.P_NOWAIT + | + +ASYNC22x.py:81:5: ASYNC221 Async functions should not run processes with blocking methods + | +80 | # if it is P_WAIT, ASYNC221 +81 | os.spawnl(P_WAIT) # ASYNC221 + | ^^^^^^^^^ ASYNC221 +82 | os.spawnl(mode=os.P_WAIT) # ASYNC221 +83 | os.spawnl(mode=P_WAIT) # ASYNC221 + | + +ASYNC22x.py:82:5: ASYNC221 Async functions should not run processes with blocking methods + | +80 | # if it is P_WAIT, ASYNC221 +81 | os.spawnl(P_WAIT) # ASYNC221 +82 | os.spawnl(mode=os.P_WAIT) # ASYNC221 + | ^^^^^^^^^ ASYNC221 +83 | os.spawnl(mode=P_WAIT) # ASYNC221 + | + +ASYNC22x.py:83:5: ASYNC221 Async functions should not run processes with blocking methods + | +81 | os.spawnl(P_WAIT) # ASYNC221 +82 | os.spawnl(mode=os.P_WAIT) # ASYNC221 +83 | os.spawnl(mode=P_WAIT) # ASYNC221 + | ^^^^^^^^^ ASYNC221 +84 | +85 | # other weird cases: ASYNC220 + | diff --git a/crates/ruff_linter/src/rules/flake8_async/snapshots/ruff_linter__rules__flake8_async__tests__ASYNC222_ASYNC22x.py.snap b/crates/ruff_linter/src/rules/flake8_async/snapshots/ruff_linter__rules__flake8_async__tests__ASYNC222_ASYNC22x.py.snap new file mode 100644 index 0000000000..0c9e675ffa --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_async/snapshots/ruff_linter__rules__flake8_async__tests__ASYNC222_ASYNC22x.py.snap @@ -0,0 +1,64 @@ +--- +source: crates/ruff_linter/src/rules/flake8_async/mod.rs +--- +ASYNC22x.py:20:5: ASYNC222 Async functions should not wait on processes with blocking methods + | +19 | async def func(): +20 | os.wait4(10) # ASYNC222 + | ^^^^^^^^ ASYNC222 + | + +ASYNC22x.py:24:5: ASYNC222 Async functions should not wait on processes with blocking methods + | +23 | async def func(): +24 | os.wait(12) # ASYNC222 + | ^^^^^^^ ASYNC222 + | + +ASYNC22x.py:91:5: ASYNC222 Async functions should not wait on processes with blocking methods + | +90 | # ASYNC222 +91 | os.wait() # ASYNC222 + | ^^^^^^^ ASYNC222 +92 | os.wait3() # ASYNC222 +93 | os.wait4() # ASYNC222 + | + +ASYNC22x.py:92:5: ASYNC222 Async functions should not wait on processes with blocking methods + | +90 | # ASYNC222 +91 | os.wait() # ASYNC222 +92 | os.wait3() # ASYNC222 + | ^^^^^^^^ ASYNC222 +93 | os.wait4() # ASYNC222 +94 | os.waitid() # ASYNC222 + | + +ASYNC22x.py:93:5: ASYNC222 Async functions should not wait on processes with blocking methods + | +91 | os.wait() # ASYNC222 +92 | os.wait3() # ASYNC222 +93 | os.wait4() # ASYNC222 + | ^^^^^^^^ ASYNC222 +94 | os.waitid() # ASYNC222 +95 | os.waitpid() # ASYNC222 + | + +ASYNC22x.py:94:5: ASYNC222 Async functions should not wait on processes with blocking methods + | +92 | os.wait3() # ASYNC222 +93 | os.wait4() # ASYNC222 +94 | os.waitid() # ASYNC222 + | ^^^^^^^^^ ASYNC222 +95 | os.waitpid() # ASYNC222 + | + +ASYNC22x.py:95:5: ASYNC222 Async functions should not wait on processes with blocking methods + | +93 | os.wait4() # ASYNC222 +94 | os.waitid() # ASYNC222 +95 | os.waitpid() # ASYNC222 + | ^^^^^^^^^^ ASYNC222 +96 | +97 | os.waitpi() + | diff --git a/crates/ruff_linter/src/rules/flake8_async/snapshots/ruff_linter__rules__flake8_async__tests__ASYNC230_ASYNC230.py.snap b/crates/ruff_linter/src/rules/flake8_async/snapshots/ruff_linter__rules__flake8_async__tests__ASYNC230_ASYNC230.py.snap new file mode 100644 index 0000000000..c0b5faf4c4 --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_async/snapshots/ruff_linter__rules__flake8_async__tests__ASYNC230_ASYNC230.py.snap @@ -0,0 +1,101 @@ +--- +source: crates/ruff_linter/src/rules/flake8_async/mod.rs +--- +ASYNC230.py:6:5: ASYNC230 Async functions should not open files with blocking methods like `open` + | +5 | async def foo(): +6 | open("") # ASYNC230 + | ^^^^ ASYNC230 +7 | io.open_code("") # ASYNC230 + | + +ASYNC230.py:7:5: ASYNC230 Async functions should not open files with blocking methods like `open` + | +5 | async def foo(): +6 | open("") # ASYNC230 +7 | io.open_code("") # ASYNC230 + | ^^^^^^^^^^^^ ASYNC230 +8 | +9 | with open(""): # ASYNC230 + | + +ASYNC230.py:9:10: ASYNC230 Async functions should not open files with blocking methods like `open` + | + 7 | io.open_code("") # ASYNC230 + 8 | + 9 | with open(""): # ASYNC230 + | ^^^^ ASYNC230 +10 | ... + | + +ASYNC230.py:12:10: ASYNC230 Async functions should not open files with blocking methods like `open` + | +10 | ... +11 | +12 | with open("") as f: # ASYNC230 + | ^^^^ ASYNC230 +13 | ... + | + +ASYNC230.py:15:17: ASYNC230 Async functions should not open files with blocking methods like `open` + | +13 | ... +14 | +15 | with foo(), open(""): # ASYNC230 + | ^^^^ ASYNC230 +16 | ... + | + +ASYNC230.py:18:16: ASYNC230 Async functions should not open files with blocking methods like `open` + | +16 | ... +17 | +18 | async with open(""): # ASYNC230 + | ^^^^ ASYNC230 +19 | ... + | + +ASYNC230.py:29:5: ASYNC230 Async functions should not open files with blocking methods like `open` + | +28 | async def func(): +29 | open("foo") # ASYNC230 + | ^^^^ ASYNC230 + | + +ASYNC230.py:36:5: ASYNC230 Async functions should not open files with blocking methods like `open` + | +35 | async def func(): +36 | Path("foo").open() # ASYNC230 + | ^^^^^^^^^^^^^^^^ ASYNC230 + | + +ASYNC230.py:41:5: ASYNC230 Async functions should not open files with blocking methods like `open` + | +39 | async def func(): +40 | p = Path("foo") +41 | p.open() # ASYNC230 + | ^^^^^^ ASYNC230 + | + +ASYNC230.py:45:10: ASYNC230 Async functions should not open files with blocking methods like `open` + | +44 | async def func(): +45 | with Path("foo").open() as f: # ASYNC230 + | ^^^^^^^^^^^^^^^^ ASYNC230 +46 | pass + | + +ASYNC230.py:53:9: ASYNC230 Async functions should not open files with blocking methods like `open` + | +52 | async def bar(): +53 | p.open() # ASYNC230 + | ^^^^^^ ASYNC230 + | + +ASYNC230.py:59:5: ASYNC230 Async functions should not open files with blocking methods like `open` + | +57 | (p1, p2) = (Path("foo"), Path("bar")) +58 | +59 | p1.open() # ASYNC230 + | ^^^^^^^ ASYNC230 + | diff --git a/crates/ruff_linter/src/rules/flake8_async/snapshots/ruff_linter__rules__flake8_async__tests__ASYNC251_ASYNC251.py.snap b/crates/ruff_linter/src/rules/flake8_async/snapshots/ruff_linter__rules__flake8_async__tests__ASYNC251_ASYNC251.py.snap new file mode 100644 index 0000000000..ffafdf9d99 --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_async/snapshots/ruff_linter__rules__flake8_async__tests__ASYNC251_ASYNC251.py.snap @@ -0,0 +1,9 @@ +--- +source: crates/ruff_linter/src/rules/flake8_async/mod.rs +--- +ASYNC251.py:6:5: ASYNC251 Async functions should not call `time.sleep` + | +5 | async def func(): +6 | time.sleep(1) # ASYNC251 + | ^^^^^^^^^^ ASYNC251 + | diff --git a/crates/ruff_linter/src/rules/flake8_trio/mod.rs b/crates/ruff_linter/src/rules/flake8_trio/mod.rs deleted file mode 100644 index 2e0bb7911e..0000000000 --- a/crates/ruff_linter/src/rules/flake8_trio/mod.rs +++ /dev/null @@ -1,31 +0,0 @@ -//! Rules from [flake8-trio](https://pypi.org/project/flake8-trio/). -pub(super) mod method_name; -pub(crate) mod rules; - -#[cfg(test)] -mod tests { - use std::path::Path; - - use anyhow::Result; - use test_case::test_case; - - use crate::assert_messages; - use crate::registry::Rule; - use crate::settings::LinterSettings; - use crate::test::test_path; - - #[test_case(Rule::TrioTimeoutWithoutAwait, Path::new("TRIO100.py"))] - #[test_case(Rule::TrioSyncCall, Path::new("TRIO105.py"))] - #[test_case(Rule::TrioAsyncFunctionWithTimeout, Path::new("TRIO109.py"))] - #[test_case(Rule::TrioUnneededSleep, Path::new("TRIO110.py"))] - #[test_case(Rule::TrioZeroSleepCall, Path::new("TRIO115.py"))] - fn rules(rule_code: Rule, path: &Path) -> Result<()> { - let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy()); - let diagnostics = test_path( - Path::new("flake8_trio").join(path).as_path(), - &LinterSettings::for_rule(rule_code), - )?; - assert_messages!(snapshot, diagnostics); - Ok(()) - } -} diff --git a/crates/ruff_linter/src/rules/flake8_trio/rules/mod.rs b/crates/ruff_linter/src/rules/flake8_trio/rules/mod.rs deleted file mode 100644 index 3126b10224..0000000000 --- a/crates/ruff_linter/src/rules/flake8_trio/rules/mod.rs +++ /dev/null @@ -1,11 +0,0 @@ -pub(crate) use async_function_with_timeout::*; -pub(crate) use sync_call::*; -pub(crate) use timeout_without_await::*; -pub(crate) use unneeded_sleep::*; -pub(crate) use zero_sleep_call::*; - -mod async_function_with_timeout; -mod sync_call; -mod timeout_without_await; -mod unneeded_sleep; -mod zero_sleep_call; diff --git a/crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO100_TRIO100.py.snap b/crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO100_TRIO100.py.snap deleted file mode 100644 index 1d364b2670..0000000000 --- a/crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO100_TRIO100.py.snap +++ /dev/null @@ -1,22 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/flake8_trio/mod.rs ---- -TRIO100.py:5:5: TRIO100 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(): -5 | with trio.fail_after(): - | _____^ -6 | | ... - | |___________^ TRIO100 - | - -TRIO100.py:15:5: TRIO100 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(): -15 | with trio.move_on_after(): - | _____^ -16 | | ... - | |___________^ TRIO100 - | - - diff --git a/crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO109_TRIO109.py.snap b/crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO109_TRIO109.py.snap deleted file mode 100644 index fc08800b67..0000000000 --- a/crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO109_TRIO109.py.snap +++ /dev/null @@ -1,18 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/flake8_trio/mod.rs ---- -TRIO109.py:8:16: TRIO109 Prefer `trio.fail_after` and `trio.move_on_after` over manual `async` timeout behavior - | -8 | async def func(timeout): - | ^^^^^^^ TRIO109 -9 | ... - | - -TRIO109.py:12:16: TRIO109 Prefer `trio.fail_after` and `trio.move_on_after` over manual `async` timeout behavior - | -12 | async def func(timeout=10): - | ^^^^^^^^^^ TRIO109 -13 | ... - | - - diff --git a/crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO110_TRIO110.py.snap b/crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO110_TRIO110.py.snap deleted file mode 100644 index d95be0a65d..0000000000 --- a/crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO110_TRIO110.py.snap +++ /dev/null @@ -1,22 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/flake8_trio/mod.rs ---- -TRIO110.py:5:5: TRIO110 Use `trio.Event` instead of awaiting `trio.sleep` in a `while` loop - | -4 | async def func(): -5 | while True: - | _____^ -6 | | await trio.sleep(10) - | |____________________________^ TRIO110 - | - -TRIO110.py:10:5: TRIO110 Use `trio.Event` instead of awaiting `trio.sleep` in a `while` loop - | - 9 | async def func(): -10 | while True: - | _____^ -11 | | await trio.sleep_until(10) - | |__________________________________^ TRIO110 - | - - diff --git a/crates/ruff_linter/src/rules/mod.rs b/crates/ruff_linter/src/rules/mod.rs index 64b0128cae..6240d93d12 100644 --- a/crates/ruff_linter/src/rules/mod.rs +++ b/crates/ruff_linter/src/rules/mod.rs @@ -37,7 +37,6 @@ pub mod flake8_simplify; pub mod flake8_slots; pub mod flake8_tidy_imports; pub mod flake8_todos; -pub mod flake8_trio; pub mod flake8_type_checking; pub mod flake8_unused_arguments; pub mod flake8_use_pathlib; diff --git a/docs/faq.md b/docs/faq.md index b287b21c21..f388bf7d08 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -81,7 +81,6 @@ natively, including: - [flake8-super](https://pypi.org/project/flake8-super/) - [flake8-tidy-imports](https://pypi.org/project/flake8-tidy-imports/) - [flake8-todos](https://pypi.org/project/flake8-todos/) -- [flake8-trio](https://pypi.org/project/flake8-trio/) ([#8451](https://github.com/astral-sh/ruff/issues/8451)) - [flake8-type-checking](https://pypi.org/project/flake8-type-checking/) - [flake8-use-pathlib](https://pypi.org/project/flake8-use-pathlib/) - [flynt](https://pypi.org/project/flynt/) ([#2102](https://github.com/astral-sh/ruff/issues/2102)) @@ -194,7 +193,6 @@ Today, Ruff can be used to replace Flake8 when used with any of the following pl - [flake8-super](https://pypi.org/project/flake8-super/) - [flake8-tidy-imports](https://pypi.org/project/flake8-tidy-imports/) - [flake8-todos](https://pypi.org/project/flake8-todos/) -- [flake8-trio](https://pypi.org/project/flake8-trio/) ([#8451](https://github.com/astral-sh/ruff/issues/8451)) - [flake8-type-checking](https://pypi.org/project/flake8-type-checking/) - [flake8-use-pathlib](https://pypi.org/project/flake8-use-pathlib/) - [flynt](https://pypi.org/project/flynt/) ([#2102](https://github.com/astral-sh/ruff/issues/2102)) diff --git a/ruff.schema.json b/ruff.schema.json index 2b57c1543a..4c77a7dd89 100644 --- a/ruff.schema.json +++ b/ruff.schema.json @@ -2703,10 +2703,23 @@ "ASYNC1", "ASYNC10", "ASYNC100", - "ASYNC101", - "ASYNC102", + "ASYNC105", + "ASYNC109", "ASYNC11", + "ASYNC110", + "ASYNC115", "ASYNC116", + "ASYNC2", + "ASYNC21", + "ASYNC210", + "ASYNC22", + "ASYNC220", + "ASYNC221", + "ASYNC222", + "ASYNC23", + "ASYNC230", + "ASYNC25", + "ASYNC251", "B", "B0", "B00", @@ -3847,15 +3860,6 @@ "TID251", "TID252", "TID253", - "TRIO", - "TRIO1", - "TRIO10", - "TRIO100", - "TRIO105", - "TRIO109", - "TRIO11", - "TRIO110", - "TRIO115", "TRY", "TRY0", "TRY00",