From 4c85ec99f39bb6687f6fb0d23c6a7daedcde990e Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 5 Dec 2014 01:43:42 +0100 Subject: [PATCH 1/2] Issue #22922: Fix ProactorEventLoop.close() Call _stop_accept_futures() before sestting the _closed attribute, otherwise call_soon() raises an error. --- Lib/asyncio/proactor_events.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Lib/asyncio/proactor_events.py b/Lib/asyncio/proactor_events.py index a1e2fef6d17..4c527aa262a 100644 --- a/Lib/asyncio/proactor_events.py +++ b/Lib/asyncio/proactor_events.py @@ -387,11 +387,13 @@ class BaseProactorEventLoop(base_events.BaseEventLoop): sock, protocol, waiter, extra) def close(self): + if self._running: + raise RuntimeError("Cannot close a running event loop") if self.is_closed(): return - super().close() self._stop_accept_futures() self._close_self_pipe() + super().close() self._proactor.close() self._proactor = None self._selector = None From f3e2e09213ecac36c3c3634a06cb7badf3e4217e Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 5 Dec 2014 01:44:10 +0100 Subject: [PATCH 2/2] Closes #22429, asyncio: Fix EventLoop.run_until_complete(), don't stop the event loop if a BaseException is raised, because the event loop is already stopped. --- Lib/asyncio/base_events.py | 14 +++++++++++-- Lib/test/test_asyncio/test_base_events.py | 25 +++++++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py index 7c38b093e00..0c7316ea97b 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 0aa01174eaa..db9d732c1e0 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