Timeouts and cancellation
Never wait forever on something outside your control. asyncio.timeout cancels the work inside it when time runs out and raises TimeoutError.
import asyncio
async def slow():
await asyncio.sleep(10)
async def main():
try:
async with asyncio.timeout(0.01):
await slow()
except TimeoutError:
print("gave up")
asyncio.run(main())asyncio.wait_for(aw, seconds) does the same for a single awaitable.
How cancellation works
task.cancel() raises CancelledError inside the task, at the await where it is waiting. The task may clean up in finally or in an except block, but it must let the error continue, so an except block ends with raise. A task that swallows CancelledError looks finished to whoever cancelled it, and timeouts built on cancellation stop working.
CancelledError is a BaseException, so a plain except Exception does not catch it by accident.
Your task
Write async def with_timeout(aw, seconds, default) that returns the result of awaiting aw, or default if that takes longer than seconds. Write async def guarded(aw, log) that returns the result of awaiting aw; if it is cancelled, it appends "cancelled" to log and lets the cancellation continue.
The explanation opens here once every check passes.