Python: Catch Ctrl-C command. Prompt “really want to quit (y/n)”, resume execution if no

前端 未结 3 1122
轮回少年
轮回少年 2020-11-30 03:36

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 executio         


        
相关标签:
3条回答
  • 2020-11-30 04:14

    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.

    0 讨论(0)
  • 2020-11-30 04:18

    from https://gist.github.com/rtfpessoa/e3b1fe0bbfcd8ac853bf

    #!/usr/bin/env python
    
    import signal
    import sys
    
    def signal_handler(signal, frame):
      # your code here
      sys.exit(0)
    
    signal.signal(signal.SIGINT, signal_handler)
    

    Bye!

    0 讨论(0)
  • 2020-11-30 04:34

    when procedure end then do something

    suppose you just want to the procedure will do something after the task end

    import time
    
    class 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:
                pass
    
    with 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 ...

    @staticmethod
    def 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 time
    from enum import Enum
    
    class Action(Enum):
        EXIT = 0
        CONTINUE = 1
        RESET = 2
    
    class 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 True
    
    with TestTask('Hello World') as task:
        task.do_something()
    

    console

    input action number:2
    Task Start!:Hello World
    1%
    2%
    ...
    60%
    KeyboardInterrupt. input action:
            EXIT:     0
            CONTINUE: 1
            RESET:    2
    :1
    61%
    status_1
    ...
    99%
    100%
    Task End!
    
    
    0 讨论(0)
提交回复
热议问题