2019年03月29日

Jython: Ctrl-Cを検知する方法

Jythonである程度常駐するプログラムを作ったときに、終了処理を加えたかった。

ユーザーは途中でCtrl-Cで止めることもあるし、上限時間まで起動させておくこともある。この両方の止め方+異常終了(exceptで捕捉しきれてない例外)時にも終了処理をフックしたい。

ふつうのPythonだとatexit.registerを使うと「Ctrl-Cで中止」「通常終了」「異常終了」すべてで実行される。さらにKeyboardInterruptを捕捉しておくとTracebackが出なくていい。(というかPython的にはCtrl-C = KeyboardInterruptで異常終了)

import time
import sys
import atexit

def cleanup():
    print("Bye")

atexit.register(cleanup)

try:
    for i in range(60):
        print(i)
        time.sleep(1)
except KeyboardInterrupt:
    pass

ところがJythonではatexitが「通常終了」「異常終了」のときしか実行されない。 代わりにsignal.signalを使ってSIGINTシグナルにハンドラを設定する必要がある。

import time
import sys
import atexit
import signal

def cleanup():
    print("Bye")

def signal_catcher(signum, frame):
    print("SIGNAL {} received.".format(signum))
    sys.exit()

atexit.register(cleanup)
signal.signal(signal.SIGINT, signal_catcher)

for i in range(60):
    print(i)
    time.sleep(1)

signalモジュールの説明によると、PythonはデフォルトでSIGINTをKeyboardInterruptに変換していてsignal.signalを使うとそれを上書きすることになる。つまりCPythonではKeyboardInterruptが発生しなくなるのでTraceback抑制のための節は必要なくなる。

atexit.registerを登録した状態での挙動

実装 終了方法 実行されるもの
CPython/Jython sys.exit atexit.register → 終了
CPython/Jython スクリプトの終端 atexit.register → 終了
CPython/Jython 異常終了 Traceback出力 → atexit.register → 終了
CPython/Jython Ctrl-C (signal.signalあり) signal.signal (sys.exitさせないとスクリプトは続く)
CPython Ctrl-C (signal.signalなし) KeyboardInterrupt発生→異常終了と同じ
Jython Ctrl-C (signal.signalなし) 終了

JythonではKeyboardInterruptを出さない?

バグ報告をさらってみると下記のものが見つかった。前者はインタラクティブシェルでのバグ報告で、後者は非対話型スクリプトの話。なのに重複としてマークされてる。

最終更新が古いので治ってるかと思いJython 2.7.1で試したら、シェルではwhileループをCtrl-Cで抜けられなくて焦った。そしてシェルの入力待ち状態ではCtrl-CでKeyboardInterruptが発生した。謎である。

posted by かぷらす at 10:00| Comment(0) | 作業記録 | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
コチラをクリックしてください