From 8ed388d311f6a7d9e205247552bc6c0ffda966f4 Mon Sep 17 00:00:00 2001 From: jack1142 <6032823+jack1142@users.noreply.github.com> Date: Fri, 24 Apr 2020 18:33:32 +0200 Subject: [PATCH 1/3] Don't stop the loop in run_until_complete on SystemExit & KeyboardInterrupt --- uvloop/loop.pyx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/uvloop/loop.pyx b/uvloop/loop.pyx index a7b24d60..9ebf3a63 100644 --- a/uvloop/loop.pyx +++ b/uvloop/loop.pyx @@ -1431,7 +1431,14 @@ cdef class Loop: # is no need to log the "destroy pending task" message future._log_destroy_pending = False - done_cb = lambda fut: self.stop() + def done_cb(fut): + if not fut.cancelled(): + exc = fut.exception() + if isinstance(exc, (SystemExit, KeyboardInterrupt)): + # Issue #336: run_forever() already finished, + # no need to stop it. + return + self.stop() future.add_done_callback(done_cb) try: From 27cea5dda1a8e4fe2745f422805420621c7dafa8 Mon Sep 17 00:00:00 2001 From: jack1142 <6032823+jack1142@users.noreply.github.com> Date: Wed, 13 May 2020 21:27:23 +0200 Subject: [PATCH 2/3] Add unit test (based on CPython's equivalent) Co-authored-by: Victor Stinner --- tests/test_base.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/test_base.py b/tests/test_base.py index e46178af..9b193ead 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -361,6 +361,31 @@ def throw(): # done-callback for the previous future. self.loop.run_until_complete(foo(0.2)) + def test_run_until_complete_keyboard_interrupt(self): + # Issue #336: run_until_complete() must not schedule a pending + # call to stop() if the future raised a KeyboardInterrupt + async 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) + def test_debug_slow_callbacks(self): logger = logging.getLogger('asyncio') self.loop.set_debug(True) From f65b9746bd9eacc9dd8bbe63a47972acfafffe78 Mon Sep 17 00:00:00 2001 From: Fantix King Date: Sun, 7 Feb 2021 15:28:55 -0500 Subject: [PATCH 3/3] Fix the test to resume its coverage. --- tests/test_base.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/tests/test_base.py b/tests/test_base.py index 9b193ead..6d87f54d 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -369,21 +369,16 @@ async def raise_keyboard_interrupt(): self.loop._process_events = mock.Mock() - try: + with self.assertRaises(KeyboardInterrupt): 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.loop.call_later(0.01, func) + self.loop.run_forever() self.assertTrue(func.called) def test_debug_slow_callbacks(self):