From 29762599412bf696f9a8cf3df7b626a97724ac7f Mon Sep 17 00:00:00 2001 From: Okiemute Date: Fri, 20 Mar 2026 04:25:38 -0700 Subject: [PATCH 1/5] gh-146194: Fix nested KeyboardInterrupt handling in asyncio - Modify _on_sigint to cancel main task on every SIGINT - Allow nested cancellations to propagate correctly through multiple levels - Add test_nested_keyboardinterrupt_handling to test_runners.py - Add NEWS entry Fixes issue where third Ctrl+C would crash with: 'Task was destroyed but it is pending!' --- Lib/asyncio/runners.py | 10 +++-- Lib/test/test_asyncio/test_runners.py | 45 +++++++++++++++++++ ...-03-20-04-20-47.gh-issue-146194.aXjDnd.rst | 11 +++++ 3 files changed, 62 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-03-20-04-20-47.gh-issue-146194.aXjDnd.rst diff --git a/Lib/asyncio/runners.py b/Lib/asyncio/runners.py index ba37e003a655c0..f864ed8c452788 100644 --- a/Lib/asyncio/runners.py +++ b/Lib/asyncio/runners.py @@ -158,12 +158,14 @@ def _lazy_init(self): def _on_sigint(self, signum, frame, main_task): self._interrupt_count += 1 - if self._interrupt_count == 1 and not main_task.done(): + + if not main_task.done(): main_task.cancel() - # wakeup loop if it is blocked by select() with long timeout self._loop.call_soon_threadsafe(lambda: None) - return - raise KeyboardInterrupt() + + + if self._interrupt_count > 10: + raise KeyboardInterrupt() def run(main, *, debug=None, loop_factory=None): diff --git a/Lib/test/test_asyncio/test_runners.py b/Lib/test/test_asyncio/test_runners.py index 8a4d7f5c796661..32058ba30fc630 100644 --- a/Lib/test/test_asyncio/test_runners.py +++ b/Lib/test/test_asyncio/test_runners.py @@ -522,6 +522,51 @@ async def coro(): self.assertEqual(0, result.repr_count) + def test_nested_keyboardinterrupt_handling(self): + """Test that multiple KeyboardInterrupts are handled correctly.""" + results = [] + + async def nested_coro(): + try: + while True: + await asyncio.sleep(0.1) + results.append('*') + except asyncio.CancelledError: + results.append('first_cancelled') + try: + while True: + await asyncio.sleep(0.1) + results.append('#') + except asyncio.CancelledError: + results.append('second_cancelled') + try: + while True: + await asyncio.sleep(0.1) + results.append('!') + except asyncio.CancelledError: + results.append('third_cancelled') + + + def run_with_cancels(): + async def main(): + task = asyncio.create_task(nested_coro()) + await asyncio.sleep(0.2) + task.cancel() + await asyncio.sleep(0.2) + task.cancel() + await asyncio.sleep(0.2) + task.cancel() + await asyncio.sleep(0.1) + + asyncio.run(main()) + + run_with_cancels() + + + self.assertIn('first_cancelled', results) + self.assertIn('second_cancelled', results) + self.assertIn('third_cancelled', results) + if __name__ == '__main__': unittest.main() diff --git a/Misc/NEWS.d/next/Library/2026-03-20-04-20-47.gh-issue-146194.aXjDnd.rst b/Misc/NEWS.d/next/Library/2026-03-20-04-20-47.gh-issue-146194.aXjDnd.rst new file mode 100644 index 00000000000000..da345ea49ff440 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-03-20-04-20-47.gh-issue-146194.aXjDnd.rst @@ -0,0 +1,11 @@ +.. gh-issue: 146194 + +.. section: Library + +Fix nested :exc:`KeyboardInterrupt` handling in :mod:`asyncio`. +Previously, +multiple Ctrl+C presses would cause a crash with ``Task was +destroyed but it +is pending!``. Now nested cancellations propagate correctly through +multiple +levels. From 598249886ff028774df29fe77cf42b168d0a14f4 Mon Sep 17 00:00:00 2001 From: Okiemute Date: Fri, 20 Mar 2026 04:30:49 -0700 Subject: [PATCH 2/5] gh-146194: Fix nested KeyboardInterrupt handling in asyncio - Modify _on_sigint to cancel main task on every SIGINT - Allow nested cancellations to propagate correctly through multiple levels - Add test_nested_keyboardinterrupt_handling to test_runners.py - Add NEWS entry Fixes issue where third Ctrl+C would crash with: 'Task was destroyed but it is pending!' --- .../2026-03-20-04-20-47.gh-issue-146194.aXjDnd.rst | 11 ----------- .../2026-03-20-08-00-00.gh-issue-146194.6ef5828d.rst | 8 ++++++++ 2 files changed, 8 insertions(+), 11 deletions(-) delete mode 100644 Misc/NEWS.d/next/Library/2026-03-20-04-20-47.gh-issue-146194.aXjDnd.rst create mode 100644 Misc/NEWS.d/next/Library/2026-03-20-08-00-00.gh-issue-146194.6ef5828d.rst diff --git a/Misc/NEWS.d/next/Library/2026-03-20-04-20-47.gh-issue-146194.aXjDnd.rst b/Misc/NEWS.d/next/Library/2026-03-20-04-20-47.gh-issue-146194.aXjDnd.rst deleted file mode 100644 index da345ea49ff440..00000000000000 --- a/Misc/NEWS.d/next/Library/2026-03-20-04-20-47.gh-issue-146194.aXjDnd.rst +++ /dev/null @@ -1,11 +0,0 @@ -.. gh-issue: 146194 - -.. section: Library - -Fix nested :exc:`KeyboardInterrupt` handling in :mod:`asyncio`. -Previously, -multiple Ctrl+C presses would cause a crash with ``Task was -destroyed but it -is pending!``. Now nested cancellations propagate correctly through -multiple -levels. diff --git a/Misc/NEWS.d/next/Library/2026-03-20-08-00-00.gh-issue-146194.6ef5828d.rst b/Misc/NEWS.d/next/Library/2026-03-20-08-00-00.gh-issue-146194.6ef5828d.rst new file mode 100644 index 00000000000000..2179a7b391baae --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-03-20-08-00-00.gh-issue-146194.6ef5828d.rst @@ -0,0 +1,8 @@ +.. gh-issue: 146194 + +.. section: Library + +Fix nested :exc:`KeyboardInterrupt` handling in :mod:`asyncio`. +Previously, multiple Ctrl+C presses would cause a crash with +``Task was destroyed but it is pending!``. Now nested cancellations +propagate correctly through multiple levels. From 2563ac4b5eb7187ea2e41b7da29d0a2207efbad7 Mon Sep 17 00:00:00 2001 From: Okiemute Date: Fri, 20 Mar 2026 04:52:40 -0700 Subject: [PATCH 3/5] Trigger docs rebuild --- ...ef5828d.rst => 2026-03-20-05-10-21.gh-issue-146194.aXjDnd.rst} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Misc/NEWS.d/next/Library/{2026-03-20-08-00-00.gh-issue-146194.6ef5828d.rst => 2026-03-20-05-10-21.gh-issue-146194.aXjDnd.rst} (100%) diff --git a/Misc/NEWS.d/next/Library/2026-03-20-08-00-00.gh-issue-146194.6ef5828d.rst b/Misc/NEWS.d/next/Library/2026-03-20-05-10-21.gh-issue-146194.aXjDnd.rst similarity index 100% rename from Misc/NEWS.d/next/Library/2026-03-20-08-00-00.gh-issue-146194.6ef5828d.rst rename to Misc/NEWS.d/next/Library/2026-03-20-05-10-21.gh-issue-146194.aXjDnd.rst From 099cf041256f4b1e4db459c8341c970844a4ec84 Mon Sep 17 00:00:00 2001 From: Okiemute Date: Fri, 20 Mar 2026 06:18:50 -0700 Subject: [PATCH 4/5] Fix NEWS file formatting - remove duplicate metadata --- .../next/Library/2026-03-20-05-10-21.gh-issue-146194.aXjDnd.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2026-03-20-05-10-21.gh-issue-146194.aXjDnd.rst b/Misc/NEWS.d/next/Library/2026-03-20-05-10-21.gh-issue-146194.aXjDnd.rst index 2179a7b391baae..b358da245f85e1 100644 --- a/Misc/NEWS.d/next/Library/2026-03-20-05-10-21.gh-issue-146194.aXjDnd.rst +++ b/Misc/NEWS.d/next/Library/2026-03-20-05-10-21.gh-issue-146194.aXjDnd.rst @@ -5,4 +5,4 @@ Fix nested :exc:`KeyboardInterrupt` handling in :mod:`asyncio`. Previously, multiple Ctrl+C presses would cause a crash with ``Task was destroyed but it is pending!``. Now nested cancellations -propagate correctly through multiple levels. +propagate correctly through multiple levels. \ No newline at end of file From 034c9af1734dcc49255cf018696ee36fc10ce61d Mon Sep 17 00:00:00 2001 From: Okiemute Date: Fri, 20 Mar 2026 06:29:49 -0700 Subject: [PATCH 5/5] Add NEWS entry for asyncio nested KeyboardInterrupt fix --- .../2026-03-20-05-10-21.gh-issue-146194.aXjDnd.rst | 8 -------- .../2026-03-20-06-26-14.gh-issue-146194.aXjDnd.rst | 4 ++++ 2 files changed, 4 insertions(+), 8 deletions(-) delete mode 100644 Misc/NEWS.d/next/Library/2026-03-20-05-10-21.gh-issue-146194.aXjDnd.rst create mode 100644 Misc/NEWS.d/next/Library/2026-03-20-06-26-14.gh-issue-146194.aXjDnd.rst diff --git a/Misc/NEWS.d/next/Library/2026-03-20-05-10-21.gh-issue-146194.aXjDnd.rst b/Misc/NEWS.d/next/Library/2026-03-20-05-10-21.gh-issue-146194.aXjDnd.rst deleted file mode 100644 index b358da245f85e1..00000000000000 --- a/Misc/NEWS.d/next/Library/2026-03-20-05-10-21.gh-issue-146194.aXjDnd.rst +++ /dev/null @@ -1,8 +0,0 @@ -.. gh-issue: 146194 - -.. section: Library - -Fix nested :exc:`KeyboardInterrupt` handling in :mod:`asyncio`. -Previously, multiple Ctrl+C presses would cause a crash with -``Task was destroyed but it is pending!``. Now nested cancellations -propagate correctly through multiple levels. \ No newline at end of file diff --git a/Misc/NEWS.d/next/Library/2026-03-20-06-26-14.gh-issue-146194.aXjDnd.rst b/Misc/NEWS.d/next/Library/2026-03-20-06-26-14.gh-issue-146194.aXjDnd.rst new file mode 100644 index 00000000000000..9951e0d8dcd63a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-03-20-06-26-14.gh-issue-146194.aXjDnd.rst @@ -0,0 +1,4 @@ +Fix nested :exc:`KeyboardInterrupt` handling in :mod:`asyncio`. Previously, +multiple Ctrl+C presses would cause a crash with ``Task was destroyed but it +is pending!``. Now nested cancellations propagate correctly through multiple +levels.