liveBook · Manning

TimeoutError

As explained in Python Concurrency with asyncio

TimeoutError is a Python exception indicating that an operation exceeded its permitted time limit.

TimeoutError signals that an asynchronous operation exceeded its allowed time. asyncio.wait_for raises it after the limit and automatically cancels the wrapped task. section 2.4.2

Catch the exception when a timeout is expected, then inspect or handle the task. Use asyncio.shield when the operation should continue after the caller’s wait ends. section 2.4.2

Timed task

A two-second delay wrapped in wait_for(timeout=1) raises TimeoutError after roughly one second, and the task reports cancelled=True. Shielding a ten-second task with a five-second wait instead reports the timeout while allowing the task to finish.

Listing 3.10 A graceful shutdown
import asyncio
from asyncio import AbstractEventLoop
import socket
import logging
import signal
from typing import List
 
 
async def echo(connection: socket,
               loop: AbstractEventLoop) -> None:
    try:
        while data := await loop.sock_recv(connection, 1024):
            print('got data!')
            if data == b'boom\r\n':
                raise Exception("Unexpected network error")
            await loop.sock_sendall(connection, data)
    except Exception as ex:
        logging.exception(ex)
    finally:
        connection.close()
 
echo_tasks = []
 
 
async def connection_listener(server_socket, loop):
    while True:
        connection, address = await loop.sock_accept(server_socket)
        connection.setblocking(False)
        print(f"Got a connection from {address}")
        echo_task = asyncio.create_task(echo(connection, loop))
        echo_tasks.append(echo_task)
 
 
class GracefulExit(SystemExit):
    pass
 
 
def shutdown():
    raise GracefulExit()
 
 
async def close_echo_tasks(echo_tasks: List[asyncio.Task]):
    waiters = [asyncio.wait_for(task, 2) for task in echo_tasks]
    for task in waiters:
        try:
            await task
        except asyncio.exceptions.TimeoutError:
            # We expect a timeout error here
            pass
 
 
async def main():
    server_socket = socket.socket()
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
 
    server_address = ('127.0.0.1', 8000)
    server_socket.setblocking(False)
    server_socket.bind(server_address)
    server_socket.listen()
 
    for signame in {'SIGINT', 'SIGTERM'}:
        loop.add_signal_handler(getattr(signal, signame), shutdown)
    await connection_listener(server_socket, loop)
 
 
loop = asyncio.new_event_loop()
 
try:
    loop.run_until_complete(main())
except GracefulExit:
    loop.run_until_complete(close_echo_tasks(echo_tasks))
finally:
    loop.close()

A graceful echo-server shutdown gives pending tasks two seconds to finish, catches expected TimeoutErrors, and then terminates remaining work. section 3.6.2

How Python Concurrency with asyncio covers it

How deep it goes
Explains raising, catching, cancellation, shielding, and shutdown handling through runnable task scenarios.
Examples
Uses delayed tasks, cancellation checks, shielded work, and an echo server that waits during shutdown.
What it assumes
Assumes familiarity with Python coroutines, tasks, await, and event loops while demonstrating timeout handling from concrete examples.
What you can do after
Operate/configure asynchronous tasks with explicit time limits and handle expected timeout-driven cancellation.
How it connects
Connects TimeoutError to asyncio.wait_for, task cancellation, asyncio.shield, event loops, and graceful shutdown.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Python Concurrency with asyncio. The section links point to the parts of the book it draws on.