Skip to content

Commit 94e6648

Browse files
CPython Developersyouknowone
authored andcommitted
Update selectors from CPython v3.12.3
1 parent 75a985e commit 94e6648

File tree

2 files changed

+45
-7
lines changed

2 files changed

+45
-7
lines changed

Lib/selectors.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,11 @@ def _fileobj_to_fd(fileobj):
5050
Object used to associate a file object to its backing
5151
file descriptor, selected event mask, and attached data.
5252
"""
53-
if sys.version_info >= (3, 5):
54-
SelectorKey.fileobj.__doc__ = 'File object registered.'
55-
SelectorKey.fd.__doc__ = 'Underlying file descriptor.'
56-
SelectorKey.events.__doc__ = 'Events that must be waited for on this file object.'
57-
SelectorKey.data.__doc__ = ('''Optional opaque data associated to this file object.
58-
For example, this could be used to store a per-client session ID.''')
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.''')
5958

6059

6160
class _SelectorMapping(Mapping):
@@ -510,6 +509,7 @@ class KqueueSelector(_BaseSelectorImpl):
510509
def __init__(self):
511510
super().__init__()
512511
self._selector = select.kqueue()
512+
self._max_events = 0
513513

514514
def fileno(self):
515515
return self._selector.fileno()
@@ -521,10 +521,12 @@ def register(self, fileobj, events, data=None):
521521
kev = select.kevent(key.fd, select.KQ_FILTER_READ,
522522
select.KQ_EV_ADD)
523523
self._selector.control([kev], 0, 0)
524+
self._max_events += 1
524525
if events & EVENT_WRITE:
525526
kev = select.kevent(key.fd, select.KQ_FILTER_WRITE,
526527
select.KQ_EV_ADD)
527528
self._selector.control([kev], 0, 0)
529+
self._max_events += 1
528530
except:
529531
super().unregister(fileobj)
530532
raise
@@ -535,6 +537,7 @@ def unregister(self, fileobj):
535537
if key.events & EVENT_READ:
536538
kev = select.kevent(key.fd, select.KQ_FILTER_READ,
537539
select.KQ_EV_DELETE)
540+
self._max_events -= 1
538541
try:
539542
self._selector.control([kev], 0, 0)
540543
except OSError:
@@ -544,6 +547,7 @@ def unregister(self, fileobj):
544547
if key.events & EVENT_WRITE:
545548
kev = select.kevent(key.fd, select.KQ_FILTER_WRITE,
546549
select.KQ_EV_DELETE)
550+
self._max_events -= 1
547551
try:
548552
self._selector.control([kev], 0, 0)
549553
except OSError:
@@ -556,7 +560,7 @@ def select(self, timeout=None):
556560
# If max_ev is 0, kqueue will ignore the timeout. For consistent
557561
# behavior with the other selector classes, we prevent that here
558562
# (using max). See https://bugs.python.org/issue29255
559-
max_ev = max(len(self._fd_to_key), 1)
563+
max_ev = self._max_events or 1
560564
ready = []
561565
try:
562566
kev_list = self._selector.control(None, max_ev, timeout)

Lib/test/test_selectors.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@
1919
resource = None
2020

2121

22+
if support.is_emscripten or support.is_wasi:
23+
raise unittest.SkipTest("Cannot create socketpair on Emscripten/WASI.")
24+
25+
2226
if hasattr(socket, 'socketpair'):
2327
socketpair = socket.socketpair
2428
else:
@@ -276,6 +280,35 @@ def test_select(self):
276280

277281
self.assertEqual([(wr_key, selectors.EVENT_WRITE)], result)
278282

283+
def test_select_read_write(self):
284+
# gh-110038: when a file descriptor is registered for both read and
285+
# write, the two events must be seen on a single call to select().
286+
s = self.SELECTOR()
287+
self.addCleanup(s.close)
288+
289+
sock1, sock2 = self.make_socketpair()
290+
sock2.send(b"foo")
291+
my_key = s.register(sock1, selectors.EVENT_READ | selectors.EVENT_WRITE)
292+
293+
seen_read, seen_write = False, False
294+
result = s.select()
295+
# We get the read and write either in the same result entry or in two
296+
# distinct entries with the same key.
297+
self.assertLessEqual(len(result), 2)
298+
for key, events in result:
299+
self.assertTrue(isinstance(key, selectors.SelectorKey))
300+
self.assertEqual(key, my_key)
301+
self.assertFalse(events & ~(selectors.EVENT_READ |
302+
selectors.EVENT_WRITE))
303+
if events & selectors.EVENT_READ:
304+
self.assertFalse(seen_read)
305+
seen_read = True
306+
if events & selectors.EVENT_WRITE:
307+
self.assertFalse(seen_write)
308+
seen_write = True
309+
self.assertTrue(seen_read)
310+
self.assertTrue(seen_write)
311+
279312
def test_context_manager(self):
280313
s = self.SELECTOR()
281314
self.addCleanup(s.close)
@@ -446,6 +479,7 @@ class ScalableSelectorMixIn:
446479
# see issue #18963 for why it's skipped on older OS X versions
447480
@support.requires_mac_ver(10, 5)
448481
@unittest.skipUnless(resource, "Test needs resource module")
482+
@support.requires_resource('cpu')
449483
def test_above_fd_setsize(self):
450484
# A scalable implementation should have no problem with more than
451485
# FD_SETSIZE file descriptors. Since we don't know the value, we just

0 commit comments

Comments
 (0)