atexit — 종료 핸들러

atexit — 종료 핸들러

atexit 모듈은 종료 핸들러를 등록·등록 해제하는 함수를 정의해요. 종료 핸들러는 "종료 시(at exit)" — 즉 정상적인 프로그램 종료(sys.exit() 호출이나 메인 모듈 실행 완료) 또는 더 일반적으로 인터프리터 종료 시 — 자동으로 실행되는 함수예요.

종료 시 등록된 모든 종료 핸들러가 등록된 역순으로 호출돼요. A, B, C를 등록하면 인터프리터 종료 시 C, B, A 순서로 실행돼요. 그 이유는 낮은 수준의 모듈이 보통 높은 수준의 모듈보다 먼저 import되므로 나중에 정리되어야 한다는 가정 때문이에요.

종료 핸들러 실행 중에 예외가 발생하면 traceback이 출력되고(SystemExit가 아니면), 예외 정보가 저장돼요. 모든 종료 핸들러가 실행될 기회를 가진 후에는 마지막으로 발생한 예외가 다시 발생해요.

여러 인터프리터를 쓰는 프로그램에서는 각 인터프리터가 자신만의 종료 핸들러 스택을 갖고, 그 인터프리터가 종료될 때(예: concurrent.interpreters.Interpreter.close() 또는 C API Py_EndInterpreter()) 실행돼요. 이 모듈의 등록 함수는 호출된 인터프리터에만 영향을 줘요.

참고: 종료 핸들러는 프로그램이 파이썬이 처리하지 않는 신호에 의해 죽거나, 파이썬 치명적 내부 오류가 감지되거나, os._exit()가 호출될 때는 호출되지 않아요.

참고: 정리 함수 안에서 함수를 등록하거나 등록 해제하는 효과는 정의되지 않아요.

경고: 종료 핸들러를 작성할 때(특히 C API 확장에서), 정리 후 다른 종료 핸들러가 여전히 임의의 파이썬 코드를 실행할 수 있다는 점을 명심하세요. 그런 코드는 크래시가 아니라 예외로 성공하거나 실패해야 해요.

버전 변경 (3.12): 종료 핸들러에서 새 스레드를 시작하거나 os.fork()로 새 프로세스를 만들려 하면 이제 RuntimeError가 나요. 이전에는 메인 런타임 스레드가 스레드 상태를 해제하는 동안 내부 스레딩 루틴이나 새 프로세스가 그 상태를 쓰려 하면서 경쟁 조건이 생겨 깨끗한 종료가 아닌 크래시로 이어질 수 있었어요.

버전 변경 (3.7): 서브인터프리터와 함께 사용할 때 등록된 함수는 등록된 인터프리터에 국한돼요.

출처: Python documentation

본문

함수

  • atexit.register(func, *args, **kwargs): func를 종료 핸들러로 등록해요. func에 전달할 선택적 인자는 register()의 인자로 전달해야 해요. 같은 함수와 인자를 여러 번 등록할 수도 있어요. 이 함수는 func를 반환해서 데코레이터로 사용할 수 있게 해 줘요.
  • atexit.unregister(func): func를 종료 핸들러 목록에서 제거해요. func가 이전에 등록되지 않았다면 조용히 아무것도 하지 않아요. func가 여러 번 등록됐다면 atexit 호출 스택에서 그 함수의 모든 항목이 제거돼요. 등록 해제 중 내부적으로 동등성 비교(==)가 사용되므로 함수 참조의 정체성(identity)이 일치할 필요는 없어요.

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.')

이것은 인자 없이 호출할 수 있는 함수에서만 동작해요.

참고: readline 모듈이 atexit으로 readline 히스토리 파일을 읽고 쓰는 유용한 예제예요.

더 알아보기 (Learn more)