Chapter 11 of 21
File I/O & Serialization
Current Study Module
Exception Handling & Defenses
OOP: Classes & Objects
Chapter 10 • Defensive Engineering

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:

BaseException
├── KeyboardInterrupt (Triggered by Ctrl+C in terminal)
├── SystemExit (Triggered by sys.exit())
└── Exception (Base class for ALL application errors)
├── ArithmeticError (ZeroDivisionError, OverflowError)
├── LookupError (IndexError, KeyError)
├── ValueError (Invalid data representation)
└── OSError (FileNotFoundError, PermissionError)
❌ CRITICAL ANTI-PATTERN: Never write a bare 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 if return was 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 finally

3. 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_error

4. 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.

Related Tools