liveBook · Manning

Selectors

As explained in Python Concurrency with asyncio

selectors is a Python standard-library module that provides efficient, portable I/O multiplexing over file objects and sockets.

selectors provides portable, efficient event notification for file objects and sockets. Instead of repeatedly checking every socket, an operating-system notification system reports which registered sockets are ready. section 3.4

The core workflow is small:

  • Create a DefaultSelector, which chooses an efficient platform-specific implementation. section 3.4
  • Register sockets with the read or write events to monitor; deregister them when monitoring is no longer needed. section 3.4
  • Call select with an optional timeout. It blocks until events occur, then returns ready sockets and their triggering events. section 3.4

With non-blocking sockets, this loop can accept connections and process client data without blocking the thread or repeatedly consuming CPU. An infinite select loop reproduces the core machinery of an asyncio event loop. section 3.7

Listing 3.7 Using selectors to build a non-blocking server
import selectors
import socket
from selectors import SelectorKey
from typing import List, Tuple
 
selector = selectors.DefaultSelector()
 
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()
 
selector.register(server_socket, selectors.EVENT_READ)
 
while True:
    events: List[Tuple[SelectorKey, int]] = selector.select(timeout=1)  #1
 
    if len(events) == 0:                                                #2
        print('No events, waiting a bit more!')
 
    for event, _ in events:
        event_socket = event.fileobj                                    #3
 
        if event_socket == server_socket:                               #4
            connection, address = server_socket.accept()
            connection.setblocking(False)
            print(f"I got a connection from {address}")
            selector.register(connection, selectors.EVENT_READ)         #5
        else:
            data = event_socket.recv(1024)                              #6
            print(f"I got some data: {data}")
            event_socket.send(data)

A complete non-blocking echo-server loop registers the server socket, waits for readiness, accepts clients, and processes socket events. section 3.4

How Python Concurrency with asyncio covers it

How deep it goes
Explains registration, deregistration, readiness, timeouts, platform abstraction, and an echo-server loop without descending into notification-system internals.
Examples
Uses a multi-client echo server, socket readiness notifications, CPU utilization near zero to one percent, and an event-loop sketch.
What it assumes
Assumes coroutines, tasks, and event loops, while developing socket behavior from blocking through non-blocking operation.
What you can do after
Build it from scratch: create a non-blocking, multi-client socket server using registration and readiness events.
How it connects
Connects non-blocking sockets, event loops, asyncio, concurrency, tasks, and echo servers.

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.