私はいくつかのプロセスとデータベース接続を開始するpythonスクリプトに取り組んでいます。時々、Ctrl+ C信号でスクリプトを強制終了したいので、クリーンアップを行いたいと思います。
Perlではこれを行います。
$SIG{'INT'} = 'exit_gracefully';
sub exit_gracefully {
print "Caught ^C \n";
exit (0);
}
これをPythonでどうやって行うのですか?
私はいくつかのプロセスとデータベース接続を開始するpythonスクリプトに取り組んでいます。時々、Ctrl+ C信号でスクリプトを強制終了したいので、クリーンアップを行いたいと思います。
Perlではこれを行います。
$SIG{'INT'} = 'exit_gracefully';
sub exit_gracefully {
print "Caught ^C \n";
exit (0);
}
これをPythonでどうやって行うのですか?
回答:
次のsignal.signal
ようにハンドラを登録します。
#!/usr/bin/env python
import signal
import sys
def signal_handler(sig, frame):
print('You pressed Ctrl+C!')
sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)
print('Press Ctrl+C')
signal.pause()
ここからコードを改造。
詳細ドキュメントはsignal
見つけることができるここに。
./program.py > output.log
。押すとCtrlキーを押しながらCを、あなたのプログラムは、それがすべてのデータファイルがフラッシュされ、それらは、既知の良好な状態で放置されている確認するために、クリーンマークしたことをログに記録させることによって適切に終了します。ただし、Ctrl-CはSIGINTをパイプライン内のすべてのプロセスに送信するため、program.pyが最終ログの印刷を完了する前に、シェルがSTDOUT(現在は「output.log」)を閉じる場合があります。Pythonは「ファイルオブジェクトデストラクタのクローズに失敗しました:sys.excepthookのエラー:」と文句を言います。
他と同様に、例外(KeyboardInterrupt)として扱うことができます。新しいファイルを作成し、次の内容でシェルから実行して、意味を確認します。
import time, sys
x = 1
while True:
try:
print x
time.sleep(.3)
x += 1
except KeyboardInterrupt:
print "Bye"
sys.exit()
signal.signal(signal.SIGINT, signal.default_int_handler)
。詳細はこちら。
そしてコンテキストマネージャとして:
import signal
class GracefulInterruptHandler(object):
def __init__(self, sig=signal.SIGINT):
self.sig = sig
def __enter__(self):
self.interrupted = False
self.released = False
self.original_handler = signal.getsignal(self.sig)
def handler(signum, frame):
self.release()
self.interrupted = True
signal.signal(self.sig, handler)
return self
def __exit__(self, type, value, tb):
self.release()
def release(self):
if self.released:
return False
signal.signal(self.sig, self.original_handler)
self.released = True
return True
使用するには:
with GracefulInterruptHandler() as h:
for i in xrange(1000):
print "..."
time.sleep(1)
if h.interrupted:
print "interrupted!"
time.sleep(2)
break
ネストされたハンドラー:
with GracefulInterruptHandler() as h1:
while True:
print "(1)..."
time.sleep(1)
with GracefulInterruptHandler() as h2:
while True:
print "\t(2)..."
time.sleep(1)
if h2.interrupted:
print "\t(2) interrupted!"
time.sleep(2)
break
if h1.interrupted:
print "(1) interrupted!"
time.sleep(2)
break
ここから:https : //gist.github.com/2907502
StopIteration
ctrl-Cが押されたときに、最も内側のループを解除するためにa をスローすることもできますよね?
言及main
主な機能として、およびexit_gracefully
としてのCTRL+のcハンドラ
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
pass
finally:
exit_gracefully()
私は@udiのコードを複数の信号をサポートするように変更しました(空想はありません):
class GracefulInterruptHandler(object):
def __init__(self, signals=(signal.SIGINT, signal.SIGTERM)):
self.signals = signals
self.original_handlers = {}
def __enter__(self):
self.interrupted = False
self.released = False
for sig in self.signals:
self.original_handlers[sig] = signal.getsignal(sig)
signal.signal(sig, self.handler)
return self
def handler(self, signum, frame):
self.release()
self.interrupted = True
def __exit__(self, type, value, tb):
self.release()
def release(self):
if self.released:
return False
for sig in self.signals:
signal.signal(sig, self.original_handlers[sig])
self.released = True
return True
このコードのサポートキーボード割り込みコール(SIGINT
)とSIGTERM
(kill <process>
)
Matt Jの答えとは対照的に、私は単純なオブジェクトを使用しています。これにより、このハンドラーを解析して、セキュリティで停止する必要のあるすべてのスレッドを処理できる可能性があります。
class SIGINT_handler():
def __init__(self):
self.SIGINT = False
def signal_handler(self, signal, frame):
print('You pressed Ctrl+C!')
self.SIGINT = True
handler = SIGINT_handler()
signal.signal(signal.SIGINT, handler.signal_handler)
他の場所
while True:
# task
if handler.SIGINT:
break
time.sleep()
変数に対してビジーループを実行する代わりに、イベントを使用する必要があります。
Pythonの組み込みシグナルモジュールの関数を使用して、Pythonでシグナルハンドラーを設定できます。具体的には、signal.signal(signalnum, handler)
関数はhandler
signal の関数を登録するために使用されsignalnum
ます。
既存の回答に感謝しますが、追加されました signal.getsignal()
import signal
# store default handler of signal.SIGINT
default_handler = signal.getsignal(signal.SIGINT)
catch_count = 0
def handler(signum, frame):
global default_handler, catch_count
catch_count += 1
print ('wait:', catch_count)
if catch_count > 3:
# recover handler for signal.SIGINT
signal.signal(signal.SIGINT, default_handler)
print('expecting KeyboardInterrupt')
signal.signal(signal.SIGINT, handler)
print('Press Ctrl+c here')
while True:
pass
クリーンアッププロセスが確実に終了するようにしたい場合は、SIG_IGNを使用してMatt Jの回答に追加します。これにより、無視され、クリーンアップが中断されなくなります。SIGINT
import signal
import sys
def signal_handler(signum, frame):
signal.signal(signum, signal.SIG_IGN) # ignore additional signals
cleanup() # give your process a chance to clean up
sys.exit(0)
signal.signal(signal.SIGINT, signal_handler) # register the signal with the signal handler first
do_stuff()
個人的には、ブロックしている標準ソケット(IPC)モードを使用していたため、try / except KeyboardInterruptを使用できませんでした。したがって、SIGINTはキューに入れられましたが、ソケットでデータを受信した後にのみ来ました。
シグナルハンドラの設定も同じように動作します。
一方、これは実際の端末でのみ機能します。他の開始環境はCtrl+を受け入れないCか、シグナルを事前に処理しない場合があります。
また、Pythonには「例外」と「BaseExceptions」があり、インタプリタ自体が完全に終了する必要があるという意味で異なるため、一部の例外は他よりも高い優先順位を持っています(例外はBaseExceptionから派生しています)