diff -r 276515d2ceed Lib/asyncio/base_events.py --- a/Lib/asyncio/base_events.py Fri Nov 28 22:42:06 2014 +0100 +++ b/Lib/asyncio/base_events.py Sat Nov 29 00:29:51 2014 +0100 @@ -267,7 +267,17 @@ class BaseEventLoop(events.AbstractEvent # is no need to log the "destroy pending task" message future._log_destroy_pending = False - future.add_done_callback(_raise_stop_error) + def done(fut): + if fut.done() and not fut.cancelled(): + 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() + + future.add_done_callback(done) try: self.run_forever() except: @@ -277,7 +287,7 @@ class BaseEventLoop(events.AbstractEvent # local task. future.exception() raise - future.remove_done_callback(_raise_stop_error) + future.remove_done_callback(done) if not future.done(): raise RuntimeError('Event loop stopped before Future completed.') diff -r 276515d2ceed Lib/test/test_asyncio/test_base_events.py --- a/Lib/test/test_asyncio/test_base_events.py Fri Nov 28 22:42:06 2014 +0100 +++ b/Lib/test/test_asyncio/test_base_events.py Sat Nov 29 00:29:51 2014 +0100 @@ -638,6 +638,31 @@ class BaseEventLoopTests(test_utils.Test 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