3
This module allows high-level and efficient I/O multiplexing, built upon the
4
`select` module primitives.
8
from abc import ABCMeta, abstractmethod
9
from collections import namedtuple
10
from collections.abc import Mapping
16
# generic events, that must be mapped to implementation-specific ones
18
EVENT_WRITE = (1 << 1)
21
def _fileobj_to_fd(fileobj):
22
"""Return a file descriptor from a file object.
25
fileobj -- file object or file descriptor
28
corresponding file descriptor
31
ValueError if the object is invalid
33
if isinstance(fileobj, int):
37
fd = int(fileobj.fileno())
38
except (AttributeError, TypeError, ValueError):
39
raise ValueError("Invalid file object: "
40
"{!r}".format(fileobj)) from None
42
raise ValueError("Invalid file descriptor: {}".format(fd))
46
SelectorKey = namedtuple('SelectorKey', ['fileobj', 'fd', 'events', 'data'])
48
SelectorKey.__doc__ = """SelectorKey(fileobj, fd, events, data)
50
Object used to associate a file object to its backing
51
file descriptor, selected event mask, and attached data.
53
SelectorKey.fileobj.__doc__ = 'File object registered.'
54
SelectorKey.fd.__doc__ = 'Underlying file descriptor.'
55
SelectorKey.events.__doc__ = 'Events that must be waited for on this file object.'
56
SelectorKey.data.__doc__ = ('''Optional opaque data associated to this file object.
57
For example, this could be used to store a per-client session ID.''')
60
class _SelectorMapping(Mapping):
61
"""Mapping of file objects to selector keys."""
63
def __init__(self, selector):
64
self._selector = selector
67
return len(self._selector._fd_to_key)
69
def get(self, fileobj, default=None):
70
fd = self._selector._fileobj_lookup(fileobj)
71
return self._selector._fd_to_key.get(fd, default)
73
def __getitem__(self, fileobj):
74
fd = self._selector._fileobj_lookup(fileobj)
75
key = self._selector._fd_to_key.get(fd)
77
raise KeyError("{!r} is not registered".format(fileobj))
81
return iter(self._selector._fd_to_key)
84
class BaseSelector(metaclass=ABCMeta):
85
"""Selector abstract base class.
87
A selector supports registering file objects to be monitored for specific
90
A file object is a file descriptor or any object with a `fileno()` method.
91
An arbitrary object can be attached to the file object, which can be used
92
for example to store context information, a callback, etc.
94
A selector can use various implementations (select(), poll(), epoll()...)
95
depending on the platform. The default `Selector` class uses the most
96
efficient implementation on the current platform.
100
def register(self, fileobj, events, data=None):
101
"""Register a file object.
104
fileobj -- file object or file descriptor
105
events -- events to monitor (bitwise mask of EVENT_READ|EVENT_WRITE)
106
data -- attached data
112
ValueError if events is invalid
113
KeyError if fileobj is already registered
114
OSError if fileobj is closed or otherwise is unacceptable to
115
the underlying system call (if a system call is made)
118
OSError may or may not be raised
120
raise NotImplementedError
123
def unregister(self, fileobj):
124
"""Unregister a file object.
127
fileobj -- file object or file descriptor
133
KeyError if fileobj is not registered
136
If fileobj is registered but has since been closed this does
137
*not* raise OSError (even if the wrapped syscall does)
139
raise NotImplementedError
141
def modify(self, fileobj, events, data=None):
142
"""Change a registered file object monitored events or attached data.
145
fileobj -- file object or file descriptor
146
events -- events to monitor (bitwise mask of EVENT_READ|EVENT_WRITE)
147
data -- attached data
153
Anything that unregister() or register() raises
155
self.unregister(fileobj)
156
return self.register(fileobj, events, data)
159
def select(self, timeout=None):
160
"""Perform the actual selection, until some monitored file objects are
161
ready or a timeout expires.
164
timeout -- if timeout > 0, this specifies the maximum wait time, in
166
if timeout <= 0, the select() call won't block, and will
167
report the currently ready file objects
168
if timeout is None, select() will block until a monitored
169
file object becomes ready
172
list of (key, events) for ready file objects
173
`events` is a bitwise mask of EVENT_READ|EVENT_WRITE
175
raise NotImplementedError
178
"""Close the selector.
180
This must be called to make sure that any underlying resource is freed.
184
def get_key(self, fileobj):
185
"""Return the key associated to a registered file object.
188
SelectorKey for this file object
190
mapping = self.get_map()
192
raise RuntimeError('Selector is closed')
194
return mapping[fileobj]
196
raise KeyError("{!r} is not registered".format(fileobj)) from None
200
"""Return a mapping of file objects to selector keys."""
201
raise NotImplementedError
206
def __exit__(self, *args):
210
class _BaseSelectorImpl(BaseSelector):
211
"""Base selector implementation."""
214
# this maps file descriptors to keys
216
# read-only mapping returned by get_map()
217
self._map = _SelectorMapping(self)
219
def _fileobj_lookup(self, fileobj):
220
"""Return a file descriptor from a file object.
222
This wraps _fileobj_to_fd() to do an exhaustive search in case
223
the object is invalid but we still have it in our map. This
224
is used by unregister() so we can unregister an object that
225
was previously registered even if it is closed. It is also
226
used by _SelectorMapping.
229
return _fileobj_to_fd(fileobj)
231
# Do an exhaustive search.
232
for key in self._fd_to_key.values():
233
if key.fileobj is fileobj:
235
# Raise ValueError after all.
238
def register(self, fileobj, events, data=None):
239
if (not events) or (events & ~(EVENT_READ | EVENT_WRITE)):
240
raise ValueError("Invalid events: {!r}".format(events))
242
key = SelectorKey(fileobj, self._fileobj_lookup(fileobj), events, data)
244
if key.fd in self._fd_to_key:
245
raise KeyError("{!r} (FD {}) is already registered"
246
.format(fileobj, key.fd))
248
self._fd_to_key[key.fd] = key
251
def unregister(self, fileobj):
253
key = self._fd_to_key.pop(self._fileobj_lookup(fileobj))
255
raise KeyError("{!r} is not registered".format(fileobj)) from None
258
def modify(self, fileobj, events, data=None):
260
key = self._fd_to_key[self._fileobj_lookup(fileobj)]
262
raise KeyError("{!r} is not registered".format(fileobj)) from None
263
if events != key.events:
264
self.unregister(fileobj)
265
key = self.register(fileobj, events, data)
266
elif data != key.data:
267
# Use a shortcut to update the data.
268
key = key._replace(data=data)
269
self._fd_to_key[key.fd] = key
273
self._fd_to_key.clear()
281
class SelectSelector(_BaseSelectorImpl):
282
"""Select-based selector."""
286
self._readers = set()
287
self._writers = set()
289
def register(self, fileobj, events, data=None):
290
key = super().register(fileobj, events, data)
291
if events & EVENT_READ:
292
self._readers.add(key.fd)
293
if events & EVENT_WRITE:
294
self._writers.add(key.fd)
297
def unregister(self, fileobj):
298
key = super().unregister(fileobj)
299
self._readers.discard(key.fd)
300
self._writers.discard(key.fd)
303
if sys.platform == 'win32':
304
def _select(self, r, w, _, timeout=None):
305
r, w, x = select.select(r, w, w, timeout)
308
_select = select.select
310
def select(self, timeout=None):
311
timeout = None if timeout is None else max(timeout, 0)
314
r, w, _ = self._select(self._readers, self._writers, [], timeout)
315
except InterruptedError:
320
fd_to_key_get = self._fd_to_key.get
322
key = fd_to_key_get(fd)
324
events = ((fd in r and EVENT_READ)
325
| (fd in w and EVENT_WRITE))
326
ready.append((key, events & key.events))
330
class _PollLikeSelector(_BaseSelectorImpl):
331
"""Base class shared between poll, epoll and devpoll selectors."""
338
self._selector = self._selector_cls()
340
def register(self, fileobj, events, data=None):
341
key = super().register(fileobj, events, data)
342
poller_events = ((events & EVENT_READ and self._EVENT_READ)
343
| (events & EVENT_WRITE and self._EVENT_WRITE) )
345
self._selector.register(key.fd, poller_events)
347
super().unregister(fileobj)
351
def unregister(self, fileobj):
352
key = super().unregister(fileobj)
354
self._selector.unregister(key.fd)
356
# This can happen if the FD was closed since it
361
def modify(self, fileobj, events, data=None):
363
key = self._fd_to_key[self._fileobj_lookup(fileobj)]
365
raise KeyError(f"{fileobj!r} is not registered") from None
368
if events != key.events:
369
selector_events = ((events & EVENT_READ and self._EVENT_READ)
370
| (events & EVENT_WRITE and self._EVENT_WRITE))
372
self._selector.modify(key.fd, selector_events)
374
super().unregister(fileobj)
381
key = key._replace(events=events, data=data)
382
self._fd_to_key[key.fd] = key
385
def select(self, timeout=None):
386
# This is shared between poll() and epoll().
387
# epoll() has a different signature and handling of timeout parameter.
393
# poll() has a resolution of 1 millisecond, round away from
394
# zero to wait *at least* timeout seconds.
395
timeout = math.ceil(timeout * 1e3)
398
fd_event_list = self._selector.poll(timeout)
399
except InterruptedError:
402
fd_to_key_get = self._fd_to_key.get
403
for fd, event in fd_event_list:
404
key = fd_to_key_get(fd)
406
events = ((event & ~self._EVENT_READ and EVENT_WRITE)
407
| (event & ~self._EVENT_WRITE and EVENT_READ))
408
ready.append((key, events & key.events))
412
if hasattr(select, 'poll'):
414
class PollSelector(_PollLikeSelector):
415
"""Poll-based selector."""
416
_selector_cls = select.poll
417
_EVENT_READ = select.POLLIN
418
_EVENT_WRITE = select.POLLOUT
421
if hasattr(select, 'epoll'):
423
_NOT_EPOLLIN = ~select.EPOLLIN
424
_NOT_EPOLLOUT = ~select.EPOLLOUT
426
class EpollSelector(_PollLikeSelector):
427
"""Epoll-based selector."""
428
_selector_cls = select.epoll
429
_EVENT_READ = select.EPOLLIN
430
_EVENT_WRITE = select.EPOLLOUT
433
return self._selector.fileno()
435
def select(self, timeout=None):
441
# epoll_wait() has a resolution of 1 millisecond, round away
442
# from zero to wait *at least* timeout seconds.
443
timeout = math.ceil(timeout * 1e3) * 1e-3
445
# epoll_wait() expects `maxevents` to be greater than zero;
446
# we want to make sure that `select()` can be called when no
448
max_ev = len(self._fd_to_key) or 1
452
fd_event_list = self._selector.poll(timeout, max_ev)
453
except InterruptedError:
456
fd_to_key = self._fd_to_key
457
for fd, event in fd_event_list:
458
key = fd_to_key.get(fd)
460
events = ((event & _NOT_EPOLLIN and EVENT_WRITE)
461
| (event & _NOT_EPOLLOUT and EVENT_READ))
462
ready.append((key, events & key.events))
466
self._selector.close()
470
if hasattr(select, 'devpoll'):
472
class DevpollSelector(_PollLikeSelector):
473
"""Solaris /dev/poll selector."""
474
_selector_cls = select.devpoll
475
_EVENT_READ = select.POLLIN
476
_EVENT_WRITE = select.POLLOUT
479
return self._selector.fileno()
482
self._selector.close()
486
if hasattr(select, 'kqueue'):
488
class KqueueSelector(_BaseSelectorImpl):
489
"""Kqueue-based selector."""
493
self._selector = select.kqueue()
497
return self._selector.fileno()
499
def register(self, fileobj, events, data=None):
500
key = super().register(fileobj, events, data)
502
if events & EVENT_READ:
503
kev = select.kevent(key.fd, select.KQ_FILTER_READ,
505
self._selector.control([kev], 0, 0)
506
self._max_events += 1
507
if events & EVENT_WRITE:
508
kev = select.kevent(key.fd, select.KQ_FILTER_WRITE,
510
self._selector.control([kev], 0, 0)
511
self._max_events += 1
513
super().unregister(fileobj)
517
def unregister(self, fileobj):
518
key = super().unregister(fileobj)
519
if key.events & EVENT_READ:
520
kev = select.kevent(key.fd, select.KQ_FILTER_READ,
522
self._max_events -= 1
524
self._selector.control([kev], 0, 0)
526
# This can happen if the FD was closed since it
529
if key.events & EVENT_WRITE:
530
kev = select.kevent(key.fd, select.KQ_FILTER_WRITE,
532
self._max_events -= 1
534
self._selector.control([kev], 0, 0)
540
def select(self, timeout=None):
541
timeout = None if timeout is None else max(timeout, 0)
542
# If max_ev is 0, kqueue will ignore the timeout. For consistent
543
# behavior with the other selector classes, we prevent that here
544
# (using max). See https://bugs.python.org/issue29255
545
max_ev = self._max_events or 1
548
105
105
kev_list = self._selector.control(None, max_ev, timeout)
549
except InterruptedError:
552
3
3
fd_to_key_get = self._fd_to_key.get
555
1
1
flag = kev.filter
556
2
2
key = fd_to_key_get(fd)
558
2
2
events = ((flag == select.KQ_FILTER_READ and EVENT_READ)
559
| (flag == select.KQ_FILTER_WRITE and EVENT_WRITE))
560
2
2
ready.append((key, events & key.events))
564
self._selector.close()
568
def _can_use(method):
569
"""Check if we can use the selector depending upon the
570
operating system. """
571
# Implementation based upon https://github.com/sethmlarson/selectors2/blob/master/selectors2.py
572
selector = getattr(select, method, None)
574
# select module does not implement method
576
# check if the OS and Kernel actually support the method. Call may fail with
577
# OSError: [Errno 38] Function not implemented
579
selector_obj = selector()
581
# check that poll actually works
584
# close epoll, kqueue, and devpoll fd
591
# Choose the best implementation, roughly:
592
# epoll|kqueue|devpoll > poll > select.
593
# select() also can't accept a FD > FD_SETSIZE (usually around 1024)
594
if _can_use('kqueue'):
595
DefaultSelector = KqueueSelector
596
elif _can_use('epoll'):
597
DefaultSelector = EpollSelector
598
elif _can_use('devpoll'):
599
DefaultSelector = DevpollSelector
600
elif _can_use('poll'):
601
DefaultSelector = PollSelector
603
DefaultSelector = SelectSelector