This rule raises an issue when an asynchronous function accepts a timeout parameter instead of using the timeout context managers.
Modern asynchronous Python libraries like asyncio, anyio, and trio promote a principle called
structured concurrency. A key aspect of this is that the caller of an asynchronous function should be responsible for managing
timeouts and cancellation, not the callee.
When an async function accepts a timeout parameter, it violates this principle:
Instead, the caller should use the timeout features provided by the concurrency library (e.g., async with asyncio.timeout() or
with trio.move_on_after()). This separates the concern of what the function does from how long the caller is willing to wait for it.
import asyncio
async def example_function(timeout): # Noncompliant
await asyncio.sleep(timeout)
async def main():
await example_function(5)
import asyncio
async def example_function():
await asyncio.sleep(5)
async def main():
async with asyncio.timeout(5): # Compliant
await example_function()
import trio
async def example_function(timeout): # Noncompliant
await trio.sleep(timeout)
async def main():
await example_function(5)
trio.run(main)
import trio
async def example_function():
await trio.sleep(5)
async def main():
with trio.move_on_after(5): # Compliant
await example_function()
trio.run(main)
import anyio
async def example_function(timeout): # Noncompliant
await anyio.sleep(timeout)
async def main():
await example_function(5)
anyio.run(main)
import anyio
async def example_function():
await anyio.sleep(5)
async def main():
with anyio.move_on_after(5): # Compliant
await example_function()
anyio.run(main)