"atexit" --- 終了ハンドラー
***************************

======================================================================

"atexit" モジュールは、*終了ハンドラー* を登録・解除する関数を定義しま
す。 終了ハンドラーは、プログラムの正常終了時 (例えば "sys.exit()" が
呼び出されたときや、メインモジュールの実行が完了したとき)、より一般的
には *interpreter shutdown* 時に自動的に実行される関数です。

終了時には、登録されているすべての終了ハンドラーを登録した順序の *逆順
* に呼び出します。 "A"、"B"、"C" の順に登録すると、インタープリタの終
了時には "C"、"B"、"A" の順に実行します。 これは、通常は低水準のモジュ
ールが高水準のモジュールより先にインポートされるので、後片付けは後で行
う必要があるという想定に基づいています。

If an exception is raised during execution of an exit handler, a
traceback is printed (unless "SystemExit" is raised) and the exception
information is saved.  After all exit handlers have had a chance to
run, the last exception to be raised is re-raised.

複数のインタープリタを使うプログラムでは、各インタープリタが終了ハンド
ラーのスタックを持ちます。 ハンドラーは、そのインタープリタが終了する
とき (例えば "concurrent.interpreters.Interpreter.close()" や C API の
"Py_EndInterpreter()" による終了時) に実行されます。 このモジュールの
登録用関数が影響するのは、呼び出し元のインタープリタだけです。

**注意:** Python が処理しないシグナルによってプログラムが強制終了した
場合、Python 内部の致命的なエラーが検出された場合、または "os._exit()"
が呼び出された場合、終了ハンドラーは呼び出されません。

**注意:** クリーンアップ関数内からの関数の登録・登録解除効果は定義され
ていません。

警告:

  終了ハンドラーを作成するとき、特に C API を使った拡張では、自身の後
  片付けが終わった後も、他の終了ハンドラーが任意の Python コードを実行
  する可能性があることに注意してください。 そのようなコードは正常に実
  行されるか例外で失敗するべきであり、クラッシュしてはいけません。

バージョン 3.12 で変更: 終了ハンドラー内で新しいスレッドを開始したり、
"os.fork()" で新しいプロセスを作成したりしようとすると、"RuntimeError"
を送出するようになりました。 以前は、Python ランタイムのメインスレッド
がスレッド状態を解放する一方で、"threading" 内部のルーチンや新しいプロ
セスがその状態を使おうとして、競合状態が発生する可能性がありました。
その結果、正常に終了せずクラッシュすることがありました。

バージョン 3.7 で変更: サブインタープリタで使う場合、登録した関数は登
録先のインタープリタに固有となりました。

atexit.register(func, *args, **kwargs)

   *func* を終了ハンドラーとして登録します。 *func* に渡す引数があれば
   、"register()" の引数として渡してください。 同じ関数と引数を複数回
   登録することもできます。

   この関数は *func* を返し、これをデコレータとして利用できます。

atexit.unregister(func)

   終了ハンドラーのリストから *func* を削除します。 *func* が登録され
   ていなければ、"unregister()" は何もせず、エラーも出しません。
   *func* が複数回登録されていた場合、"atexit" の呼び出しスタックから
   、その関数をすべて削除します。 登録解除では内部で等価比較 ("==") を
   使うため、関数の参照先が同一のオブジェクトである必要はありません。

参考:

  "readline" モジュール
     "readline" の履歴ファイルを読み書きする、"atexit" の実用的な使用
     例です。


"atexit" の使用例
=================

次の簡単な例では、モジュールのインポート時にファイルからカウンタの値を
読み込んで初期化し、プログラムの終了時に更新後の値を自動保存する方法を
示します。 アプリケーションが終了時にこのモジュールを明示的に呼び出す
必要はありません。

   try:
       with open('counterfile') as infile:
           _count = int(infile.read())
   except FileNotFoundError:
       _count = 0

   def incrcounter(n):
       global _count
       _count = _count + n

   def savecounter():
       with open('counterfile', 'w') as outfile:
           outfile.write('%d' % _count)

   import atexit

   atexit.register(savecounter)

"register()" に指定した位置引数とキーワード引数は登録した関数を呼び出
す際に渡されます:

   def goodbye(name, adjective):
       print('Goodbye %s, it was %s to meet you.' % (name, adjective))

   import atexit

   atexit.register(goodbye, 'Donny', 'nice')
   # または:
   atexit.register(goodbye, adjective='nice', name='Donny')

*デコレータ* として利用する例:

   import atexit

   @atexit.register
   def goodbye():
       print('You are now leaving the Python sector.')

デコレータとして利用できるのは、引数なしで呼び出せる関数に限られます。
