Skip to content

Asyncio does not handle nested KeyboardInterrupt correctly #146194

@user202729

Description

@user202729

Bug report

Bug description:

Consider this code

import sys
import logging
import asyncio
import signal
from time import*

logger = logging.getLogger(__name__)

logging.basicConfig(level=logging.INFO)

async def f():
    if 1:
        main_task = asyncio.current_task()
        def on_sigint() -> None:
            logger.error("Received SIGINT")
            main_task.cancel(KeyboardInterrupt)
        asyncio.get_running_loop().add_signal_handler(signal.SIGINT, on_sigint)
    try:
        while True:
            await asyncio.sleep(0.1)
            print("*")
    except asyncio.CancelledError as e:
        print(e)
        try:
            while True:
                await asyncio.sleep(0.1)
                print("#")
        except asyncio.CancelledError:
            try:
                while True:
                    await asyncio.sleep(0.1)
                    print("!")
            except asyncio.CancelledError:
                while True:
                    await asyncio.sleep(0.1)
                    print("@")

asyncio.run(f())

Intention: each time I press Ctrl-C, it repeatedly print *, #, !, @ respectively.

As is, it does do that correctly.

However, if I don't manually add the SIGINT signal handler, it will print *, {first Ctrl-C} #, {second Ctrl-C} !, {third Ctrl-C} then forcefully stop.

I don't think this behavior is desirable. Or at least it probably shouldn't log a scary error Task was destroyed but it is pending!?

CPython versions tested on:

3.13

Operating systems tested on:

Linux

Linked PRs

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    Status

    Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions