# `atexit` --- 終了ハンドラー

The `atexit` module defines functions to register and unregister cleanup functions. Functions thus registered are automatically executed upon normal interpreter termination. `atexit` runs these functions in the reverse order in which they were registered; if you register `A`, `B`, and `C`, at interpreter termination time they will be run in the order `C`, `B`, `A`.

注意: このモジュールを使用して登録された関数は、プログラムが Python が扱わないシグナルによって kill された場合、Python 内部で致命的なエラーが検出された場合、あるいは `os._exit()` が呼び出された場合は実行されません。

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

バージョン 3.7 で変更: C-API のサブインタープリタで使われているとき、登録された関数は登録先のインタープリタのローカルな関数になります。

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

  func を終了時に実行する関数として登録します。func に渡す引数は `register()` の引数として指定しなければなりません。同じ関数を同じ引数で複数回登録できます。

  通常のプログラムの終了時、例えば `sys.exit()` が呼び出されるとき、あるいは、メインモジュールの実行が完了したときに、登録された全ての関数を、最後に登録されたものから順に呼び出します。通常、より低レベルのモジュールはより高レベルのモジュールより前に import されるので、後で後始末が行われるという仮定に基づいています。

  終了ハンドラの実行中に例外が発生すると、(`SystemExit` 以外の場合は)トレースバックを表示して、例外の情報を保存します。全ての終了ハンドラに動作するチャンスを与えた後に、最後に送出された例外を再送出します。

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

  警告

  登録された関数から新しいスレッドを開始したり、 `os.fork()` を呼び出したりすると、メインの Python ランタイムスレッドがスレッド状態を開放する一方で、内部の `threading` ルーチンや新しいプロセスがそのスレッド状態を使用しようと試みる競合が発生します。これはクリーンなシャットダウンではなく、クラッシュにつながる恐れがあります。

  バージョン 3.12 で変更: 登録された関数から新しいスレッドの開始または新しいプロセスの `os.fork()` が試みられた場合は、 `RuntimeError` が発生するようになりました。

atexit.unregister(func)

  Remove func from the list of functions to be run at interpreter shutdown. `unregister()` silently does nothing if func was not previously registered. If func has been registered more than once, every occurrence of that function in the `atexit` call stack will be removed. Equality comparisons (`==`) are used internally during unregistration, so function references do not need to have matching identities.

参考

`readline` モジュール

  Useful example of `atexit` to read and write `readline` history files.

## `atexit` Example

次の簡単な例では、あるモジュールを import した時にカウンタを初期化しておき、プログラムが終了するときにアプリケーションがこのモジュールを明示的に呼び出さなくてもカウンタが更新されるようにする方法を示しています。

    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')
    # or:
    atexit.register(goodbye, adjective='nice', name='Donny')

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

    import atexit

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

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