まとめ
- Pythonの
sys.exit()
、exit
は組み込み例外SystemExit
を送出する。
よってtry-catch
の一部として振る舞いfinally
にも入る。 - 通常の例外やエラーとして誤って
catch
に捉えられないようにSystemExit
はBaseException
を継承している。(意識してcatch
することもできる。)
つまり、シャットダウン処理は上位のSystemExit
を受け取った場所で行われていると考えればいい。 - PHPの
exit()
はシャットダウン処理なので、デストラクタやシャットダウン関数は実行されるが、例外機構としてのfinally
は通過しない。
exit
を例外として処理するかしないかの違いですね。
PHP
exit
の解説やユーザーノートにあります。
例3 シャットダウン関数やデストラクタが実行される例
>
<?php
class Foo
{
public function __destruct()
{
echo 'Destruct: ' . __METHOD__ . '()' . PHP_EOL;
}
}
>
function shutdown()
{
echo 'Shutdown: ' . __FUNCTION__ . '()' . PHP_EOL;
}
>
$foo = new Foo();
register_shutdown_function('shutdown');
>
exit();
echo 'これは出力されません。';
?>
上の例の出力は以下となります。
Shutdown: shutdown()
Destruct: Foo::__destruct()
PHP: exit - Manual : User Contributed Notes
A side-note for the use of exit with finally: if you exit somewhere in a try block, the finally won't be executed. Could not sound obvious: for instance in Java you never issue an exit, at least a return in your controller; in PHP instead you could find yourself exiting from a controller method (e.g. in case you issue a redirect).
Here follows the POC:
<?php
echo "testing finally wit exit\n";
>
try {
echo "In try, exiting\n";
>
exit;
} catch(Exception $e) {
echo "catched\n";
} finally {
echo "in finally\n";
}
>
echo "In the end\n";
?>
This will print:
testing finally wit exit
In try, exiting
exitの代わりに例外をthrowして、catchで終了処理を書け、というものもありますが、完璧な代案ではないような。
PHP7で動かしてみても当然ですが同様の結果でした。
Python
Uchan Note: Python の exit(), sys.exit(), os._exit() の違い
を見れば一目です。
os._exit()
以外は例外の送出なので、finally
も関連します。
os._exit()
がPHPのexit()
相当かと言われると、またちょっと違うような説明に見えます。
exit
4. 組み込み定数 — Python 2.7.x ドキュメント
表示されたときに “Use quit() or Ctrl-D (i.e. EOF) to exit” のようなメッセージを出力し、呼び出されたときには指定された終了コードを伴って
SystemExit
を送出するオブジェクトです。
sys.exit()
28.1. sys — システムパラメータと関数 — Python 2.7.x ドキュメント
Python
を終了します。exit()
はSystemExit
を送出するので、try
ステートメントのfinally
節に終了処理を記述したり、上位レベルで例外を捕捉してexit
処理を中断したりすることができます。
(中略)
究極には、exit()
は例外を送出する “だけ” なので、これがメインスレッドから呼び出されたときは、プロセスを終了するだけで、例外は遮断されません。
6. 組み込み例外 — Python 2.7.x ドキュメント
この例外は sys.exit() 関数によって送出されます。この例外が処理されなかった場合、スタックのトレースバックを全く表示することなく Python インタプリタは終了します。関連値が通常の整数であれば、システム終了ステータスを表します (exit() 関数に渡されます)。値が None の場合、終了ステータスは 0 です。 (文字列のような) 他の型の場合、そのオブジェクトの値が表示され、終了ステータスは 1 になります。
この例外のインスタンスは属性 code を持ちます。この値は終了ステータスまたはエラーメッセージ (標準では None) に設定されます。また、この例外は厳密にはエラーではないため、 StandardError ではなく BaseException から派生しています。
sys.exit() は、クリーンアップのための処理 (try 文の finally 節) が実行されるようにするため、またデバッガが制御不能になるリスクを冒さずにスクリプトを実行できるようにするために例外に翻訳されます。即座に終了することが真に強く必要であるとき (例えば、 fork() を呼んだ後の子プロセス内) には os._exit() 関数を使うことができます。
この例外は Exception を捕まえるコードに間違って捕まえられないように、 StandardError や Exception からではなく BaseException を継承しています。これにより、この例外は着実に呼出し元の方に伝わっていってインタプリタを終了させます。
バージョン 2.5 で変更: BaseException を継承するように変更されました。
まさしく!ですね。
os._exit()
15.1. os — 雑多なオペレーティングシステムインタフェース — Python 2.7.x ドキュメント
終了ステータス n でプロセスを終了します。この時クリーンアップハンドラーの呼び出しや、標準入出力バッファーのフラッシュなどは行いません。
関係記事
同一ブログから。
finally
無い時代から実装後の時代への移り変わりですけど、
今回のexit
の動作も相まってデストラクタの再認識をした感じがします。