flake8-async
flake8-async is a highly opinionated Flake8 plugin designed to identify common problems, potential bugs, dead code, performance issues, and idiom violations specific to asynchronous programming with Trio, AnyIO, and asyncio. It is actively maintained, with frequent releases following a CalVer (YY.month.patch) scheme. The plugin integrates with the standard `flake8` tool and some of its checks are also incorporated into the `ruff` linter.
Common errors
-
flake8: ASYNC100 No await statements in timeout context
cause A `trio.fail_after` or `trio.move_on_after` block does not contain any `await` expressions, meaning the timeout cannot take effect.fixEnsure that the `with` block includes at least one `await` call. For example, add an `await trio.sleep(0)` if a checkpoint is needed without blocking for time. -
flake8: ASYNC110 while <condition>: await trio.sleep() should be replaced by a trio.Event
cause The code is using a `while` loop with `await trio.sleep()` for busy-waiting, which is inefficient and leads to a trade-off between responsiveness and CPU usage.fixRefactor the code to use an event object (e.g., `trio.Event`, `anyio.Event`, `asyncio.Event`). Instead of `while not condition: await trio.sleep(...)`, use `await event.wait()` to pause until the condition is met efficiently. -
flake8: TRIOxxx (or similar 'undefined name') while using old TRIO error codes after upgrade.
cause You are using old `TRIOxxx` error codes from `flake8-trio` after upgrading to `flake8-async` (or newer `flake8-async` which was a merge). The codes were renamed to `ASYNCxxx`.fixUpdate all instances of `TRIOxxx` error codes (e.g., in `pyproject.toml`, `.flake8` config, or inline `# noqa` comments) to their new `ASYNCxxx` counterparts. Consult the `flake8-async` documentation for the mapping if needed.
Warnings
- breaking The library was renamed from `flake8-trio` to `flake8-async` around version 24.3.1. Consequently, all error codes were renamed from `TRIOxxx` to `ASYNCxxx` (e.g., `TRIO100` became `ASYNC100`). Users upgrading from `flake8-trio` or older `flake8-async` versions need to update their configurations and `# noqa` comments.
- gotcha For `flake8-async` to correctly parse and apply configurations from `pyproject.toml`, `setup.cfg`, or other Flake8 configuration files, you must install `flake8` as an optional dependency using the `[flake8]` extra. Without this, the plugin will only respect command-line arguments.
- gotcha The `ASYNC100` rule warns about `with trio.fail_after(...)` or `with trio.move_on_after(...):` context managers that do not contain any `await` statements. This renders the timeout context ineffective, as timeouts can only be triggered by explicit checkpoints (like `await`).
- gotcha Rules like `ASYNC210-ASYNC251` detect blocking synchronous I/O, process, or OS calls (e.g., `time.sleep()`, `open()`, `subprocess.run()`, `requests.get()`) within `async def` functions. These calls will block the event loop, severely degrading concurrency and application responsiveness.
Install
-
pip install flake8-async -
pip install "flake8-async[flake8]"
Quickstart
import trio
async def busy_wait_function():
done = False
while not done:
await trio.sleep(0.001) # ASYNC110: async-busy-wait
# In a real scenario, 'done' would be set by some other async operation
async def main():
async with trio.open_nursery() as nursery:
nursery.start_soon(busy_wait_function)
if __name__ == '__main__':
# To run flake8-async, save this as a .py file (e.g., 'my_async_code.py')
# and then run: flake8 my_async_code.py
# It should report ASYNC110.
pass