Lib/selectors.py
cpython 3.14 @ ab2d84fe1023/Lib/selectors.py
selectors presents a single DefaultSelector class that resolves at
import time to the most efficient selector available on the current
platform. All selector classes share the BaseSelector ABC, which
defines register, unregister, modify, select, close, and
get_map. The SelectorKey named tuple carries the registered file
object, its underlying file descriptor, the event mask, and optional user
data.
The event constants EVENT_READ (1) and EVENT_WRITE (4) match the bits
used by select.POLLIN and select.POLLOUT so that PollSelector can
pass them directly to poll.register without translation.
Map
| Lines | Symbol | Role | gopy |
|---|---|---|---|
| 1-60 | EVENT_READ, EVENT_WRITE, SelectorKey, _fileobj_lookup | Constants and the helper that normalises any file-like object to its integer fd via fileno(). | (stdlib pending) |
| 61-150 | BaseSelector | ABC declaring register, unregister, modify, select, close, get_key, get_map; implements __enter__/__exit__ and modify as unregister+register. | (stdlib pending) |
| 151-230 | _BaseSelectorImpl | Concrete base that stores keys in self._fd_to_key; supplies the _fileobj_lookup normalization and get_map implementation used by all subclasses. | (stdlib pending) |
| 231-300 | SelectSelector | Uses select.select(); works on every platform including Windows; splits the registered fds into read-set and write-set for the syscall. | (stdlib pending) |
| 301-370 | PollSelector | Uses select.poll(); available on Linux and most POSIX systems; a single poll object holds all registrations. | (stdlib pending) |
| 371-450 | EpollSelector | Uses select.epoll(); Linux only; edge-triggered or level-triggered; fileno() returns the epoll fd itself for nested use. | (stdlib pending) |
| 451-530 | KqueueSelector | Uses select.kqueue(); macOS and BSD; registers separate kevent filters for reads and writes. | (stdlib pending) |
| 531-600 | DefaultSelector, platform resolution | Picks the best available class at import time; order is EpollSelector, KqueueSelector, PollSelector, SelectSelector. | (stdlib pending) |
Reading
BaseSelector.register protocol (lines 61 to 150)
cpython 3.14 @ ab2d84fe1023/Lib/selectors.py#L61-150
class BaseSelector(metaclass=ABCMeta):
@abstractmethod
def register(self, fileobj, events, data=None):
"""Register a file object for I/O events.
Returns a SelectorKey instance.
"""
raise NotImplementedError
def modify(self, fileobj, events, data=None):
self.unregister(fileobj)
return self.register(fileobj, events, data)
def __enter__(self):
return self
def __exit__(self, *args):
self.close()
register returns a SelectorKey(fileobj, fd, events, data) named
tuple. The fd field is the integer file descriptor extracted by
_fileobj_lookup; the fileobj field retains the original object
(socket, file, integer) as passed by the caller. Storing both lets
callers retrieve the original object from the key without needing to
keep an external mapping.
modify is provided as a default implementation that unregisters then
re-registers. Subclasses with incremental update syscalls (e.g., epoll
EPOLL_CTL_MOD) override it for efficiency.
The ABC is a context manager: __exit__ calls close(), which on
EpollSelector and KqueueSelector closes the underlying kernel object.
EpollSelector.select (lines 371 to 450)
cpython 3.14 @ ab2d84fe1023/Lib/selectors.py#L371-450
class EpollSelector(_BaseSelectorImpl):
def __init__(self):
super().__init__()
self._epoll = select.epoll()
def register(self, fileobj, events, data=None):
key = super().register(fileobj, events, data)
epoll_events = 0
if events & EVENT_READ:
epoll_events |= select.EPOLLIN
if events & EVENT_WRITE:
epoll_events |= select.EPOLLOUT
try:
self._epoll.register(key.fd, epoll_events)
except BaseException:
super().unregister(fileobj)
raise
return key
def select(self, timeout=None):
if timeout is None:
timeout = -1
elif timeout <= 0:
timeout = 0
else:
timeout = math.ceil(timeout * 1e3) * 1e-3
max_ev = max(len(self._fd_to_key), 1)
ready = []
try:
fd_event_list = self._epoll.poll(timeout, max_ev)
except InterruptedError:
return ready
for fd, event in fd_event_list:
events = 0
if event & ~select.EPOLLIN:
events |= EVENT_WRITE
if event & ~select.EPOLLOUT:
events |= EVENT_READ
key = self._fd_to_key.get(fd)
if key:
ready.append((key, events & key.events))
return ready
EpollSelector translates EVENT_READ/EVENT_WRITE to EPOLLIN/
EPOLLOUT on registration and back again on select. The max_ev
argument to epoll.poll bounds the number of events returned per call;
using the current registration count avoids allocating a fixed large
buffer while still returning all pending events in one syscall.
The InterruptedError catch at line 425 (approximately) handles
EINTR: a signal arriving during epoll_wait raises
InterruptedError in Python 3.5+. Returning an empty list on interrupt
is correct; the caller's event loop will call select again.
DefaultSelector platform choice (lines 531 to 600)
cpython 3.14 @ ab2d84fe1023/Lib/selectors.py#L531-600
# Choose the best implementation, roughly:
# epoll|kqueue|devpoll > poll > select.
# select() also can't accept a FD > FD_SETSIZE (usually around 1024)
if 'KqueueSelector' in globals():
DefaultSelector = KqueueSelector
elif 'EpollSelector' in globals():
DefaultSelector = EpollSelector
elif 'PollSelector' in globals():
DefaultSelector = PollSelector
else:
DefaultSelector = SelectSelector
Each selector class is defined only if the underlying select module
attribute exists. For example, EpollSelector is wrapped in
if hasattr(select, 'epoll'). The DefaultSelector assignment at the
bottom picks the best available implementation for the current platform.
asyncio uses DefaultSelector by default, so this choice determines the
I/O scalability of the event loop on each OS.
gopy mirror
selectors.py is pure Python and depends only on the select C
extension. Porting requires select.select() at minimum for
SelectSelector, and select.epoll() or select.kqueue() for the
faster backends. Until those are available, SelectSelector provides a
complete cross-platform implementation that passes the test suite.
SelectorKey is a collections.namedtuple and requires no special
treatment.