Signal handling: Catch Ctrl-C in Python
Python allows us to set up signal -handlers so when a particular signal arrives to our program we can have a behavior different from the default.
For example when you run a program on the terminal and press Ctrl-C the default behavior is to quit the program.
In this example we override that behavior and instead we ask the user if s/he really want to quit or if we can continue running.
This can be useful in all kinds of situations. For example a long-running process that somehow knows how much more time is needed can ask the user if s/he really wants to abandon the process at this point or return to processing.
This is how it works:
The first thing is to create a function that will be executed when the specific signal arrives.
Then we use the signal.signal call to connect a specific signal to a specific function.
From that point on this is going to be the behavior.
In this example we also use the "magic" of printing carriage return ("\r") only and not new line ("\n") so we keep counting in the same row. We also tell the print function to flush the output to the screen immediately and not wait for a newline. Finally we also employ readchar so the python code will be able to react to the pressing of a single button on the keyboard without the need to press ENTER. (That would move us to another line.)
examples/python/ctrl_c.py
import signal import time import readchar def handler(signum, frame): msg = "Ctrl-c was pressed. Do you really want to exit? y/n " print(msg, end="", flush=True) res = readchar.readchar() if res == 'y': print("") exit(1) else: print("", end="\r", flush=True) print(" " * len(msg), end="", flush=True) # clear the printed line print(" ", end="\r", flush=True) signal.signal(signal.SIGINT, handler) count = 0 while True: print(f"{count}", end="\r", flush=True) count += 1 time.sleep(0.1)
Skeleton
A simplified version of the script, that does not include the fancy "keep it in one line" thing:
examples/python/ctrl_c_skeleton.py
import signal import time def handler(signum, frame): res = input("Ctrl-c was pressed. Do you really want to exit? y/n ") if res == 'y': exit(1) signal.signal(signal.SIGINT, handler) count = 0 while True: print(count) count += 1 time.sleep(0.1)
This is how it looks like when the numbers are running out of the screen:
Published on 2021-05-06