A context variable stores state local to an asynchronous task. Inner coroutines and tasks inherit access to it, while unrelated tasks cannot see or modify it. Unlike thread-local state, it works within asyncio’s single-threaded concurrency model, letting APIs share task-specific state without adding explicit parameters. section 14.2
Tracking a connected client
A server defines ContextVar('user_address'), sets it when a client connects, and lets the message-listening task print the client address alongside each message. section 14.2
Listing 14.2 A server with a context variables
import asyncio
from asyncio import StreamReader, StreamWriter
from contextvars import ContextVar
class Server:
user_address = ContextVar('user_address') #1
def __init__(self, host: str, port: int):
self.host = host
self.port = port
async def start_server(self):
server = await asyncio.start_server(self._client_connected,
self.host, self.port)
await server.serve_forever()
def _client_connected(self, reader: StreamReader, writer: StreamWriter):
self.user_address.set(writer.get_extra_info('peername')) #2
asyncio.create_task(self.listen_for_messages(reader))
async def listen_for_messages(self, reader: StreamReader):
while data := await reader.readline():
print(f'Got message {data} from {self.user_address.get()}') #3
async def main():
server = Server('127.0.0.1', 9000)
await server.start_server()
asyncio.run(main())
Runnable server listing showing ContextVar creation, connection-time assignment, task creation, and per-client message output. section 14.2

