Python: Catch Ctrl-C command. Prompt "really want to quit (y/n)", resume execution if no
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 signalimport timeimport sysdef 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
.
from https://gist.github.com/rtfpessoa/e3b1fe0bbfcd8ac853bf
#!/usr/bin/env pythonimport signalimport sysdef signal_handler(signal, frame): # your code here sys.exit(0)signal.signal(signal.SIGINT, signal_handler)
Bye!
when procedure end then do something
suppose you just want to the procedure will do something after the task end
import timeclass TestTask: def __init__(self, msg: str): self.msg = msg def __enter__(self): print(f'Task Start!:{self.msg}') return self def __exit__(self, exc_type, exc_val, exc_tb): print('Task End!') @staticmethod def do_something(): try: time.sleep(5) except: passwith TestTask('Hello World') as task: task.do_something()
when the process leaves with
that will run __exit__
even with KeyboardInterrupt happen that are same.
if you don't like to see the error, add try ... except ...
@staticmethoddef do_something(): try: time.sleep(5) except: pass
pause, continue, reset, and etc.
I don't have a perfect solution, but it may be useful to you.
It's means divided your process to many subprocesses and save it that finished.it will not be executed again since you find it already done.
import timefrom enum import Enumclass Action(Enum): EXIT = 0 CONTINUE = 1 RESET = 2class TestTask: def __init__(self, msg: str): self.msg = msg def __enter__(self): print(f'Task Start!:{self.msg}') return self def __exit__(self, exc_type, exc_val, exc_tb): print('Task End!') def do_something(self): tuple_job = (self._foo, self._bar) # implement by yourself list_job_state = [0] * len(tuple_job) dict_keep = {} # If there is a need to communicate between jobs, and you don’t want to use class members, you can use this method. while 1: try: for idx, cur_process in enumerate(tuple_job): if not list_job_state[idx]: cur_process(dict_keep) list_job_state[idx] = True if all(list_job_state): print('100%') break except KeyboardInterrupt: print('KeyboardInterrupt. input action:') msg = '\n\t'.join([f"{action + ':':<10}{str(act_number)}" for act_number, action in enumerate([name for name in vars(Action) if not name.startswith('_')]) ]) case = Action(int(input(f'\t{msg}\n:'))) if case == Action.EXIT: break if case == Action.RESET: list_job_state = [0] * len(tuple_job) @staticmethod def _foo(keep_dict: dict) -> bool: # implement by yourself time.sleep(2) print('1%') print('2%') print('...') print('60%') keep_dict['status_1'] = 'status_1' return True @staticmethod def _bar(keep_dict: dict) -> bool: # implement by yourself time.sleep(2) print('61%') print(keep_dict.get('status_1')) print('...') print('99%') return Truewith TestTask('Hello World') as task: task.do_something()
console
input action number:2Task Start!:Hello World1%2%...60%KeyboardInterrupt. input action: EXIT: 0 CONTINUE: 1 RESET: 2:161%status_1...99%100%Task End!