]> granicus.if.org Git - python/commitdiff
Issue #23485: Enhance and update selectors doc and test_selectors
authorVictor Stinner <victor.stinner@gmail.com>
Tue, 31 Mar 2015 10:08:09 +0000 (12:08 +0200)
committerVictor Stinner <victor.stinner@gmail.com>
Tue, 31 Mar 2015 10:08:09 +0000 (12:08 +0200)
Selector.select() is now retried with the recomputed timeout when interrupted
by a signal.

Write an unit test with a signal handler raising an exception, and a unit with
a signal handler which does not raise an exception (it does nothing).

Doc/library/selectors.rst
Lib/test/test_selectors.py

index 8bd9e1ce2e2dcce09f8b4cc385075ca18d7f87c9..f6ef24b6ae6de55645da1e69442550f891b1bb7b 100644 (file)
@@ -159,6 +159,12 @@ below:
           timeout has elapsed if the current process receives a signal: in this
           case, an empty list will be returned.
 
+      .. versionchanged:: 3.5
+         The selector is now retried with a recomputed timeout when interrupted
+         by a signal if the signal handler did not raise an exception (see
+         :pep:`475` for the rationale), instead of returning an empty list
+         of events before the timeout.
+
    .. method:: close()
 
       Close the selector.
index 9521481901ba18a5ab8461d63b8231d74f73b22a..454c17be99953c788e7d745ef73c94de47cc7f0a 100644 (file)
@@ -357,7 +357,35 @@ class BaseSelectorTestCase(unittest.TestCase):
 
     @unittest.skipUnless(hasattr(signal, "alarm"),
                          "signal.alarm() required for this test")
-    def test_select_interrupt(self):
+    def test_select_interrupt_exc(self):
+        s = self.SELECTOR()
+        self.addCleanup(s.close)
+
+        rd, wr = self.make_socketpair()
+
+        class InterruptSelect(Exception):
+            pass
+
+        def handler(*args):
+            raise InterruptSelect
+
+        orig_alrm_handler = signal.signal(signal.SIGALRM, handler)
+        self.addCleanup(signal.signal, signal.SIGALRM, orig_alrm_handler)
+        self.addCleanup(signal.alarm, 0)
+
+        signal.alarm(1)
+
+        s.register(rd, selectors.EVENT_READ)
+        t = time()
+        # select() is interrupted by a signal which raises an exception
+        with self.assertRaises(InterruptSelect):
+            s.select(30)
+        # select() was interrupted before the timeout of 30 seconds
+        self.assertLess(time() - t, 5.0)
+
+    @unittest.skipUnless(hasattr(signal, "alarm"),
+                         "signal.alarm() required for this test")
+    def test_select_interrupt_noraise(self):
         s = self.SELECTOR()
         self.addCleanup(s.close)
 
@@ -371,8 +399,11 @@ class BaseSelectorTestCase(unittest.TestCase):
 
         s.register(rd, selectors.EVENT_READ)
         t = time()
-        self.assertFalse(s.select(2))
-        self.assertLess(time() - t, 2.5)
+        # select() is interrupted by a signal, but the signal handler doesn't
+        # raise an exception, so select() should by retries with a recomputed
+        # timeout
+        self.assertFalse(s.select(1.5))
+        self.assertGreaterEqual(time() - t, 1.0)
 
 
 class ScalableSelectorMixIn: