From ff1cca544a4034aa2304736d974f66d4e4d0c845 Mon Sep 17 00:00:00 2001 From: Jimmy Lai Date: Mon, 4 Sep 2017 10:33:11 -0700 Subject: [PATCH 1/2] move remove_done_callback to finally block to prevent RuntimeError('Event loop stopped before Future completed.') --- uvloop/loop.pyx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/uvloop/loop.pyx b/uvloop/loop.pyx index 256ef53f..2c88b253 100644 --- a/uvloop/loop.pyx +++ b/uvloop/loop.pyx @@ -1196,7 +1196,8 @@ cdef class Loop: # local task. future.exception() raise - future.remove_done_callback(done_cb) + finally: + future.remove_done_callback(done_cb) if not future.done(): raise RuntimeError('Event loop stopped before Future completed.') From 3cc3f5e06ee6e96d9782c743cf43c89cabf32498 Mon Sep 17 00:00:00 2001 From: Jimmy Lai Date: Fri, 8 Sep 2017 14:10:16 -0700 Subject: [PATCH 2/2] add test_run_until_complete_loop_orphan_future_close_loop --- tests/test_base.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/test_base.py b/tests/test_base.py index 96bb3eea..3e58f8ad 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -295,6 +295,20 @@ async def foo(): with self.assertRaisesRegex(ValueError, 'aaa'): self.loop.run_until_complete(foo()) + def test_run_until_complete_loop_orphan_future_close_loop(self): + async def foo(sec=0): + await asyncio.sleep(sec) + self.loop.close() + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + try: + with mock.patch('asyncio.base_events.BaseEventLoop.run_forever', side_effect=Exception): + loop.run_until_complete(foo()) + except: + pass + loop.run_until_complete(foo(0.1)) + loop.close() + def test_debug_slow_callbacks(self): logger = logging.getLogger('asyncio') self.loop.set_debug(True)