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:
- Coupling between logic and timeout handling: The function dictates how the timeout is handled internally, rather than letting
the caller decide.
- Preempting caller control: The caller might want to enforce a different timeout duration or coordinate timeouts across several
concurrent operations. An internal timeout parameter makes this difficult or impossible.
- Reducing composability: Combining functions that manage their own timeouts can lead to complex and unpredictable behavior,
especially when nesting calls or running tasks concurrently under a shared deadline.
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.