← Back to Python series
⚙️
Intermediate
try · except · else · finally · raise · Custom exceptions

Week 5 — Exception Handling

Write robust programs by handling errors gracefully. Learn the full try/except/else/finally pattern, re-raise exceptions, and define custom exception classes.

exceptiontryexceptraisecustom exception
Duration
2 hours
Level
📊 Intermediate
Prerequisite
🎯 Basic Week 9
OUTCOME
Wrap a file-reading function with proper error handling and a custom exception

What you'll learn

  • 1Catch specific exceptions with except ExceptionType
  • 2Use else (runs when no exception) and finally (always runs)
  • 3Re-raise exceptions with raise
  • 4Create custom exception classes
  • 5Log exceptions and provide user-friendly error messages

1. try / except / else / finally

python
def read_int(prompt):
    try:
        return int(input(prompt))
    except ValueError as e:
        print(f"Invalid input: {e}")
        return None

try:
    with open("data.txt") as f:
        data = f.read()
except FileNotFoundError:
    print("File not found")
except PermissionError:
    print("Permission denied")
else:
    print("File read successfully")
finally:
    print("Done (always runs)")

2. Custom Exceptions

python
class ValidationError(ValueError):
    """Raised when user input fails validation."""
    def __init__(self, field, message):
        super().__init__(f"{field}: {message}")
        self.field = field

def validate_age(age):
    if not isinstance(age, int):
        raise ValidationError("age", "must be an integer")
    if age < 0 or age > 150:
        raise ValidationError("age", "must be between 0 and 150")

try:
    validate_age(-5)
except ValidationError as e:
    print(f"Validation failed — {e}")

3. Exception Hierarchy

ℹ️

BaseException → Exception → ValueError, TypeError, OSError (FileNotFoundError, PermissionError), IndexError, KeyError, etc. Catch the most specific exception you can handle.

4. Common Mistakes

  1. Bare except: (no type) catches everything including KeyboardInterrupt and SystemExit. Always specify a type.
  2. Swallowing exceptions silently: except Exception: pass hides bugs. At minimum log them.
  3. Using exceptions for normal control flow — e.g., catching ValueError to detect end of input instead of checking first.

💻 Examples

Run these examples and check the output yourself.

01_safe_divide.pyDivision with multiple exception handling
CODE
def safe_divide(a, b):
    try:
        result = a / b
    except ZeroDivisionError:
        return None, "Cannot divide by zero"
    except TypeError as e:
        return None, f"Type error: {e}"
    else:
        return result, None

for a, b in [(10, 2), (10, 0), (10, "x")]:
    val, err = safe_divide(a, b)
    if err:
        print(f"{a}/{b} → Error: {err}")
    else:
        print(f"{a}/{b} = {val}")
▶ Output
10/2 = 5.0
10/0 → Error: Cannot divide by zero
10/x → Error: Type error: unsupported operand type(s) for /: 'int' and 'str'
02_custom_exc.pyCustom exception hierarchy
CODE
class AppError(Exception): pass
class DatabaseError(AppError): pass
class NetworkError(AppError): pass

def connect(host):
    if not host:
        raise NetworkError("Host cannot be empty")
    if host == "bad-host":
        raise NetworkError(f"Cannot reach {host}")
    return f"Connected to {host}"

for host in ["db.example.com", "", "bad-host"]:
    try:
        print(connect(host))
    except NetworkError as e:
        print(f"Network error: {e}")
▶ Output
Connected to db.example.com
Network error: Host cannot be empty
Network error: Cannot reach bad-host

📝 Exercises

Try them yourself first, then open the solution to compare.

Exercise 1

Robust Input Loop

Goal: Write a function get_positive_int(prompt) that keeps asking until valid.

Requirements
  • Catches ValueError for non-integers
  • Validates that the value is positive
  • Returns the valid integer
Toggle solution
SOLUTION
def get_positive_int(prompt):
    while True:
        try:
            n = int(input(prompt))
            if n <= 0:
                raise ValueError("must be positive")
            return n
        except ValueError as e:
            print(f"  Invalid: {e}. Try again.")

n = get_positive_int("Enter a positive integer: ")
print(f"Got: {n}")
Example code / lecture materials

All lecture materials and example code are openly available on GitHub.

View on GitHub ↗