summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/asyncio/base_events.py14
-rw-r--r--Lib/test/test_asyncio/test_base_events.py25
2 files changed, 37 insertions, 2 deletions
diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py
index 7c38b09..0c7316e 100644
--- a/Lib/asyncio/base_events.py
+++ b/Lib/asyncio/base_events.py
@@ -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.')
diff --git a/Lib/test/test_asyncio/test_base_events.py b/Lib/test/test_asyncio/test_base_events.py
index 0aa0117..db9d732 100644
--- a/Lib/test/test_asyncio/test_base_events.py
+++ b/Lib/test/test_asyncio/test_base_events.py
@@ -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