]> granicus.if.org Git - python/commitdiff
Closes #22429, asyncio: Fix EventLoop.run_until_complete(), don't stop the
authorVictor Stinner <victor.stinner@gmail.com>
Fri, 5 Dec 2014 00:44:10 +0000 (01:44 +0100)
committerVictor Stinner <victor.stinner@gmail.com>
Fri, 5 Dec 2014 00:44:10 +0000 (01:44 +0100)
event loop if a BaseException is raised, because the event loop is already
stopped.

Lib/asyncio/base_events.py
Lib/test/test_asyncio/test_base_events.py

index 7c38b093e000ba8c27f7cd1e2350166065837490..0c7316ea97b1ac25e4d8ab1e976a7a6238f606da 100644 (file)
@@ -102,6 +102,16 @@ def _raise_stop_error(*args):
     raise _StopError
 
 
+def _run_until_complete_cb(fut):
+    exc = fut._exception
+    if (isinstance(exc, BaseException)
+    and not isinstance(exc, Exception)):
+        # Issue #22429: run_forever() already finished, no need to
+        # stop it.
+        return
+    _raise_stop_error()
+
+
 class Server(events.AbstractServer):
 
     def __init__(self, loop, sockets):
@@ -268,7 +278,7 @@ class BaseEventLoop(events.AbstractEventLoop):
             # is no need to log the "destroy pending task" message
             future._log_destroy_pending = False
 
-        future.add_done_callback(_raise_stop_error)
+        future.add_done_callback(_run_until_complete_cb)
         try:
             self.run_forever()
         except:
@@ -278,7 +288,7 @@ class BaseEventLoop(events.AbstractEventLoop):
                 # local task.
                 future.exception()
             raise
-        future.remove_done_callback(_raise_stop_error)
+        future.remove_done_callback(_run_until_complete_cb)
         if not future.done():
             raise RuntimeError('Event loop stopped before Future completed.')
 
index 0aa01174eaa18d5aa018b2cb022623bec3d61924..db9d732c1e08f2a6b9ee4300dd2c9695274b294b 100644 (file)
@@ -638,6 +638,31 @@ class BaseEventLoopTests(test_utils.TestCase):
 
         self.assertFalse(self.loop.call_exception_handler.called)
 
+    def test_run_until_complete_baseexception(self):
+        # Python issue #22429: run_until_complete() must not schedule a pending
+        # call to stop() if the future raised a BaseException
+        @asyncio.coroutine
+        def raise_keyboard_interrupt():
+            raise KeyboardInterrupt
+
+        self.loop._process_events = mock.Mock()
+
+        try:
+            self.loop.run_until_complete(raise_keyboard_interrupt())
+        except KeyboardInterrupt:
+            pass
+
+        def func():
+            self.loop.stop()
+            func.called = True
+        func.called = False
+        try:
+            self.loop.call_soon(func)
+            self.loop.run_forever()
+        except KeyboardInterrupt:
+            pass
+        self.assertTrue(func.called)
+
 
 class MyProto(asyncio.Protocol):
     done = None