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.