The atexit module registers functions to run when your program exits normally. Essential for cleanup tasks.
Basic Registration
import atexit
def cleanup():
print("Cleaning up...")
atexit.register(cleanup)
# Program runs...
# At exit: "Cleaning up..."Register with Arguments
import atexit
def save_data(filename, data):
print(f"Saving to {filename}")
with open(filename, 'w') as f:
f.write(str(data))
# Register with args
atexit.register(save_data, 'backup.txt', {'key': 'value'})
# Or use decorator syntax for no-arg functions
@atexit.register
def goodbye():
print("Goodbye!")Execution Order
Handlers run in reverse registration order (LIFO):
import atexit
atexit.register(print, "First registered, last executed")
atexit.register(print, "Second registered, second executed")
atexit.register(print, "Third registered, first executed")
# At exit:
# Third registered, first executed
# Second registered, second executed
# First registered, last executedUnregister Handlers
import atexit
def cleanup():
print("Cleaning up...")
atexit.register(cleanup)
# Later, if cleanup no longer needed:
atexit.unregister(cleanup)When atexit Runs
Runs on:
- Normal program termination
sys.exit()calls- Unhandled exceptions reaching top level
Doesn't run on:
os._exit()(immediate exit)SIGKILLsignal- Fatal Python errors
os.fork()in child process (unless registered after fork)
Practical Examples
Temporary File Cleanup
import atexit
import tempfile
import os
temp_files = []
def create_temp_file():
fd, path = tempfile.mkstemp()
temp_files.append(path)
return path
def cleanup_temp_files():
for path in temp_files:
try:
os.unlink(path)
except OSError:
pass
atexit.register(cleanup_temp_files)Database Connection
import atexit
class Database:
def __init__(self, connection_string):
self.conn = connect(connection_string)
atexit.register(self.close)
def close(self):
if self.conn:
self.conn.close()
self.conn = None
db = Database("localhost:5432")
# Connection automatically closed at exitLogging Session End
import atexit
import logging
from datetime import datetime
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
start_time = datetime.now()
def log_session_end():
duration = datetime.now() - start_time
logger.info(f"Session ended. Duration: {duration}")
atexit.register(log_session_end)
logger.info("Session started")Save Application State
import atexit
import json
class AppState:
def __init__(self, state_file='state.json'):
self.state_file = state_file
self.data = self._load()
atexit.register(self._save)
def _load(self):
try:
with open(self.state_file) as f:
return json.load(f)
except FileNotFoundError:
return {}
def _save(self):
with open(self.state_file, 'w') as f:
json.dump(self.data, f)
def set(self, key, value):
self.data[key] = value
state = AppState()
state.set('last_run', str(datetime.now()))
# Automatically saved at exitPID File Management
import atexit
import os
PID_FILE = '/var/run/myapp.pid'
def write_pid():
with open(PID_FILE, 'w') as f:
f.write(str(os.getpid()))
def remove_pid():
try:
os.unlink(PID_FILE)
except OSError:
pass
write_pid()
atexit.register(remove_pid)Combining with Signal Handling
import atexit
import signal
import sys
cleanup_done = False
def cleanup():
global cleanup_done
if cleanup_done:
return
cleanup_done = True
print("Cleanup...")
def signal_handler(signum, frame):
cleanup()
sys.exit(0)
atexit.register(cleanup)
signal.signal(signal.SIGTERM, signal_handler)
signal.signal(signal.SIGINT, signal_handler)Context Manager Alternative
For scoped cleanup, prefer context managers:
# atexit: program-level cleanup
import atexit
atexit.register(global_cleanup)
# Context manager: scoped cleanup
from contextlib import contextmanager
@contextmanager
def managed_resource():
resource = acquire()
try:
yield resource
finally:
resource.release()
with managed_resource() as r:
use(r)
# Cleaned up here, not at program exitQuick Reference
import atexit
# Register function
atexit.register(func)
atexit.register(func, *args, **kwargs)
# Decorator (no args)
@atexit.register
def cleanup():
pass
# Unregister
atexit.unregister(func)| Scenario | Solution |
|---|---|
| Cleanup on exit | atexit.register() |
| Scoped cleanup | Context manager |
| Handle signals | signal + atexit |
| Force immediate exit | os._exit() (skips atexit) |
atexit is your last chance to run code. Use it for cleanup that must happen regardless of how the program ends.
React to this post: