Pythonのsys.exit()についてのメモ
先日、Pythonのsys.exit()について、いろいろ調べたので、そのことについてまとめておく。
なにはともあれ、公式ドキュメント sys --- システムパラメータと関数 — Python 3.8.5 ドキュメント を読む。
Python を終了します。 exit() は SystemExit を送出するので、 try ステートメントの finally 節に終了処理を記述したり、上位レベルで例外を捕捉して exit 処理を中断したりすることができます。
オプション引数 arg には、終了ステータスとして整数 (デフォルトは0)や他の型のオブジェクトを指定することができます。整数を指定した場合、シェル等は 0 は "正常終了"、0 以外の整数を "異常終了" として扱います。多くのシステムでは、有効な終了ステータスは 0--127 で、これ以外の値を返した場合の動作は未定義です。システムによっては特定の終了コードに個別の意味を持たせている場合がありますが、このような定義は僅かしかありません。Unix プログラムでは構文エラーの場合には 2 を、それ以外のエラーならば 1 を返します。arg に None を指定した場合は、数値の 0 を指定した場合と同じです。それ以外の型のオブジェクトを指定すると、そのオブェクトが stderr に出力され、終了コードとして 1 を返します。エラー発生時には sys.exit("エラーメッセージ") と書くと、簡単にプログラムを終了することができます。
究極には、 exit() は例外を送出する "だけ" なので、これがメインスレッドから呼び出されたときは、プロセスを終了するだけで、例外は遮断されません。
バージョン 3.6 で変更: Python インタープリタが (バッファされたデータを標準ストリームに吐き出すときのエラーなどの) SystemExit を捕捉した後の後始末でエラーが起きた場合、終了ステータスは 120 に変更されます。
まず、基本的な使い方は、きっと
import sys print("Hello!") sys.exit() print("Hello!!")
(二回目のprintは実行されない)です。
知りたかったこと1. sys.exitのリターンコードは一体どうなるのか
ドキュメントでは、オプション引数argが、整数か他の型で異なると書いてあります。整数とNone以外のオプジェクトを渡すと、stderrにオブジェクトを表示させて、プログラムを終了すると書かれていますが、
その時のリターンコードが1だと書かれています。
整数を渡したときは、どうでしょうか。例えば、-1や300を渡したら、どうなるかを調べてみました。(環境依存?)
$ python3.7 rtn_code.py 1; echo $? 1 1 $ python3.7 rtn_code.py 255; echo $? 255 255 $ python3.7 rtn_code.py 256; echo $? 256 0 $ python3.7 rtn_code.py -1; echo $? -1 255
となり、循環した整数になるようです。
知りたかったこと2 sys.exitするのと、raise SystemExitとの違い
これは、ほぼほぼ違いがないようです。raise SystemExit(99)とか書けば良いっぽい。
知りたくなったこと SystemExitとはなんぞや。
通常の例外は、Exceptionを継承していますが、SystemExitは、BaseExceptionを継承しているようです。この違いっていまいち不明だが・・・。
話は変わり、flake8のE722
flake8で、
try: f = open("hoge.txt", "r") except: print("except")
のようなコードを書くと、flake8で怒られます。
E722 do not use bare 'except'
exceptのままで使うなと。(この例だと、ちゃんとFileNotFoundErrorをExceptしてあげれば良い。)
で、実験
以下のようなコードがあったとき、何が出力されるのか。
import sys try: sys.exit(10) except Exception: print("sys.exit E") except BaseException: print("sys.exit BE") try: raise SystemExit(10) except Exception: print("SystemExit E") except SystemExit: print("SystemExit BE") try: raise except Exception: print("raise E") except BaseException: print("raise BE")
答えは、
sys.exit BE SystemExit BE raise E
sys.exit()しても、raise SystemExitしても、BaseExceptionで補足されている。つまり、Exceptionでは補足されないです。(ドキュメント通り)
で、次は、bare exceptを使った場合。
import sys try: raise except: print("raise bare except") try: raise SystemExit(10) except: print("SystemExit bare except")
結果は、
raise bare except SystemExit bare except
となる。つまり、bare exceptを使うと、SystemExitも、Exceptionの例外も両方握り潰す処理することになる。
(昔知ったTips)
Jupyter notebookで、処理の途中で、exit相当のことをしたいなと思って、sys.exitを書くと、カーネルごとお亡くなりになります。
そこで、raiseをかませるというTipsを、昔StackOverflowか何かで読んだ記憶があります。(けど、最近のjupyter labとか使うと、カーネル落ちなくなってて、賢い!と思ったり)
まとめ
E722 do not use bare 'except'