web-dev-qa-db-ja.com

Python:Ctrl-Cコマンドをキャッチします。 「本当に終了したい(y / n)」というプロンプト、いいえの場合は実行を再開

実行に時間がかかるプログラムがあります。メインモジュールには次のものがあります。

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()

これはうまく機能しますが、SIGINTをキャッチすると実行を一時停止し、本当に終了したい場合はユーザーにプロンプ​​トを表示し、run_program()で終了したくないと判断した場合は中断した場所に再開する可能性が欲しいです。

これを行うことを考えることができる唯一の方法は、プログラムを別のスレッドで実行し、メインスレッドを待機させ、SIGINTをキャッチする準備を整えることです。ユーザーがメインスレッドを終了する場合は、クリーンアップを実行して子スレッドを強制終了できます。

もっと簡単な方法はありますか?

32
Colin M

pythonシグナルハンドラーは本当のシグナルハンドラーではないようです;それは、事実の後、通常のフローで、Cハンドラーがすでに戻った後に発生します。シグナルハンドラー内の終了ロジック:シグナルハンドラーはメインスレッドで実行されるため、メインスレッドでも実行がブロックされます。

このような何かがうまくいくようです。

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()

このコードは、raw_inputの間、元のシグナルハンドラーを復元します。 raw_input自体は再入可能ではなく、再入力するとRuntimeError: can't re-enter readlineからtime.sleepが発生しますが、これはKeyboardInterrupt。むしろ、2つの連続したCtrl-CでKeyboardInterruptを発生させます。

54
Antti Haapala

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)

さようなら!

0
Marc