Error & Exception Handling
6. Exception Handling, Stack Unwinding & System Resilience
6.1Failure Paradigms & Exception Propagation
In software development, things will go wrong: network connections drop, disk drives fill up, or users type text into number boxes. When an error occurs during program execution, Python raises an Exception.
If an exception is not caught, Python stops running the current function and passes the exception up to the function that called it. This step-by-step backward walk is called Stack Unwinding. If no function handles the error all the way up to the top level, Python crashes and prints a Traceback (an error summary report showing exactly which lines led to the crash).
ANALOGY TIME
6.2Standard Exception Class Hierarchy
All exceptions in Python are structured in a built-in class family tree (hierarchy):
Crucial Safety Rule: Never catch BaseException or write a bare except:. Doing so accidentally catches KeyboardInterrupt, preventing users from stopping your program with Ctrl+C! Always catch specific errors like except ValueError:.
6.3Complete Control Flow: try / except / else / finally
Python gives us four blocks to handle errors gracefully:
- `try`: Put code here that might cause an error.
- `except`: Code here runs ONLY if a specific error occurs inside the
tryblock. - `else`: Code here runs ONLY if NO errors occurred inside the
tryblock. - `finally`: Code here ALWAYS runs no matter what—whether an error occurred or not! Perfect for cleanup tasks like closing database connections.
6.4EAFP vs LBYL Philosophies & Exception Chaining
There are two common design styles for handling potential errors in code:
- LBYL (Look Before You Leap): Check every condition explicitly before taking action (e.g. checking
if file_exists(path):before opening). Can lead to race conditions if the file is deleted right after the check! - EAFP (Easier to Ask Forgiveness than Permission): Python's preferred style! Assume the operation will succeed and wrap it in a
try/exceptblock to catch failures if they happen. - Exception Chaining (`raise ... from`): Wrapping a lower-level error inside a higher-level custom error while keeping the original error context intact.
6.5Code Demo 1: Complete try/except/else/finally Handler
Demonstrating all four control flow blocks together:
6.6Code Demo 2: Exception Chaining with raise ... from
Chain low-level system errors into custom domain errors:
GOTCHA 1
Using bare except: statements. This catches ALL exceptions, including KeyboardInterrupt (Ctrl+C) and SystemExit, making scripts impossible to stop in terminal. Always specify except Exception: or specific types.
GOTCHA 2
Swallowing exceptions silently by writing except Exception: pass. Always log the error or re-raise with raise.
GOTCHA 3
Putting code inside the finally block that returns a value. Returning inside finally overrides any return statement or active exception from the try/except block!
GOTCHA 4
Modifying state inside a try block before an operation fails, leaving the system in an inconsistent state. Perform validations before modifying state.
WHERE THIS SHOWS UP IN REAL CODE
Robust applications don't crash when faced with bad network inputs, invalid user data, or missing files. Exception handling isolates runtime errors, logs diagnostic telemetry, and triggers graceful fallback behavior.
TRY THIS YOURSELF
Modify Demo 1 to add a custom check for reserved ports (< 1024) printing a warning message.
Notice how the else block only runs when float conversion succeeds without raising a ValueError.
Exception Handling Paths
Demonstrate exception handling control flow (try/except/else/finally) and custom exception classes.
Let's handle division by zero errors safely with try / except!
Steps to Complete:
- Define
safe_divide(a, b). - Use
try / exceptto catchZeroDivisionError, printError: Division by zero, and returnNone. - Call
safe_divide(10, 0).
Input: a = 10, b = 0
Expected Output:
Let's handle invalid user age inputs gracefully!
Steps to Complete:
- Define
parse_age(val)that tries to convertvaltoint. - Catch
ValueErrorif conversion fails, printInvalid age integer, and return-1. - Call
parse_age('thirty').
Input: val = 'thirty'
Expected Output:
Let's create and catch a custom security exception!
Steps to Complete:
- Define class
InvalidTokenError(Exception). - Write
verify_token(token)raisingInvalidTokenError('Token expired')iftoken != 'VALID_SECRET'. - Call
verify_token('EXPIRED_XYZ')insidetry / exceptand printCaught: Token expired.
Input: token = 'EXPIRED_XYZ'
Expected Output:
What is the primary risk of using a bare `except:` block in Python?
When does the `else` block in a `try/except/else/finally` structure execute?
What guarantee does the `finally` block provide?
What does the EAFP coding philosophy stand for in Python?
How do you create a custom application exception class in Python?
Which exception type is raised when attempting to access a dictionary key that does not exist?
# comments · exceptions
- @priya · 2h ago
The typewriter hero is such a nice touch.
- @alex · 1h ago
Totally agree! Made it super engaging.
- @sam · 1d ago
Finally a course where I can run code without leaving the page.