Skip to content

gh-79698: selector.EpollSelector: add new parameter to support extra events #11193

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions Doc/library/selectors.rst
Original file line number Diff line number Diff line change
Expand Up @@ -102,14 +102,15 @@ constants below:
:class:`BaseSelector` and its concrete implementations support the
:term:`context manager` protocol.

.. abstractmethod:: register(fileobj, events, data=None)
.. abstractmethod:: register(fileobj, events, data=None, *, extra_events=0)

Register a file object for selection, monitoring it for I/O events.

*fileobj* is the file object to monitor. It may either be an integer
file descriptor or an object with a ``fileno()`` method.
*events* is a bitwise mask of events to monitor.
*data* is an opaque object.
*extra_events* is the extra events people want to use. All of the events are defined in :mod:`select` module.

This returns a new :class:`SelectorKey` instance, or raises a
:exc:`ValueError` in case of invalid event mask or file descriptor, or
Expand All @@ -127,12 +128,12 @@ constants below:
:exc:`ValueError` if *fileobj* is invalid (e.g. it has no ``fileno()``
method or its ``fileno()`` method has an invalid return value).

.. method:: modify(fileobj, events, data=None)
.. method:: modify(fileobj, events, data=None, *, extra_events=0)

Change a registered file object's monitored events or attached data.

This is equivalent to :meth:`BaseSelector.unregister(fileobj)` followed
by :meth:`BaseSelector.register(fileobj, events, data)`, except that it
by :meth:`BaseSelector.register(fileobj, events, data, *, extra_events=0)`, except that it
can be implemented more efficiently.

This returns a new :class:`SelectorKey` instance, or raises a
Expand Down
24 changes: 14 additions & 10 deletions Lib/selectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,14 @@ class BaseSelector(metaclass=ABCMeta):
"""

@abstractmethod
def register(self, fileobj, events, data=None):
def register(self, fileobj, events, data=None, *, extra_events=0):
"""Register a file object.

Parameters:
fileobj -- file object or file descriptor
events -- events to monitor (bitwise mask of EVENT_READ|EVENT_WRITE)
data -- attached data
extra_events -- extra events which is supported in select module

Returns:
SelectorKey instance
Expand Down Expand Up @@ -134,7 +135,7 @@ def unregister(self, fileobj):
"""
raise NotImplementedError

def modify(self, fileobj, events, data=None):
def modify(self, fileobj, events, data=None, *, extra_events=0):
"""Change a registered file object monitored events or attached data.

Parameters:
Expand All @@ -149,7 +150,7 @@ def modify(self, fileobj, events, data=None):
Anything that unregister() or register() raises
"""
self.unregister(fileobj)
return self.register(fileobj, events, data)
return self.register(fileobj, events, data, extra_events=extra_events)

@abstractmethod
def select(self, timeout=None):
Expand Down Expand Up @@ -231,7 +232,7 @@ def _fileobj_lookup(self, fileobj):
# Raise ValueError after all.
raise

def register(self, fileobj, events, data=None):
def register(self, fileobj, events, data=None, *, extra_events=0):
if (not events) or (events & ~(EVENT_READ | EVENT_WRITE)):
raise ValueError("Invalid events: {!r}".format(events))

Expand All @@ -251,14 +252,14 @@ def unregister(self, fileobj):
raise KeyError("{!r} is not registered".format(fileobj)) from None
return key

def modify(self, fileobj, events, data=None):
def modify(self, fileobj, events, data=None, *, extra_events=0):
try:
key = self._fd_to_key[self._fileobj_lookup(fileobj)]
except KeyError:
raise KeyError("{!r} is not registered".format(fileobj)) from None
if events != key.events:
self.unregister(fileobj)
key = self.register(fileobj, events, data)
key = self.register(fileobj, events, data, extra_events=extra_events)
elif data != key.data:
# Use a shortcut to update the data.
key = key._replace(data=data)
Expand Down Expand Up @@ -295,7 +296,7 @@ def __init__(self):
self._readers = set()
self._writers = set()

def register(self, fileobj, events, data=None):
def register(self, fileobj, events, data=None, *, extra_events=0):
key = super().register(fileobj, events, data)
if events & EVENT_READ:
self._readers.add(key.fd)
Expand Down Expand Up @@ -348,13 +349,15 @@ def __init__(self):
super().__init__()
self._selector = self._selector_cls()

def register(self, fileobj, events, data=None):
def register(self, fileobj, events, data=None, *, extra_events=0):
key = super().register(fileobj, events, data)
poller_events = 0
if events & EVENT_READ:
poller_events |= self._EVENT_READ
if events & EVENT_WRITE:
poller_events |= self._EVENT_WRITE
# allow user use events defiend in select
poller_events |= extra_events
try:
self._selector.register(key.fd, poller_events)
except:
Expand All @@ -372,7 +375,7 @@ def unregister(self, fileobj):
pass
return key

def modify(self, fileobj, events, data=None):
def modify(self, fileobj, events, data=None, *, extra_events=0):
try:
key = self._fd_to_key[self._fileobj_lookup(fileobj)]
except KeyError:
Expand All @@ -385,6 +388,7 @@ def modify(self, fileobj, events, data=None):
selector_events |= self._EVENT_READ
if events & EVENT_WRITE:
selector_events |= self._EVENT_WRITE
selector_events |= extra_events
try:
self._selector.modify(key.fd, selector_events)
except:
Expand Down Expand Up @@ -513,7 +517,7 @@ def __init__(self):
def fileno(self):
return self._selector.fileno()

def register(self, fileobj, events, data=None):
def register(self, fileobj, events, data=None, *, extra_events=0):
key = super().register(fileobj, events, data)
try:
if events & EVENT_READ:
Expand Down
10 changes: 10 additions & 0 deletions Lib/test/test_selectors.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import errno
import os
import random
import select
import selectors
import signal
import socket
Expand Down Expand Up @@ -524,6 +525,15 @@ def test_register_file(self):
with self.assertRaises(KeyError):
s.get_key(f)

def test_register(self):
super().test_register()
s = self.SELECTOR()
if hasattr("select", 'EPOLLEXCLUSIVE'):
key = s.register(wr, selectors.EVENT_WRITE, 'data', extra_events=select.EPOLLEXCLUSIVE)
self.assertEqual(key.fileobj, wr)
self.assertEqual(key.fd, wr.fileno())
self.assertEqual(key.events, select.EPOLLEXCLUSIVE)
self.assertEqual(key.data, "data")

@unittest.skipUnless(hasattr(selectors, 'KqueueSelector'),
"Test needs selectors.KqueueSelector)")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
selector.EpollSelector: add new parameter to support extra_events