atexit --- 終了ハンドラ


atexit モジュールは、クリーンアップ関数の登録およびその解除を行う関数を定義します。登録された関数はインタプリタの通常終了時に自動的に実行されます。atexit はそれら関数を登録した順と 逆に 実行します; ABC の順に登録した場合、インタプリタ終了時に CBA の順に実行されます。

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

Note: The effect of registering or unregistering functions from within a cleanup function is undefined.

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

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

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

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

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

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

警告

Starting new threads or calling os.fork() from a registered function can lead to race condition between the main Python runtime thread freeing thread states while internal threading routines or the new process try to use that state. This can lead to crashes rather than clean shutdown.

バージョン 3.12 で変更: Attempts to start a new thread or os.fork() a new process in a registered function now leads to RuntimeError.

atexit.unregister(func)

関数 func をインタプリタ終了時に実行する関数のリストから削除します。func が登録されていなければ、unregister() は何もしません。func が2回以上登録されている場合は atexit のコールスタックにおける func のすべての出現が削除されます。削除の際の比較には等価比較 (==) が使われます。したがって削除したいものと同一の関数を func に指定する必要はありません。

参考

readline モジュール

readline ヒストリファイルを読み書きするための atexit の有用な例です。

atexit の例

次の簡単な例では、あるモジュールを 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.')

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