Python: Catch Ctrl-C command. Prompt "really want to quit (y/n)", resume execution if no
Question: I have a program that may have a lengthy execution. In the main module I have
the following:

    
    
    import signal
    def run_program()
       ...time consuming execution...
    
    def Exit_gracefully(signal, frame):
        ... log exiting information ...
        ... close any open files ...
        sys.exit(0)
    
    if __name__ == '__main__':
        signal.signal(signal.SIGINT, Exit_gracefully)
        run_program()
    

This works fine, but I'd like the possibility to pause execution upon catching
SIGINT, prompting the user if they would really like to quit, and resuming
where I left off in run_program() if they decide they don't want to quit.

The only way I can think of doing this is running the program in a separate
thread, keeping the main thread waiting on it and ready to catch SIGINT. If
the user wants to quit the main thread can do cleanup and kill the child
thread.

Is there a simpler way?


Answer: The python signal handlers do not seem to be real signal handlers; that is
they happen after the fact, in the normal flow and after the C handler has
already returned. Thus you'd try to put your quit logic within the signal
handler. As the signal handler runs in the main thread, it will block
execution there too.

Something like this seems to work nicely.

    
    
    import signal
    import time
    import sys
    
    def run_program():
        while True:
            time.sleep(1)
            print("a")
    
    def exit_gracefully(signum, frame):
        # restore the original signal handler as otherwise evil things will happen
        # in raw_input when CTRL+C is pressed, and our signal handler is not re-entrant
        signal.signal(signal.SIGINT, original_sigint)
    
        try:
            if raw_input("\nReally quit? (y/n)> ").lower().startswith('y'):
                sys.exit(1)
    
        except KeyboardInterrupt:
            print("Ok ok, quitting")
            sys.exit(1)
    
        # restore the exit gracefully handler here    
        signal.signal(signal.SIGINT, exit_gracefully)
    
    if __name__ == '__main__':
        # store the original SIGINT handler
        original_sigint = signal.getsignal(signal.SIGINT)
        signal.signal(signal.SIGINT, exit_gracefully)
        run_program()
    

The code restores the original signal handler for the duration of `raw_input`;
`raw_input` itself is not re-entrable, and re-entering it will lead to
`RuntimeError: can't re-enter readline` being raised from `time.sleep` which
is something we don't want as it is harder to catch than `KeyboardInterrupt`.
Rather, we let 2 consecutive Ctrl-C's to raise `KeyboardInterrupt`.

