Python selectors Module

The selectors module in Python provides a high-level I/O multiplexing interface built on top of the lower-level select module primitives (select, poll, epoll, and kqueue). This module allows you to manage multiple I/O events in a more efficient and portable way.

Table of Contents

  1. Introduction
  2. Key Classes and Methods
    • DefaultSelector
    • EVENT_READ
    • EVENT_WRITE
    • register
    • unregister
    • select
  3. Examples
    • Basic Usage of selectors
    • Creating a Simple Server
    • Handling Multiple Connections
    • Real-World Use Case: Chat Server
  4. Conclusion
  5. References

Introduction

The selectors module provides a unified interface for monitoring multiple I/O events, making it easier to write portable and efficient I/O code. It abstracts the platform-specific details and allows you to work with a common interface for handling I/O readiness.

Key Classes and Methods

DefaultSelector

Creates a default selector object which automatically selects the most efficient implementation available on the platform (epoll on Linux, kqueue on BSD, select on Windows, etc.).

import selectors

sel = selectors.DefaultSelector()

EVENT_READ

Constant to indicate that the file object is ready for reading.

selectors.EVENT_READ

EVENT_WRITE

Constant to indicate that the file object is ready for writing.

selectors.EVENT_WRITE

register

Registers a file object for monitoring.

sel.register(fileobj, selectors.EVENT_READ | selectors.EVENT_WRITE, data)

unregister

Unregisters a file object from monitoring.

sel.unregister(fileobj)

select

Monitors registered file objects and returns a list of ready file objects.

events = sel.select(timeout)

Examples

Basic Usage of selectors

import selectors
import socket

sel = selectors.DefaultSelector()

def accept(sock):
    conn, addr = sock.accept()
    print(f'Accepted connection from {addr}')
    conn.setblocking(False)
    sel.register(conn, selectors.EVENT_READ, read)

def read(conn):
    data = conn.recv(1024)
    if data:
        print(f'Received data: {data}')
        conn.send(data)  # Echo back the data
    else:
        print('Closing connection')
        sel.unregister(conn)
        conn.close()

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('localhost', 12345))
sock.listen()
sock.setblocking(False)
sel.register(sock, selectors.EVENT_READ, accept)

while True:
    events = sel.select()
    for key, mask in events:
        callback = key.data
        callback(key.fileobj)

Creating a Simple Server

import selectors
import socket

sel = selectors.DefaultSelector()

def accept(sock, mask):
    conn, addr = sock.accept()
    print(f'Accepted connection from {addr}')
    conn.setblocking(False)
    sel.register(conn, selectors.EVENT_READ, read)

def read(conn, mask):
    data = conn.recv(1024)
    if data:
        print(f'Received data: {data}')
        conn.send(data)
    else:
        print('Closing connection')
        sel.unregister(conn)
        conn.close()

sock = socket.socket()
sock.bind(('localhost', 12345))
sock.listen(100)
sock.setblocking(False)
sel.register(sock, selectors.EVENT_READ, accept)

while True:
    events = sel.select()
    for key, mask in events:
        callback = key.data
        callback(key.fileobj, mask)

Handling Multiple Connections

import selectors
import socket

sel = selectors.DefaultSelector()

def accept(sock, mask):
    conn, addr = sock.accept()
    print(f'Accepted connection from {addr}')
    conn.setblocking(False)
    sel.register(conn, selectors.EVENT_READ, read)

def read(conn, mask):
    data = conn.recv(1024)
    if data:
        print(f'Received data: {data}')
        conn.send(data)
    else:
        print('Closing connection')
        sel.unregister(conn)
        conn.close()

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('localhost', 12345))
sock.listen()
sock.setblocking(False)
sel.register(sock, selectors.EVENT_READ, accept)

while True:
    events = sel.select()
    for key, mask in events:
        callback = key.data
        callback(key.fileobj, mask)

Real-World Use Case: Chat Server

import selectors
import socket

sel = selectors.DefaultSelector()
clients = {}

def accept(sock, mask):
    conn, addr = sock.accept()
    print(f'Accepted connection from {addr}')
    conn.setblocking(False)
    sel.register(conn, selectors.EVENT_READ, read)
    clients[conn] = addr

def read(conn, mask):
    data = conn.recv(1024)
    if data:
        print(f'Received data from {clients[conn]}: {data.decode()}')
        broadcast(data, conn)
    else:
        print(f'Closing connection to {clients[conn]}')
        sel.unregister(conn)
        conn.close()
        del clients[conn]

def broadcast(message, sender):
    for conn in clients:
        if conn != sender:
            conn.send(message)

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('localhost', 12345))
sock.listen()
sock.setblocking(False)
sel.register(sock, selectors.EVENT_READ, accept)

print('Chat server started on port 12345')

while True:
    events = sel.select()
    for key, mask in events:
        callback = key.data
        callback(key.fileobj, mask)

Conclusion

The selectors module in Python provides a high-level interface for efficient I/O multiplexing, making it easier to write portable and efficient network code. By abstracting the platform-specific details, it allows you to focus on building your application without worrying about the underlying system calls.

References

Comments

Spring Boot 3 Paid Course Published for Free
on my Java Guides YouTube Channel

Subscribe to my YouTube Channel (165K+ subscribers):
Java Guides Channel

Top 10 My Udemy Courses with Huge Discount:
Udemy Courses - Ramesh Fadatare