Network calls can hang forever; every real client needs a timeout.
Since Python 3.11, the preferred tool is the async context manager asyncio.timeout(delay). Everything inside the async with block must finish within delay seconds. If it doesn't, the current task is cancelled at its current await, and on leaving the block the cancellation is converted into a TimeoutError (the built-in; asyncio.TimeoutError is an alias).
async with asyncio.timeout(2):
data = await fetch()The block can contain several awaits, and they share one deadline. asyncio.timeout_at(when) takes an absolute loop time instead, and the returned object's reschedule() lets you extend the deadline. Pass None for no timeout.
asyncio.wait_for(aw, timeout) is the older, single-awaitable form; since 3.12 it is implemented on top of timeout(). It wraps the awaitable, cancels it on timeout and raises TimeoutError.
Cancellation means cleanup in finally blocks of the cancelled code runs. Catch TimeoutError outside the async with block, not inside it, because inside the block you would be catching the cancellation itself.
Example
import asyncio
async def slow():
try:
await asyncio.sleep(10)
finally:
print("slow: cleaning up") # runs on cancellation
async def main():
try:
async with asyncio.timeout(0.2):
await slow()
except TimeoutError:
print("timed out")
print(await asyncio.wait_for(asyncio.sleep(0.01, "fast"), timeout=1))
asyncio.run(main())
# slow: cleaning up
# timed out
# fastRemember
- async with asyncio.timeout(s): sets one deadline for a block
- On expiry the work is cancelled and TimeoutError is raised
- wait_for(aw, timeout) is the single-awaitable form
Watch out for
- Catching TimeoutError inside the timeout block; catch it around the async with.