atexit registers functions that run when the interpreter terminates normally. It fits small, local cleanup such as removing a temporary marker or emitting one final metric.

Practical example

import atexit
from pathlib import Path

temporary = Path("job.lock")
temporary.touch()

@atexit.register
def remove_lock() -> None:
    temporary.unlink(missing_ok=True)
    print("lock removed")

Order and arguments

Functions run in reverse registration order. register() accepts arguments too, but a short function without fragile dependencies is more predictable during shutdown.

What not to delegate

Do not make a handler the only way to save important data. The process may receive SIGKILL, hit a fatal failure, or stop before completion. Persist progress atomically during normal work.

Prefer a context manager

When a resource has a clear scope, with closes files, connections, and locks at the right point, including after exceptions. Keep atexit for resources tied to the whole process lifetime.

Keep learning

Strengthen the foundation with error handling in Python. The official Python documentation, accessed July 22, 2026, documents the API, limitations, and version differences.