Exception Handling & Fault Resilience
In production software, failures are inevitable—databases disconnect, API rate limits are exceeded, and user input formats fail. Learn how Python resolves errors through its BaseException hierarchy, how to harness the clean separation of else and finally blocks, and how to create domain-specific custom exceptions with chained tracebacks.
1. The Python Exception Hierarchy
All Python errors inherit from BaseException. Standard application errors inherit fromException:
except: or except BaseException:! Catching BaseException traps KeyboardInterrupt and SystemExit, making it impossible for administrators to terminate your application using Ctrl+C! Always catch except Exception:.2. The Complete "try...except...else...finally" Pattern
Most developers only know try and except. The else and finally blocks are essential for clean resource management:
try: Code that might throw an exception.except SpecificError as e: Handles the exact targeted failure.else: Executes only if NO exception occurred in the try block!finally: Guaranteed to execute unconditionally, even ifreturnwas called!
def parse_user_age(raw_input: str) -> int | None:
try:
age = int(raw_input)
except ValueError as err:
print(f"Validation Error: '{raw_input}' is not a valid integer. Detail: {err}")
return None
else:
# Executes ONLY when int() conversion succeeded!
print("Validation succeeded! Proceeding to database write...")
return age
finally:
# Always runs for metrics cleanup or auditing
print("Audit: Age parsing transaction finished.")
parse_user_age("28") # Validates successfully, runs else & finally
parse_user_age("invalid") # Traps ValueError, runs finally3. Custom Exceptions & Exception Chaining ("raise ... from")
Enterprise architectures define custom domain exceptions to avoid leaking low-level SQL or HTTP details up to UI callers. Use raise ... from (PEP 3134) to preserve the original traceback for debugging:
# 1. Define custom application domain exception hierarchy
class PaymentGatewayError(Exception):
"""Base exception for all billing and checkout failures."""
pass
class InsufficientFundsError(PaymentGatewayError):
def __init__(self, required: float, available: float):
super().__init__(f"Transaction requires ${required}, but account balance is only ${available}.")
self.required = required
self.available = available
# 2. Re-raise with root-cause preservation
def process_checkout(user_id: int, charge_amount: float):
try:
# Simulated low-level network failure
raise ConnectionResetError("Stripe API socket reset by peer.")
except ConnectionResetError as original_error:
# Wrap in high-level domain exception while preserving root cause traceback
raise PaymentGatewayError("Payment provider unreachable during checkout.") from original_error4. Modern Python 3.11+ ExceptionGroup & except*
When running concurrent tasks with asyncio.TaskGroup, multiple asynchronous coroutines can raise errors simultaneously. Python 3.11 introduced ExceptionGroup and the except* syntax to catch multiple concurrent errors:
# Grouping multiple concurrent failures
try:
raise ExceptionGroup(
"Multiple background worker failures",
[ValueError("Invalid currency code"), FileNotFoundError("Missing cache file")]
)
except* ValueError as eg:
print(f"Handled ValueErrors: {eg.exceptions}")
except* FileNotFoundError as eg:
print(f"Handled FileErrors: {eg.exceptions}")Explore Related Tools
Hand-picked utilities and calculators related to this tool.
