Your validation function checks if age is negative. You need to signal "this is wrong" to the caller. raise creates and throws an exception. The caller must handle it or let it propagate up.

Raise for invalid input

Signal that input doesn't meet requirements.

usernames
invalid_input.py
Replay: real traced execution (multi-file project)
# Raising exceptions on invalid input

def main():
    # Raise on invalid input
    def check_age(age):
        if age < 0:
            raise ValueError("Age cannot be negative")
        if age > 150:
            raise ValueError("Age is unrealistic")
        return age

    print("Age validation:")

    # Valid age
    try:
        result = check_age(25)
        print(f"  Age {result}: Valid")
    except ValueError as e:
        print(f"  Error: {e}")

    # Invalid age
    try:
        result = check_age(-5)
        print(f"  Age {result}: Valid")
    except ValueError as e:
        print(f"  Error: {e}")


    # Raise on division by zero
    def safe_divide(a, b):
        if b == 0:
            raise ZeroDivisionError("Cannot divide by zero")
        return a / b

    print("\nDivision:")

    try:
        result = safe_divide(10, 2)
        print(f"  10 / 2 = {result}")
    except ZeroDivisionError as e:
        print(f"  Error: {e}")

    try:
        result = safe_divide(10, 0)
        print(f"  10 / 0 = {result}")
    except ZeroDivisionError as e:
        print(f"  Error: {e}")

    # Validate string input
    def validate_username(username):
        if not username:
            raise ValueError("Username cannot be empty")
        if len(username) < 3:
            raise ValueError("Username must be at least 3 characters")
        if not username.isalnum():
            raise ValueError("Username must be alphanumeric")
        return username

    print("\nUsername validation:")

    usernames = ["alice", "ab", "", "user@123"]

    for name in usernames:
        try:
            valid = validate_username(name)
            print(f"  '{name}': OK")
        except ValueError as e:
            print(f"  '{name}': {e}")

if __name__ == "__main__":
    main()
# Raising exceptions on invalid input

def main():
    # Raise on invalid input
    def check_age(age):
        if age < 0:
            raise ValueError("Age cannot be negative")
        if age > 150:
            raise ValueError("Age is unrealistic")
        return age

    print("Age validation:")

    # Valid age
    try:
        result = check_age(25)
        print(f"  Age {result}: Valid")
    except ValueError as e:
        print(f"  Error: {e}")

    # Invalid age
    try:
        result = check_age(-5)
        print(f"  Age {result}: Valid")
    except ValueError as e:
        print(f"  Error: {e}")


    # Raise on division by zero
    def safe_divide(a, b):
        if b == 0:
            raise ZeroDivisionError("Cannot divide by zero")
        return a / b

    print("\nDivision:")

    try:
        result = safe_divide(10, 2)
        print(f"  10 / 2 = {result}")
    except ZeroDivisionError as e:
        print(f"  Error: {e}")

    try:
        result = safe_divide(10, 0)
        print(f"  10 / 0 = {result}")
    except ZeroDivisionError as e:
        print(f"  Error: {e}")

    # Validate string input
    def validate_username(username):
        if not username:
            raise ValueError("Username cannot be empty")
        if len(username) < 3:
            raise ValueError("Username must be at least 3 characters")
        if not username.isalnum():
            raise ValueError("Username must be alphanumeric")
        return username

    print("\nUsername validation:")

    usernames = ["bob", ""]

    for name in usernames:
        try:
            valid = validate_username(name)
            print(f"  '{name}': OK")
        except ValueError as e:
            print(f"  '{name}': {e}")

if __name__ == "__main__":
    main()
# Raising exceptions on invalid input

def main():
    # Raise on invalid input
    def check_age(age):
        if age < 0:
            raise ValueError("Age cannot be negative")
        if age > 150:
            raise ValueError("Age is unrealistic")
        return age

    print("Age validation:")

    # Valid age
    try:
        result = check_age(25)
        print(f"  Age {result}: Valid")
    except ValueError as e:
        print(f"  Error: {e}")

    # Invalid age
    try:
        result = check_age(-5)
        print(f"  Age {result}: Valid")
    except ValueError as e:
        print(f"  Error: {e}")


    # Raise on division by zero
    def safe_divide(a, b):
        if b == 0:
            raise ZeroDivisionError("Cannot divide by zero")
        return a / b

    print("\nDivision:")

    try:
        result = safe_divide(10, 2)
        print(f"  10 / 2 = {result}")
    except ZeroDivisionError as e:
        print(f"  Error: {e}")

    try:
        result = safe_divide(10, 0)
        print(f"  10 / 0 = {result}")
    except ZeroDivisionError as e:
        print(f"  Error: {e}")

    # Validate string input
    def validate_username(username):
        if not username:
            raise ValueError("Username cannot be empty")
        if len(username) < 3:
            raise ValueError("Username must be at least 3 characters")
        if not username.isalnum():
            raise ValueError("Username must be alphanumeric")
        return username

    print("\nUsername validation:")

    usernames = ["user1", "a", "test!", "valid123"]

    for name in usernames:
        try:
            valid = validate_username(name)
            print(f"  '{name}': OK")
        except ValueError as e:
            print(f"  '{name}': {e}")

if __name__ == "__main__":
    main()
  1. def main(): # Raise on invalid input

    3def main():4    # Raise on invalid input5    def check_age(age):6        if age < 0:7            raise ValueError("Age cannot be negative")8        if age > 150:9            raise ValueError("Age is unrealistic")10        return age11    12    print("Age validation:")
    outputAge validation:
  2. def check_age(age):

    pass 1 of 2
    4# Raise on invalid input5def check_age(age25):6    if age < 0:7        raise ValueError("Age cannot be negative")8    if age > 150:9        raise ValueError("Age is unrealistic")10    return age25
  3. result ← 25

    15try:16    result→ 25 = check_age(25)17    print(f"  Age {result25}: Valid")18except ValueError as e:
    output  Age 25: Valid
  4. def check_age(age):

    pass 2 of 2
    4# Raise on invalid input5def check_age(age-5):6    if age < 0:7        raise ValueError("Age cannot be negative")
  5. if age < 0:

    5def check_age(age):6    if age-5 < 0:7        raise ValueError("Age cannot be negative")8    if age > 150:
  6. except ValueError as e:

    24    print(f"  Age {result}: Valid")25except ValueError as e:26    print(f"  Error: {eAge cannot be negative}")2728#@help h1
    output  Error: Age cannot be negative
      Error: Age cannot be negative
  7. print(" Division:")

    40print("\nDivision:")
    output
    Division:
  8. def safe_divide(a, b):

    pass 1 of 2
    34# Raise on division by zero35def safe_divide(a10, b2):36    if b == 0:37        raise ZeroDivisionError("Cannot divide by zero")38    return a10 / b2
  9. result ← 5.0

    42try:43    result→ 5.0 = safe_divide(10, 2)44    print(f"  10 / 2 = {result5.0}")45except ZeroDivisionError as e:
    output  10 / 2 = 5.0
  10. def safe_divide(a, b):

    pass 2 of 2
    34# Raise on division by zero35def safe_divide(a10, b0):36    if b == 0:37        raise ZeroDivisionError("Cannot divide by zero")
  11. if b == 0:

    35def safe_divide(a, b):36    if b0 == 0:37        raise ZeroDivisionError("Cannot divide by zero")38    return a / b
  12. except ZeroDivisionError as e:

    50    print(f"  10 / 0 = {result}")51except ZeroDivisionError as e:52    print(f"  Error: {eCannot divide by zero}")5354# Validate string input
    output  Error: Cannot divide by zero
      Error: Cannot divide by zero
  13. usernames ← ['alice', 'ab', '', 'user@123']

    64print("\nUsername validation:")6566usernames→ ['alice', 'ab', '', 'user@123'] = ["alice", "ab", "", "user@123"]67#@usernames=["bob", ""], ["user1", "a", "test!", "valid123"]
    output
    Username validation:
  14. for name in usernames:

    pass 1 of 4
    69for namealice in usernames['alice', 'ab', '', 'user@123']:70    try:71        valid = validate_username(name)
    All 4 passes — pass 1 is the card above
    passnameusername
    1alice
    2abab
    3(empty)(empty)
    4user@123user@123
  15. try:

    pass 1 of 4
    69for name in usernames:70    try:71        valid = validate_username(namealice)72        print(f"  '{name}': OK")
    All 4 passes — pass 1 is the card above
    passnameusername
    1alice
    2abab
    3(empty)(empty)
    4user@123user@123
  16. def validate_username(username):

    pass 1 of 4
    54# Validate string input55def validate_username(usernamealice):56    if not username:57        raise ValueError("Username cannot be empty")58    if len(username) < 3:59        raise ValueError("Username must be at least 3 characters")60    if not username.isalnum():61        raise ValueError("Username must be alphanumeric")62    return usernamealice
    All 4 passes — pass 1 is the card above
    passusername
    1alice
    2ab
    3(empty)
    4user@123
  17. valid ← alice

    70try:71    valid→ alice = validate_username(namealice)72    print(f"  '{namealice}': OK")73except ValueError as e:
    output  'alice': OK
  18. if len(username) < 3:

    57    raise ValueError("Username cannot be empty")58if len(usernameab) < 3:59    raise ValueError("Username must be at least 3 characters")60if not username.isalnum():
  19. except ValueError as e:

    pass 1 of 3
    72    print(f"  '{name}': OK")73except ValueError as e:74    print(f"  '{nameab}': {eUsername must be at least 3 characters}")
    output  'ab': Username must be at least 3 characters
      'ab': Username must be at least 3 characters
    All 3 passes — pass 1 is the card above
    passnameeusername
    1abUsername must be at least 3 characters(empty)
    2(empty)Username cannot be emptyuser@123
    3user@123Username must be alphanumeric
  20. if not username:

    55def validate_username(username):56    if not username(empty):57        raise ValueError("Username cannot be empty")58    if len(username) < 3:
  21. if not username.isalnum():

    59    raise ValueError("Username must be at least 3 characters")60if not usernameuser@123.isalnum():61    raise ValueError("Username must be alphanumeric")62return username
  22. main()

    76if __name__ == "__main__":77    main()
  1. def main(): # Raise on invalid input

    3def main():4    # Raise on invalid input5    def check_age(age):6        if age < 0:7            raise ValueError("Age cannot be negative")8        if age > 150:9            raise ValueError("Age is unrealistic")10        return age11    12    print("Age validation:")
    outputAge validation:
  2. def check_age(age):

    pass 1 of 2
    4# Raise on invalid input5def check_age(age25):6    if age < 0:7        raise ValueError("Age cannot be negative")8    if age > 150:9        raise ValueError("Age is unrealistic")10    return age25
  3. result ← 25

    15try:16    result→ 25 = check_age(25)17    print(f"  Age {result25}: Valid")18except ValueError as e:
    output  Age 25: Valid
  4. def check_age(age):

    pass 2 of 2
    4# Raise on invalid input5def check_age(age-5):6    if age < 0:7        raise ValueError("Age cannot be negative")
  5. if age < 0:

    5def check_age(age):6    if age-5 < 0:7        raise ValueError("Age cannot be negative")8    if age > 150:
  6. except ValueError as e:

    24    print(f"  Age {result}: Valid")25except ValueError as e:26    print(f"  Error: {eAge cannot be negative}")27
    output  Error: Age cannot be negative
      Error: Age cannot be negative
  7. print(" Division:")

    35print("\nDivision:")
    output
    Division:
  8. def safe_divide(a, b):

    pass 1 of 2
    29# Raise on division by zero30def safe_divide(a10, b2):31    if b == 0:32        raise ZeroDivisionError("Cannot divide by zero")33    return a10 / b2
  9. result ← 5.0

    37try:38    result→ 5.0 = safe_divide(10, 2)39    print(f"  10 / 2 = {result5.0}")40except ZeroDivisionError as e:
    output  10 / 2 = 5.0
  10. def safe_divide(a, b):

    pass 2 of 2
    29# Raise on division by zero30def safe_divide(a10, b0):31    if b == 0:32        raise ZeroDivisionError("Cannot divide by zero")
  11. if b == 0:

    30def safe_divide(a, b):31    if b0 == 0:32        raise ZeroDivisionError("Cannot divide by zero")33    return a / b
  12. except ZeroDivisionError as e:

    45    print(f"  10 / 0 = {result}")46except ZeroDivisionError as e:47    print(f"  Error: {eCannot divide by zero}")4849# Validate string input
    output  Error: Cannot divide by zero
      Error: Cannot divide by zero
  13. usernames ← ['bob', '']

    59print("\nUsername validation:")6061usernames→ ['bob', ''] = ["bob", ""]
    output
    Username validation:
  14. for name in usernames:

    pass 1 of 2
    63for namebob in usernames['bob', '']:64    try:65        valid = validate_username(name)
  15. try:

    pass 1 of 2
    63for name in usernames:64    try:65        valid = validate_username(namebob)66        print(f"  '{name}': OK")
  16. def validate_username(username):

    pass 1 of 2
    49# Validate string input50def validate_username(usernamebob):51    if not username:52        raise ValueError("Username cannot be empty")53    if len(username) < 3:54        raise ValueError("Username must be at least 3 characters")55    if not username.isalnum():56        raise ValueError("Username must be alphanumeric")57    return usernamebob
  17. valid ← bob

    64try:65    valid→ bob = validate_username(namebob)66    print(f"  '{namebob}': OK")67except ValueError as e:
    output  'bob': OK
  18. for name in usernames:

    pass 2 of 2
    63for name(empty) in usernames['bob', '']:64    try:65        valid = validate_username(name)
  19. try:

    pass 2 of 2
    63for name in usernames:64    try:65        valid = validate_username(name(empty))66        print(f"  '{name}': OK")
  20. def validate_username(username):

    pass 2 of 2
    49# Validate string input50def validate_username(username(empty)):51    if not username:52        raise ValueError("Username cannot be empty")
  21. if not username:

    50def validate_username(username):51    if not username(empty):52        raise ValueError("Username cannot be empty")53    if len(username) < 3:
  22. except ValueError as e:

    66    print(f"  '{name}': OK")67except ValueError as e:68    print(f"  '{name(empty)}': {eUsername cannot be empty}")
    output  '': Username cannot be empty
      '': Username cannot be empty
  23. main()

    70if __name__ == "__main__":71    main()
  1. def main(): # Raise on invalid input

    3def main():4    # Raise on invalid input5    def check_age(age):6        if age < 0:7            raise ValueError("Age cannot be negative")8        if age > 150:9            raise ValueError("Age is unrealistic")10        return age11    12    print("Age validation:")
    outputAge validation:
  2. def check_age(age):

    pass 1 of 2
    4# Raise on invalid input5def check_age(age25):6    if age < 0:7        raise ValueError("Age cannot be negative")8    if age > 150:9        raise ValueError("Age is unrealistic")10    return age25
  3. result ← 25

    15try:16    result→ 25 = check_age(25)17    print(f"  Age {result25}: Valid")18except ValueError as e:
    output  Age 25: Valid
  4. def check_age(age):

    pass 2 of 2
    4# Raise on invalid input5def check_age(age-5):6    if age < 0:7        raise ValueError("Age cannot be negative")
  5. if age < 0:

    5def check_age(age):6    if age-5 < 0:7        raise ValueError("Age cannot be negative")8    if age > 150:
  6. except ValueError as e:

    24    print(f"  Age {result}: Valid")25except ValueError as e:26    print(f"  Error: {eAge cannot be negative}")27
    output  Error: Age cannot be negative
      Error: Age cannot be negative
  7. print(" Division:")

    35print("\nDivision:")
    output
    Division:
  8. def safe_divide(a, b):

    pass 1 of 2
    29# Raise on division by zero30def safe_divide(a10, b2):31    if b == 0:32        raise ZeroDivisionError("Cannot divide by zero")33    return a10 / b2
  9. result ← 5.0

    37try:38    result→ 5.0 = safe_divide(10, 2)39    print(f"  10 / 2 = {result5.0}")40except ZeroDivisionError as e:
    output  10 / 2 = 5.0
  10. def safe_divide(a, b):

    pass 2 of 2
    29# Raise on division by zero30def safe_divide(a10, b0):31    if b == 0:32        raise ZeroDivisionError("Cannot divide by zero")
  11. if b == 0:

    30def safe_divide(a, b):31    if b0 == 0:32        raise ZeroDivisionError("Cannot divide by zero")33    return a / b
  12. except ZeroDivisionError as e:

    45    print(f"  10 / 0 = {result}")46except ZeroDivisionError as e:47    print(f"  Error: {eCannot divide by zero}")4849# Validate string input
    output  Error: Cannot divide by zero
      Error: Cannot divide by zero
  13. usernames ← ['user1', 'a', 'test!', 'valid123']

    59print("\nUsername validation:")6061usernames→ ['user1', 'a', 'test!', 'valid123'] = ["user1", "a", "test!", "valid123"]
    output
    Username validation:
  14. for name in usernames:

    pass 1 of 4
    63for nameuser1 in usernames['user1', 'a', 'test!', 'valid123']:64    try:65        valid = validate_username(name)
    All 4 passes — pass 1 is the card above
    passnameusernamee
    1user1
    2aaUsername must be at least 3 characters
    3test!test!Username must be alphanumeric
    4valid123
  15. try:

    pass 1 of 4
    63for name in usernames:64    try:65        valid = validate_username(nameuser1)66        print(f"  '{name}': OK")
    All 4 passes — pass 1 is the card above
    passnameusernamee
    1user1
    2aaUsername must be at least 3 characters
    3test!test!Username must be alphanumeric
    4valid123
  16. def validate_username(username):

    pass 1 of 4
    49# Validate string input50def validate_username(usernameuser1):51    if not username:52        raise ValueError("Username cannot be empty")53    if len(username) < 3:54        raise ValueError("Username must be at least 3 characters")55    if not username.isalnum():56        raise ValueError("Username must be alphanumeric")57    return usernameuser1
    All 4 passes — pass 1 is the card above
    passusernamenamee
    1user1
    2aaUsername must be at least 3 characters
    3test!test!Username must be alphanumeric
    4valid123
  17. valid ← user1

    64try:65    valid→ user1 = validate_username(nameuser1)66    print(f"  '{nameuser1}': OK")67except ValueError as e:
    output  'user1': OK
  18. if len(username) < 3:

    52    raise ValueError("Username cannot be empty")53if len(usernamea) < 3:54    raise ValueError("Username must be at least 3 characters")55if not username.isalnum():
  19. except ValueError as e:

    pass 1 of 2
    66    print(f"  '{name}': OK")67except ValueError as e:68    print(f"  '{namea}': {eUsername must be at least 3 characters}")
    output  'a': Username must be at least 3 characters
      'a': Username must be at least 3 characters
  20. if not username.isalnum():

    54    raise ValueError("Username must be at least 3 characters")55if not usernametest!.isalnum():56    raise ValueError("Username must be alphanumeric")57return username
  21. except ValueError as e:

    pass 2 of 2
    66    print(f"  '{name}': OK")67except ValueError as e:68    print(f"  '{nametest!}': {eUsername must be alphanumeric}")
    output  'test!': Username must be alphanumeric
      'test!': Username must be alphanumeric
  22. valid ← valid123

    64try:65    valid→ valid123 = validate_username(namevalid123)66    print(f"  '{namevalid123}': OK")67except ValueError as e:
    output  'valid123': OK
  23. main()

    70if __name__ == "__main__":71    main()

raise ValueError("message") stops execution and signals error to caller.

raise Trigger an exception: `raise ValueError("Invalid")`. Stops normal flow.

Exception propagation

Exceptions bubble up until caught.

test_values
propagation.py
Replay: real traced execution (multi-file project)
# Exception propagation

def main():
    # Exception propagates through call stack
    def level3():
        print("    level3: About to raise")
        raise RuntimeError("Error in level3")

    def level2():
        print("  level2: Calling level3")
        level3()
        print("  level2: This won't print")

    def level1():
        print("level1: Calling level2")
        level2()
        print("level1: This won't print")

    print("Propagation demo:\n")

    try:
        level1()
    except RuntimeError as e:
        print(f"\nCaught in main: {e}")


    # Function that lets exception propagate
    def read_config(key):
        config = {"host": "localhost", "port": 8080}
        # KeyError propagates if key missing
        return config[key]

    def get_server_url():
        host = read_config("host")
        port = read_config("port")
        return f"http://{host}:{port}"

    print("\nConfig reading:")

    try:
        url = get_server_url()
        print(f"  URL: {url}")
    except KeyError as e:
        print(f"  Missing config key: {e}")

    try:
        timeout = read_config("timeout")  # Missing key
        print(f"  Timeout: {timeout}")
    except KeyError as e:
        print(f"  Missing config key: {e}")

    # Catch at different levels
    def process_data(data):
        if not data:
            raise ValueError("Data is empty")
        return data.upper()

    def transform(data):
        # Let exception propagate
        return process_data(data)

    def handle_request(data):
        try:
            # Catch here
            result = transform(data)
            return f"Success: {result}"
        except ValueError as e:
            return f"Error: {e}"

    print("\nRequest handling:")
    print(f"  {handle_request('hello')}")
    print(f"  {handle_request('')}")

    # Chain of operations
    def step1(value):
        if value < 0:
            raise ValueError("Step1: Value must be positive")
        return value * 2

    def step2(value):
        if value > 100:
            raise ValueError("Step2: Value too large")
        return value + 10

    def step3(value):
        if value % 2 != 0:
            raise ValueError("Step3: Value must be even")
        return value / 2

    def pipeline(value):
        v1 = step1(value)
        v2 = step2(v1)
        v3 = step3(v2)
        return v3

    print("\nPipeline processing:")

    test_values = [10, -5, 50]

    for val in test_values:
        try:
            result = pipeline(val)
            print(f"  {val} → {result}")
        except ValueError as e:
            print(f"  {val} → Failed: {e}")

if __name__ == "__main__":
    main()
# Exception propagation

def main():
    # Exception propagates through call stack
    def level3():
        print("    level3: About to raise")
        raise RuntimeError("Error in level3")

    def level2():
        print("  level2: Calling level3")
        level3()
        print("  level2: This won't print")

    def level1():
        print("level1: Calling level2")
        level2()
        print("level1: This won't print")

    print("Propagation demo:\n")

    try:
        level1()
    except RuntimeError as e:
        print(f"\nCaught in main: {e}")


    # Function that lets exception propagate
    def read_config(key):
        config = {"host": "localhost", "port": 8080}
        # KeyError propagates if key missing
        return config[key]

    def get_server_url():
        host = read_config("host")
        port = read_config("port")
        return f"http://{host}:{port}"

    print("\nConfig reading:")

    try:
        url = get_server_url()
        print(f"  URL: {url}")
    except KeyError as e:
        print(f"  Missing config key: {e}")

    try:
        timeout = read_config("timeout")  # Missing key
        print(f"  Timeout: {timeout}")
    except KeyError as e:
        print(f"  Missing config key: {e}")

    # Catch at different levels
    def process_data(data):
        if not data:
            raise ValueError("Data is empty")
        return data.upper()

    def transform(data):
        # Let exception propagate
        return process_data(data)

    def handle_request(data):
        try:
            # Catch here
            result = transform(data)
            return f"Success: {result}"
        except ValueError as e:
            return f"Error: {e}"

    print("\nRequest handling:")
    print(f"  {handle_request('hello')}")
    print(f"  {handle_request('')}")

    # Chain of operations
    def step1(value):
        if value < 0:
            raise ValueError("Step1: Value must be positive")
        return value * 2

    def step2(value):
        if value > 100:
            raise ValueError("Step2: Value too large")
        return value + 10

    def step3(value):
        if value % 2 != 0:
            raise ValueError("Step3: Value must be even")
        return value / 2

    def pipeline(value):
        v1 = step1(value)
        v2 = step2(v1)
        v3 = step3(v2)
        return v3

    print("\nPipeline processing:")

    test_values = [5, 20]

    for val in test_values:
        try:
            result = pipeline(val)
            print(f"  {val} → {result}")
        except ValueError as e:
            print(f"  {val} → Failed: {e}")

if __name__ == "__main__":
    main()
# Exception propagation

def main():
    # Exception propagates through call stack
    def level3():
        print("    level3: About to raise")
        raise RuntimeError("Error in level3")

    def level2():
        print("  level2: Calling level3")
        level3()
        print("  level2: This won't print")

    def level1():
        print("level1: Calling level2")
        level2()
        print("level1: This won't print")

    print("Propagation demo:\n")

    try:
        level1()
    except RuntimeError as e:
        print(f"\nCaught in main: {e}")


    # Function that lets exception propagate
    def read_config(key):
        config = {"host": "localhost", "port": 8080}
        # KeyError propagates if key missing
        return config[key]

    def get_server_url():
        host = read_config("host")
        port = read_config("port")
        return f"http://{host}:{port}"

    print("\nConfig reading:")

    try:
        url = get_server_url()
        print(f"  URL: {url}")
    except KeyError as e:
        print(f"  Missing config key: {e}")

    try:
        timeout = read_config("timeout")  # Missing key
        print(f"  Timeout: {timeout}")
    except KeyError as e:
        print(f"  Missing config key: {e}")

    # Catch at different levels
    def process_data(data):
        if not data:
            raise ValueError("Data is empty")
        return data.upper()

    def transform(data):
        # Let exception propagate
        return process_data(data)

    def handle_request(data):
        try:
            # Catch here
            result = transform(data)
            return f"Success: {result}"
        except ValueError as e:
            return f"Error: {e}"

    print("\nRequest handling:")
    print(f"  {handle_request('hello')}")
    print(f"  {handle_request('')}")

    # Chain of operations
    def step1(value):
        if value < 0:
            raise ValueError("Step1: Value must be positive")
        return value * 2

    def step2(value):
        if value > 100:
            raise ValueError("Step2: Value too large")
        return value + 10

    def step3(value):
        if value % 2 != 0:
            raise ValueError("Step3: Value must be even")
        return value / 2

    def pipeline(value):
        v1 = step1(value)
        v2 = step2(v1)
        v3 = step3(v2)
        return v3

    print("\nPipeline processing:")

    test_values = [-10, 60, 15]

    for val in test_values:
        try:
            result = pipeline(val)
            print(f"  {val} → {result}")
        except ValueError as e:
            print(f"  {val} → Failed: {e}")

if __name__ == "__main__":
    main()
  1. def main(): # Exception propagates through call stack

    3def main():4    # Exception propagates through call stack5    def level3():6        print("    level3: About to raise")7        raise RuntimeError("Error in level3")8    9    def level2():10        print("  level2: Calling level3")11        level3()12        print("  level2: This won't print")13    14    def level1():15        print("level1: Calling level2")16        level2()17        print("level1: This won't print")18    19    print("Propagation demo:\n")
    outputPropagation demo:
  2. def level1():

    14def level1():15    print("level1: Calling level2")16    level2()17    print("level1: This won't print")
    outputlevel1: Calling level2
  3. def level2():

    9def level2():10    print("  level2: Calling level3")11    level3()12    print("  level2: This won't print")
    output  level2: Calling level3
  4. def level3():

    4# Exception propagates through call stack5def level3():6    print("    level3: About to raise")7    raise RuntimeError("Error in level3")
    output    level3: About to raise
  5. except RuntimeError as e:

    22    level1()23except RuntimeError as e:24    print(f"\nCaught in main: {eError in level3}")2526#@help h1
    output
    Caught in main: Error in level3
    
    Caught in main: Error in level3
  6. print(" Config reading:")

    43print("\nConfig reading:")
    output
    Config reading:
  7. config ← {'host': 'localhost', 'port': 8080}

    pass 1 of 3
    32# Function that lets exception propagate33def read_config(keyhost):34    config→ {'host': 'localhost', 'port': 8080} = {"host": "localhost", "port": 8080}35    # KeyError propagates if key missing36    return config[key]localhost
    All 3 passes — pass 1 is the card above
    passkeyconfig[key]econfig
    1hostlocalhost{'host': 'localhost', 'port': 8080}
    2port8080{'host': 'localhost', 'port': 8080}
    3timeout(empty)'timeout'{'host': 'localhost', 'port': 8080}
  8. host ← localhost

    38def get_server_url():39    host→ localhost = read_config("host")40    port = read_config("port")41    return f"http://{host}:{port}"
  9. port ← 8080

    39host = read_config("host")40port→ 8080 = read_config("port")41return f"http://{hostlocalhost}:{port8080}"
  10. url ← http://localhost:8080

    45try:46    url→ http://localhost:8080 = get_server_url()47    print(f"  URL: {urlhttp://localhost:8080}")48except KeyError as e:
    output  URL: http://localhost:8080
  11. except KeyError as e:

    53    print(f"  Timeout: {timeout}")54except KeyError as e:55    print(f"  Missing config key: {e'timeout'}")5657# Catch at different levels
    output  Missing config key: 'timeout'
      Missing config key: 'timeout'
  12. print(" Request handling:")

    75print("\nRequest handling:")76print(f"  {handle_request('hello')}")77print(f"  {handle_request('')}")
    output
    Request handling:
  13. def handle_request(data):

    pass 1 of 2
    67def handle_request(datahello):68    try:69        # Catch here
  14. try: # Catch here

    pass 1 of 2
    67def handle_request(data):68    try:69        # Catch here70        result = transform(datahello)71        return f"Success: {result}"
  15. def transform(data): # Let exception propagate

    pass 1 of 2
    63def transform(datahello):64    # Let exception propagate65    return process_data(datahello)
  16. def process_data(data):

    pass 1 of 2
    57# Catch at different levels58def process_data(datahello):59    if not data:60        raise ValueError("Data is empty")61    return datahello.upper()
  17. result ← HELLO

    69    # Catch here70    result→ HELLO = transform(datahello)71    return f"Success: {resultHELLO}"72except ValueError as e:
  18. print(f" {handle_request('hello')}")

    75print("\nRequest handling:")76print(f"  {handle_request('hello')}")77print(f"  {handle_request('')}")
    output  Success: HELLO
  19. def handle_request(data):

    pass 2 of 2
    67def handle_request(data(empty)):68    try:69        # Catch here
  20. try: # Catch here

    pass 2 of 2
    67def handle_request(data):68    try:69        # Catch here70        result = transform(data(empty))71        return f"Success: {result}"
  21. def transform(data): # Let exception propagate

    pass 2 of 2
    63def transform(data(empty)):64    # Let exception propagate65    return process_data(data(empty))
  22. def process_data(data):

    pass 2 of 2
    57# Catch at different levels58def process_data(data(empty)):59    if not data:60        raise ValueError("Data is empty")
  23. if not data:

    58def process_data(data):59    if not data(empty):60        raise ValueError("Data is empty")61    return data.upper()
  24. except ValueError as e:

    71    return f"Success: {result}"72except ValueError as e:73    return f"Error: {eData is empty}"
  25. test_values ← [10, -5, 50]

    76print(f"  {handle_request('hello')}")77print(f"  {handle_request('')}")7879# Chain of operations80def step1(value):81    if value < 0:82        raise ValueError("Step1: Value must be positive")83    return value * 28485def step2(value):86    if value > 100:87        raise ValueError("Step2: Value too large")88    return value + 108990def step3(value):91    if value % 2 != 0:92        raise ValueError("Step3: Value must be even")93    return value / 29495def pipeline(value):96    v1 = step1(value)97    v2 = step2(v1)98    v3 = step3(v2)99    return v3100101print("\nPipeline processing:")102103test_values→ [10, -5, 50] = [10, -5, 50]104#@test_values=[5, 20], [-10, 60, 15]
    output  Error: Data is empty
    
    Pipeline processing:
  26. for val in test_values:

    pass 1 of 3
    106for val10 in test_values[10, -5, 50]:107    try:108        result = pipeline(val)
    All 3 passes — pass 1 is the card above
    passvalvaluee
    110
    2-5-5Step1: Value must be positive
    350
  27. try:

    pass 1 of 3
    106for val in test_values:107    try:108        result = pipeline(val10)109        print(f"  {val} → {result}")
    All 3 passes — pass 1 is the card above
    passvalvaluee
    110
    2-5-5Step1: Value must be positive
    350
  28. def pipeline(value):

    pass 1 of 3
    95def pipeline(value10):96    v1 = step1(value10)97    v2 = step2(v1)
    All 3 passes — pass 1 is the card above
    passvaluevale
    110
    2-5-5Step1: Value must be positive
    350
  29. def step1(value):

    pass 1 of 3
    79# Chain of operations80def step1(value10):81    if value < 0:82        raise ValueError("Step1: Value must be positive")83    return value10 * 2
    All 3 passes — pass 1 is the card above
    passvaluevale
    110
    2-5-5Step1: Value must be positive
    350
  30. v1 ← 20

    95def pipeline(value):96    v1→ 20 = step1(value10)97    v2 = step2(v120)98    v3 = step3(v2)
  31. def step2(value):

    pass 1 of 2
    85def step2(value20):86    if value > 100:87        raise ValueError("Step2: Value too large")88    return value20 + 10
  32. v2 ← 30

    96v1 = step1(value)97v2→ 30 = step2(v120)98v3 = step3(v230)99return v3
  33. def step3(value):

    pass 1 of 2
    90def step3(value30):91    if value % 2 != 0:92        raise ValueError("Step3: Value must be even")93    return value30 / 2
  34. v3 ← 15.0

    97v2 = step2(v1)98v3→ 15.0 = step3(v230)99return v315.0
  35. result ← 15.0

    107try:108    result→ 15.0 = pipeline(val10)109    print(f"  {val10} → {result15.0}")110except ValueError as e:
    output  10 → 15.0
  36. if value < 0:

    80def step1(value):81    if value-5 < 0:82        raise ValueError("Step1: Value must be positive")83    return value * 2
  37. except ValueError as e:

    109    print(f"  {val} → {result}")110except ValueError as e:111    print(f"  {val-5} → Failed: {eStep1: Value must be positive}")
    output  -5 → Failed: Step1: Value must be positive
      -5 → Failed: Step1: Value must be positive
  38. v1 ← 100

    95def pipeline(value):96    v1→ 100 = step1(value50)97    v2 = step2(v1100)98    v3 = step3(v2)
  39. def step2(value):

    pass 2 of 2
    85def step2(value100):86    if value > 100:87        raise ValueError("Step2: Value too large")88    return value100 + 10
  40. v2 ← 110

    96v1 = step1(value)97v2→ 110 = step2(v1100)98v3 = step3(v2110)99return v3
  41. def step3(value):

    pass 2 of 2
    90def step3(value110):91    if value % 2 != 0:92        raise ValueError("Step3: Value must be even")93    return value110 / 2
  42. v3 ← 55.0

    97v2 = step2(v1)98v3→ 55.0 = step3(v2110)99return v355.0
  43. result ← 55.0

    107try:108    result→ 55.0 = pipeline(val50)109    print(f"  {val50} → {result55.0}")110except ValueError as e:
    output  50 → 55.0
  44. main()

    113if __name__ == "__main__":114    main()
  1. def main(): # Exception propagates through call stack

    3def main():4    # Exception propagates through call stack5    def level3():6        print("    level3: About to raise")7        raise RuntimeError("Error in level3")8    9    def level2():10        print("  level2: Calling level3")11        level3()12        print("  level2: This won't print")13    14    def level1():15        print("level1: Calling level2")16        level2()17        print("level1: This won't print")18    19    print("Propagation demo:\n")
    outputPropagation demo:
  2. def level1():

    14def level1():15    print("level1: Calling level2")16    level2()17    print("level1: This won't print")
    outputlevel1: Calling level2
  3. def level2():

    9def level2():10    print("  level2: Calling level3")11    level3()12    print("  level2: This won't print")
    output  level2: Calling level3
  4. def level3():

    4# Exception propagates through call stack5def level3():6    print("    level3: About to raise")7    raise RuntimeError("Error in level3")
    output    level3: About to raise
  5. except RuntimeError as e:

    22    level1()23except RuntimeError as e:24    print(f"\nCaught in main: {eError in level3}")25
    output
    Caught in main: Error in level3
    
    Caught in main: Error in level3
  6. print(" Config reading:")

    38print("\nConfig reading:")
    output
    Config reading:
  7. config ← {'host': 'localhost', 'port': 8080}

    pass 1 of 3
    27# Function that lets exception propagate28def read_config(keyhost):29    config→ {'host': 'localhost', 'port': 8080} = {"host": "localhost", "port": 8080}30    # KeyError propagates if key missing31    return config[key]localhost
    All 3 passes — pass 1 is the card above
    passkeyconfig[key]econfig
    1hostlocalhost{'host': 'localhost', 'port': 8080}
    2port8080{'host': 'localhost', 'port': 8080}
    3timeout(empty)'timeout'{'host': 'localhost', 'port': 8080}
  8. host ← localhost

    33def get_server_url():34    host→ localhost = read_config("host")35    port = read_config("port")36    return f"http://{host}:{port}"
  9. port ← 8080

    34host = read_config("host")35port→ 8080 = read_config("port")36return f"http://{hostlocalhost}:{port8080}"
  10. url ← http://localhost:8080

    40try:41    url→ http://localhost:8080 = get_server_url()42    print(f"  URL: {urlhttp://localhost:8080}")43except KeyError as e:
    output  URL: http://localhost:8080
  11. except KeyError as e:

    48    print(f"  Timeout: {timeout}")49except KeyError as e:50    print(f"  Missing config key: {e'timeout'}")5152# Catch at different levels
    output  Missing config key: 'timeout'
      Missing config key: 'timeout'
  12. print(" Request handling:")

    70print("\nRequest handling:")71print(f"  {handle_request('hello')}")72print(f"  {handle_request('')}")
    output
    Request handling:
  13. def handle_request(data):

    pass 1 of 2
    62def handle_request(datahello):63    try:64        # Catch here
  14. try: # Catch here

    pass 1 of 2
    62def handle_request(data):63    try:64        # Catch here65        result = transform(datahello)66        return f"Success: {result}"
  15. def transform(data): # Let exception propagate

    pass 1 of 2
    58def transform(datahello):59    # Let exception propagate60    return process_data(datahello)
  16. def process_data(data):

    pass 1 of 2
    52# Catch at different levels53def process_data(datahello):54    if not data:55        raise ValueError("Data is empty")56    return datahello.upper()
  17. result ← HELLO

    64    # Catch here65    result→ HELLO = transform(datahello)66    return f"Success: {resultHELLO}"67except ValueError as e:
  18. print(f" {handle_request('hello')}")

    70print("\nRequest handling:")71print(f"  {handle_request('hello')}")72print(f"  {handle_request('')}")
    output  Success: HELLO
  19. def handle_request(data):

    pass 2 of 2
    62def handle_request(data(empty)):63    try:64        # Catch here
  20. try: # Catch here

    pass 2 of 2
    62def handle_request(data):63    try:64        # Catch here65        result = transform(data(empty))66        return f"Success: {result}"
  21. def transform(data): # Let exception propagate

    pass 2 of 2
    58def transform(data(empty)):59    # Let exception propagate60    return process_data(data(empty))
  22. def process_data(data):

    pass 2 of 2
    52# Catch at different levels53def process_data(data(empty)):54    if not data:55        raise ValueError("Data is empty")
  23. if not data:

    53def process_data(data):54    if not data(empty):55        raise ValueError("Data is empty")56    return data.upper()
  24. except ValueError as e:

    66    return f"Success: {result}"67except ValueError as e:68    return f"Error: {eData is empty}"
  25. test_values ← [5, 20]

    71print(f"  {handle_request('hello')}")72print(f"  {handle_request('')}")7374# Chain of operations75def step1(value):76    if value < 0:77        raise ValueError("Step1: Value must be positive")78    return value * 27980def step2(value):81    if value > 100:82        raise ValueError("Step2: Value too large")83    return value + 108485def step3(value):86    if value % 2 != 0:87        raise ValueError("Step3: Value must be even")88    return value / 28990def pipeline(value):91    v1 = step1(value)92    v2 = step2(v1)93    v3 = step3(v2)94    return v39596print("\nPipeline processing:")9798test_values→ [5, 20] = [5, 20]
    output  Error: Data is empty
    
    Pipeline processing:
  26. for val in test_values:

    pass 1 of 2
    100for val5 in test_values[5, 20]:101    try:102        result = pipeline(val)
  27. try:

    pass 1 of 2
    100for val in test_values:101    try:102        result = pipeline(val5)103        print(f"  {val} → {result}")
  28. def pipeline(value):

    pass 1 of 2
    90def pipeline(value5):91    v1 = step1(value5)92    v2 = step2(v1)
  29. def step1(value):

    pass 1 of 2
    74# Chain of operations75def step1(value5):76    if value < 0:77        raise ValueError("Step1: Value must be positive")78    return value5 * 2
  30. v1 ← 10

    90def pipeline(value):91    v1→ 10 = step1(value5)92    v2 = step2(v110)93    v3 = step3(v2)
  31. def step2(value):

    pass 1 of 2
    80def step2(value10):81    if value > 100:82        raise ValueError("Step2: Value too large")83    return value10 + 10
  32. v2 ← 20

    91v1 = step1(value)92v2→ 20 = step2(v110)93v3 = step3(v220)94return v3
  33. def step3(value):

    pass 1 of 2
    85def step3(value20):86    if value % 2 != 0:87        raise ValueError("Step3: Value must be even")88    return value20 / 2
  34. v3 ← 10.0

    92v2 = step2(v1)93v3→ 10.0 = step3(v220)94return v310.0
  35. result ← 10.0

    101try:102    result→ 10.0 = pipeline(val5)103    print(f"  {val5} → {result10.0}")104except ValueError as e:
    output  5 → 10.0
  36. for val in test_values:

    pass 2 of 2
    100for val20 in test_values[5, 20]:101    try:102        result = pipeline(val)
  37. try:

    pass 2 of 2
    100for val in test_values:101    try:102        result = pipeline(val20)103        print(f"  {val} → {result}")
  38. def pipeline(value):

    pass 2 of 2
    90def pipeline(value20):91    v1 = step1(value20)92    v2 = step2(v1)
  39. def step1(value):

    pass 2 of 2
    74# Chain of operations75def step1(value20):76    if value < 0:77        raise ValueError("Step1: Value must be positive")78    return value20 * 2
  40. v1 ← 40

    90def pipeline(value):91    v1→ 40 = step1(value20)92    v2 = step2(v140)93    v3 = step3(v2)
  41. def step2(value):

    pass 2 of 2
    80def step2(value40):81    if value > 100:82        raise ValueError("Step2: Value too large")83    return value40 + 10
  42. v2 ← 50

    91v1 = step1(value)92v2→ 50 = step2(v140)93v3 = step3(v250)94return v3
  43. def step3(value):

    pass 2 of 2
    85def step3(value50):86    if value % 2 != 0:87        raise ValueError("Step3: Value must be even")88    return value50 / 2
  44. v3 ← 25.0

    92v2 = step2(v1)93v3→ 25.0 = step3(v250)94return v325.0
  45. result ← 25.0

    101try:102    result→ 25.0 = pipeline(val20)103    print(f"  {val20} → {result25.0}")104except ValueError as e:
    output  20 → 25.0
  46. main()

    107if __name__ == "__main__":108    main()
  1. def main(): # Exception propagates through call stack

    3def main():4    # Exception propagates through call stack5    def level3():6        print("    level3: About to raise")7        raise RuntimeError("Error in level3")8    9    def level2():10        print("  level2: Calling level3")11        level3()12        print("  level2: This won't print")13    14    def level1():15        print("level1: Calling level2")16        level2()17        print("level1: This won't print")18    19    print("Propagation demo:\n")
    outputPropagation demo:
  2. def level1():

    14def level1():15    print("level1: Calling level2")16    level2()17    print("level1: This won't print")
    outputlevel1: Calling level2
  3. def level2():

    9def level2():10    print("  level2: Calling level3")11    level3()12    print("  level2: This won't print")
    output  level2: Calling level3
  4. def level3():

    4# Exception propagates through call stack5def level3():6    print("    level3: About to raise")7    raise RuntimeError("Error in level3")
    output    level3: About to raise
  5. except RuntimeError as e:

    22    level1()23except RuntimeError as e:24    print(f"\nCaught in main: {eError in level3}")25
    output
    Caught in main: Error in level3
    
    Caught in main: Error in level3
  6. print(" Config reading:")

    38print("\nConfig reading:")
    output
    Config reading:
  7. config ← {'host': 'localhost', 'port': 8080}

    pass 1 of 3
    27# Function that lets exception propagate28def read_config(keyhost):29    config→ {'host': 'localhost', 'port': 8080} = {"host": "localhost", "port": 8080}30    # KeyError propagates if key missing31    return config[key]localhost
    All 3 passes — pass 1 is the card above
    passkeyconfig[key]econfig
    1hostlocalhost{'host': 'localhost', 'port': 8080}
    2port8080{'host': 'localhost', 'port': 8080}
    3timeout(empty)'timeout'{'host': 'localhost', 'port': 8080}
  8. host ← localhost

    33def get_server_url():34    host→ localhost = read_config("host")35    port = read_config("port")36    return f"http://{host}:{port}"
  9. port ← 8080

    34host = read_config("host")35port→ 8080 = read_config("port")36return f"http://{hostlocalhost}:{port8080}"
  10. url ← http://localhost:8080

    40try:41    url→ http://localhost:8080 = get_server_url()42    print(f"  URL: {urlhttp://localhost:8080}")43except KeyError as e:
    output  URL: http://localhost:8080
  11. except KeyError as e:

    48    print(f"  Timeout: {timeout}")49except KeyError as e:50    print(f"  Missing config key: {e'timeout'}")5152# Catch at different levels
    output  Missing config key: 'timeout'
      Missing config key: 'timeout'
  12. print(" Request handling:")

    70print("\nRequest handling:")71print(f"  {handle_request('hello')}")72print(f"  {handle_request('')}")
    output
    Request handling:
  13. def handle_request(data):

    pass 1 of 2
    62def handle_request(datahello):63    try:64        # Catch here
  14. try: # Catch here

    pass 1 of 2
    62def handle_request(data):63    try:64        # Catch here65        result = transform(datahello)66        return f"Success: {result}"
  15. def transform(data): # Let exception propagate

    pass 1 of 2
    58def transform(datahello):59    # Let exception propagate60    return process_data(datahello)
  16. def process_data(data):

    pass 1 of 2
    52# Catch at different levels53def process_data(datahello):54    if not data:55        raise ValueError("Data is empty")56    return datahello.upper()
  17. result ← HELLO

    64    # Catch here65    result→ HELLO = transform(datahello)66    return f"Success: {resultHELLO}"67except ValueError as e:
  18. print(f" {handle_request('hello')}")

    70print("\nRequest handling:")71print(f"  {handle_request('hello')}")72print(f"  {handle_request('')}")
    output  Success: HELLO
  19. def handle_request(data):

    pass 2 of 2
    62def handle_request(data(empty)):63    try:64        # Catch here
  20. try: # Catch here

    pass 2 of 2
    62def handle_request(data):63    try:64        # Catch here65        result = transform(data(empty))66        return f"Success: {result}"
  21. def transform(data): # Let exception propagate

    pass 2 of 2
    58def transform(data(empty)):59    # Let exception propagate60    return process_data(data(empty))
  22. def process_data(data):

    pass 2 of 2
    52# Catch at different levels53def process_data(data(empty)):54    if not data:55        raise ValueError("Data is empty")
  23. if not data:

    53def process_data(data):54    if not data(empty):55        raise ValueError("Data is empty")56    return data.upper()
  24. except ValueError as e:

    66    return f"Success: {result}"67except ValueError as e:68    return f"Error: {eData is empty}"
  25. test_values ← [-10, 60, 15]

    71print(f"  {handle_request('hello')}")72print(f"  {handle_request('')}")7374# Chain of operations75def step1(value):76    if value < 0:77        raise ValueError("Step1: Value must be positive")78    return value * 27980def step2(value):81    if value > 100:82        raise ValueError("Step2: Value too large")83    return value + 108485def step3(value):86    if value % 2 != 0:87        raise ValueError("Step3: Value must be even")88    return value / 28990def pipeline(value):91    v1 = step1(value)92    v2 = step2(v1)93    v3 = step3(v2)94    return v39596print("\nPipeline processing:")9798test_values→ [-10, 60, 15] = [-10, 60, 15]
    output  Error: Data is empty
    
    Pipeline processing:
  26. for val in test_values:

    pass 1 of 3
    100for val-10 in test_values[-10, 60, 15]:101    try:102        result = pipeline(val)
    All 3 passes — pass 1 is the card above
    passvalvaluee
    1-10-10Step1: Value must be positive
    260
    315
  27. try:

    pass 1 of 3
    100for val in test_values:101    try:102        result = pipeline(val-10)103        print(f"  {val} → {result}")
    All 3 passes — pass 1 is the card above
    passvalvaluee
    1-10-10Step1: Value must be positive
    260
    315
  28. def pipeline(value):

    pass 1 of 3
    90def pipeline(value-10):91    v1 = step1(value-10)92    v2 = step2(v1)
    All 3 passes — pass 1 is the card above
    passvaluevale
    1-10-10Step1: Value must be positive
    260
    315
  29. def step1(value):

    pass 1 of 3
    74# Chain of operations75def step1(value-10):76    if value < 0:77        raise ValueError("Step1: Value must be positive")
    All 3 passes — pass 1 is the card above
    passvaluevale
    1-10-10Step1: Value must be positive
    260
    315
  30. if value < 0:

    75def step1(value):76    if value-10 < 0:77        raise ValueError("Step1: Value must be positive")78    return value * 2
  31. except ValueError as e:

    pass 1 of 2
    103    print(f"  {val} → {result}")104except ValueError as e:105    print(f"  {val-10} → Failed: {eStep1: Value must be positive}")
    output  -10 → Failed: Step1: Value must be positive
      -10 → Failed: Step1: Value must be positive
  32. v1 ← 120

    90def pipeline(value):91    v1→ 120 = step1(value60)92    v2 = step2(v1120)93    v3 = step3(v2)
  33. def step2(value):

    pass 1 of 2
    80def step2(value120):81    if value > 100:82        raise ValueError("Step2: Value too large")
  34. if value > 100:

    80def step2(value):81    if value120 > 100:82        raise ValueError("Step2: Value too large")83    return value + 10
  35. except ValueError as e:

    pass 2 of 2
    103    print(f"  {val} → {result}")104except ValueError as e:105    print(f"  {val60} → Failed: {eStep2: Value too large}")
    output  60 → Failed: Step2: Value too large
      60 → Failed: Step2: Value too large
  36. v1 ← 30

    90def pipeline(value):91    v1→ 30 = step1(value15)92    v2 = step2(v130)93    v3 = step3(v2)
  37. def step2(value):

    pass 2 of 2
    80def step2(value30):81    if value > 100:82        raise ValueError("Step2: Value too large")83    return value30 + 10
  38. v2 ← 40

    91v1 = step1(value)92v2→ 40 = step2(v130)93v3 = step3(v240)94return v3
  39. def step3(value):

    85def step3(value40):86    if value % 2 != 0:87        raise ValueError("Step3: Value must be even")88    return value40 / 2
  40. v3 ← 20.0

    92v2 = step2(v1)93v3→ 20.0 = step3(v240)94return v320.0
  41. result ← 20.0

    101try:102    result→ 20.0 = pipeline(val15)103    print(f"  {val15} → {result20.0}")104except ValueError as e:
    output  15 → 20.0
  42. main()

    107if __name__ == "__main__":108    main()

Uncaught exception travels up the call stack to the first handler.

Include error details

Provide useful information in the exception message.

balance
with_message.py
Replay: real traced execution (multi-file project)
# Raising exceptions with messages

def main():
    # Detailed error messages
    def withdraw(balance, amount):
        if amount <= 0:
            raise ValueError(f"Withdrawal amount must be positive, got {amount}")
        if amount > balance:
            raise ValueError(f"Insufficient funds: balance={balance}, requested={amount}")
        return balance - amount

    print("Bank withdrawals:\n")

    balance = 100

    # Successful withdrawal
    try:
        new_balance = withdraw(balance, 30)
        print(f"  Withdrew $30: New balance ${new_balance}")
        balance = new_balance
    except ValueError as e:
        print(f"  Error: {e}")

    # Invalid amount
    try:
        new_balance = withdraw(balance, -10)
        print(f"  Withdrew $-10: New balance ${new_balance}")
    except ValueError as e:
        print(f"  Error: {e}")

    # Insufficient funds
    try:
        new_balance = withdraw(balance, 200)
        print(f"  Withdrew $200: New balance ${new_balance}")
    except ValueError as e:
        print(f"  Error: {e}")


    # Type-specific error messages
    def calculate_discount(price, discount_percent):
        if not isinstance(price, (int, float)):
            raise TypeError(f"Price must be numeric, got {type(price).__name__}")
        if not isinstance(discount_percent, (int, float)):
            raise TypeError(f"Discount must be numeric, got {type(discount_percent).__name__}")
        if discount_percent < 0 or discount_percent > 100:
            raise ValueError(f"Discount must be 0-100%, got {discount_percent}")

        return price * (1 - discount_percent / 100)

    print("\nDiscount calculations:")

    test_cases = [
        (100, 10),
        (100, "10"),
        ("100", 10),
        (100, 150)
    ]

    for price, discount in test_cases:
        try:
            final_price = calculate_discount(price, discount)
            print(f"  ${price} - {discount}% = ${final_price:.2f}")
        except (TypeError, ValueError) as e:
            print(f"  ${price} - {discount}%: {e}")

    # Index validation with context
    def get_item(items, index):
        if not isinstance(index, int):
            raise TypeError(f"Index must be integer, got {type(index).__name__}")
        if index < 0:
            raise IndexError(f"Index must be non-negative, got {index}")
        if index >= len(items):
            raise IndexError(f"Index {index} out of range (list size: {len(items)})")
        return items[index]

    print("\nList access:")

    fruits = ["apple", "banana", "cherry"]
    indices = [0, 5, -1, "2"]

    for idx in indices:
        try:
            fruit = get_item(fruits, idx)
            print(f"  fruits[{idx}] = {fruit}")
        except (TypeError, IndexError) as e:
            print(f"  fruits[{idx}]: {e}")

if __name__ == "__main__":
    main()
# Raising exceptions with messages

def main():
    # Detailed error messages
    def withdraw(balance, amount):
        if amount <= 0:
            raise ValueError(f"Withdrawal amount must be positive, got {amount}")
        if amount > balance:
            raise ValueError(f"Insufficient funds: balance={balance}, requested={amount}")
        return balance - amount

    print("Bank withdrawals:\n")

    balance = 50

    # Successful withdrawal
    try:
        new_balance = withdraw(balance, 30)
        print(f"  Withdrew $30: New balance ${new_balance}")
        balance = new_balance
    except ValueError as e:
        print(f"  Error: {e}")

    # Invalid amount
    try:
        new_balance = withdraw(balance, -10)
        print(f"  Withdrew $-10: New balance ${new_balance}")
    except ValueError as e:
        print(f"  Error: {e}")

    # Insufficient funds
    try:
        new_balance = withdraw(balance, 200)
        print(f"  Withdrew $200: New balance ${new_balance}")
    except ValueError as e:
        print(f"  Error: {e}")


    # Type-specific error messages
    def calculate_discount(price, discount_percent):
        if not isinstance(price, (int, float)):
            raise TypeError(f"Price must be numeric, got {type(price).__name__}")
        if not isinstance(discount_percent, (int, float)):
            raise TypeError(f"Discount must be numeric, got {type(discount_percent).__name__}")
        if discount_percent < 0 or discount_percent > 100:
            raise ValueError(f"Discount must be 0-100%, got {discount_percent}")

        return price * (1 - discount_percent / 100)

    print("\nDiscount calculations:")

    test_cases = [
        (100, 10),
        (100, "10"),
        ("100", 10),
        (100, 150)
    ]

    for price, discount in test_cases:
        try:
            final_price = calculate_discount(price, discount)
            print(f"  ${price} - {discount}% = ${final_price:.2f}")
        except (TypeError, ValueError) as e:
            print(f"  ${price} - {discount}%: {e}")

    # Index validation with context
    def get_item(items, index):
        if not isinstance(index, int):
            raise TypeError(f"Index must be integer, got {type(index).__name__}")
        if index < 0:
            raise IndexError(f"Index must be non-negative, got {index}")
        if index >= len(items):
            raise IndexError(f"Index {index} out of range (list size: {len(items)})")
        return items[index]

    print("\nList access:")

    fruits = ["apple", "banana", "cherry"]
    indices = [0, 5, -1, "2"]

    for idx in indices:
        try:
            fruit = get_item(fruits, idx)
            print(f"  fruits[{idx}] = {fruit}")
        except (TypeError, IndexError) as e:
            print(f"  fruits[{idx}]: {e}")

if __name__ == "__main__":
    main()
# Raising exceptions with messages

def main():
    # Detailed error messages
    def withdraw(balance, amount):
        if amount <= 0:
            raise ValueError(f"Withdrawal amount must be positive, got {amount}")
        if amount > balance:
            raise ValueError(f"Insufficient funds: balance={balance}, requested={amount}")
        return balance - amount

    print("Bank withdrawals:\n")

    balance = 200

    # Successful withdrawal
    try:
        new_balance = withdraw(balance, 30)
        print(f"  Withdrew $30: New balance ${new_balance}")
        balance = new_balance
    except ValueError as e:
        print(f"  Error: {e}")

    # Invalid amount
    try:
        new_balance = withdraw(balance, -10)
        print(f"  Withdrew $-10: New balance ${new_balance}")
    except ValueError as e:
        print(f"  Error: {e}")

    # Insufficient funds
    try:
        new_balance = withdraw(balance, 200)
        print(f"  Withdrew $200: New balance ${new_balance}")
    except ValueError as e:
        print(f"  Error: {e}")


    # Type-specific error messages
    def calculate_discount(price, discount_percent):
        if not isinstance(price, (int, float)):
            raise TypeError(f"Price must be numeric, got {type(price).__name__}")
        if not isinstance(discount_percent, (int, float)):
            raise TypeError(f"Discount must be numeric, got {type(discount_percent).__name__}")
        if discount_percent < 0 or discount_percent > 100:
            raise ValueError(f"Discount must be 0-100%, got {discount_percent}")

        return price * (1 - discount_percent / 100)

    print("\nDiscount calculations:")

    test_cases = [
        (100, 10),
        (100, "10"),
        ("100", 10),
        (100, 150)
    ]

    for price, discount in test_cases:
        try:
            final_price = calculate_discount(price, discount)
            print(f"  ${price} - {discount}% = ${final_price:.2f}")
        except (TypeError, ValueError) as e:
            print(f"  ${price} - {discount}%: {e}")

    # Index validation with context
    def get_item(items, index):
        if not isinstance(index, int):
            raise TypeError(f"Index must be integer, got {type(index).__name__}")
        if index < 0:
            raise IndexError(f"Index must be non-negative, got {index}")
        if index >= len(items):
            raise IndexError(f"Index {index} out of range (list size: {len(items)})")
        return items[index]

    print("\nList access:")

    fruits = ["apple", "banana", "cherry"]
    indices = [0, 5, -1, "2"]

    for idx in indices:
        try:
            fruit = get_item(fruits, idx)
            print(f"  fruits[{idx}] = {fruit}")
        except (TypeError, IndexError) as e:
            print(f"  fruits[{idx}]: {e}")

if __name__ == "__main__":
    main()
  1. balance ← 100

    3def main():4    # Detailed error messages5    def withdraw(balance, amount):6        if amount <= 0:7            raise ValueError(f"Withdrawal amount must be positive, got {amount}")8        if amount > balance:9            raise ValueError(f"Insufficient funds: balance={balance}, requested={amount}")10        return balance - amount11    12    print("Bank withdrawals:\n")13    14    balance→ 100 = 10015    #@balance=50, 200
    outputBank withdrawals:
  2. try:

    17# Successful withdrawal18try:19    new_balance = withdraw(balance100, 30)20    print(f"  Withdrew $30: New balance ${new_balance}")
  3. def withdraw(balance, amount):

    pass 1 of 3
    4# Detailed error messages5def withdraw(balance100, amount30):6    if amount <= 0:7        raise ValueError(f"Withdrawal amount must be positive, got {amount}")8    if amount > balance:9        raise ValueError(f"Insufficient funds: balance={balance}, requested={amount}")10    return balance100 - amount30
    All 3 passes — pass 1 is the card above
    passbalanceamounte
    110030
    270-10Withdrawal amount must be positive, got -10
    370200Insufficient funds: balance=70, requested=200
  4. new_balance ← 70, balance ← 70

    18try:19    new_balance→ 70 = withdraw(balance100, 30)20    print(f"  Withdrew $30: New balance ${new_balance70}")21    balance→ 70 = new_balance7022except ValueError as e:
    output  Withdrew $30: New balance $70
  5. try:

    25# Invalid amount26try:27    new_balance = withdraw(balance70, -10)28    print(f"  Withdrew $-10: New balance ${new_balance}")
  6. if amount <= 0:

    5def withdraw(balance, amount):6    if amount-10 <= 0:7        raise ValueError(f"Withdrawal amount must be positive, got {amount-10}")8    if amount > balance:
  7. except ValueError as e:

    28    print(f"  Withdrew $-10: New balance ${new_balance}")29except ValueError as e:30    print(f"  Error: {eWithdrawal amount must be positive, got -10}")3132# Insufficient funds
    output  Error: Withdrawal amount must be positive, got -10
      Error: Withdrawal amount must be positive, got -10
  8. try:

    32# Insufficient funds33try:34    new_balance = withdraw(balance70, 200)35    print(f"  Withdrew $200: New balance ${new_balance}")
  9. if amount > balance:

    7    raise ValueError(f"Withdrawal amount must be positive, got {amount}")8if amount200 > balance70:9    raise ValueError(f"Insufficient funds: balance={balance70}, requested={amount200}")10return balance - amount
  10. except ValueError as e:

    35    print(f"  Withdrew $200: New balance ${new_balance}")36except ValueError as e:37    print(f"  Error: {eInsufficient funds: balance=70, requested=200}")3839#@help h1
    output  Error: Insufficient funds: balance=70, requested=200
      Error: Insufficient funds: balance=70, requested=200
  11. test_cases ← [(100, 10), (100, '10'), ('100', 10), (100, 150)]

    56print("\nDiscount calculations:")5758test_cases→ [(100, 10), (100, '10'), ('100', 10), (100, 150)] = [59    (100, 10),60    (100, "10"),61    ("100", 10),62    (100, 150)63]64#@test_cases=[(50, 20), (50, 110)]
    output
    Discount calculations:
  12. for price, discount in test_cases:

    pass 1 of 4
    66for price100, discount10 in test_cases[(100, 10), (100, '10'), ('100', 10), (100, 150)]:67    try:68        final_price = calculate_discount(price, discount)
    All 4 passes — pass 1 is the card above
    passdiscountdiscount_percent
    110
    21010
    310
    4150150
  13. try:

    pass 1 of 4
    66for price, discount in test_cases:67    try:68        final_price = calculate_discount(price100, discount10)69        print(f"  ${price} - {discount}% = ${final_price:.2f}")
    All 4 passes — pass 1 is the card above
    passdiscountdiscount_percent
    110
    21010
    310
    4150150
  14. def calculate_discount(price, discount_percent):

    pass 1 of 4
    45# Type-specific error messages46def calculate_discount(price100, discount_percent10):47    if not isinstance(price, (int, float)):48        raise TypeError(f"Price must be numeric, got {type(price).__name__}")49    if not isinstance(discount_percent, (int, float)):50        raise TypeError(f"Discount must be numeric, got {type(discount_percent).__name__}")51    if discount_percent < 0 or discount_percent > 100:52        raise ValueError(f"Discount must be 0-100%, got {discount_percent}")53    54    return price100 * (1 - discount_percent10 / 100)
    All 4 passes — pass 1 is the card above
    passdiscount_percent
    110
    210
    310
    4150
  15. final_price ← 90.0

    67try:68    final_price→ 90.0 = calculate_discount(price100, discount10)69    print(f"  ${price100} - {discount10}% = ${final_price90.0:.2f}")70except (TypeError, ValueError) as e:
    output  $100 - 10% = $90.00
  16. if not isinstance(discount_percent, (int, float)):

    48    raise TypeError(f"Price must be numeric, got {type(price).__name__}")49if not isinstance(discount_percent10, (int, float)):50    raise TypeError(f"Discount must be numeric, got {type(discount_percent10).__name__}")51if discount_percent < 0 or discount_percent > 100:
  17. except (TypeError, ValueError) as e:

    pass 1 of 3
    69    print(f"  ${price} - {discount}% = ${final_price:.2f}")70except (TypeError, ValueError) as e:71    print(f"  ${price100} - {discount10}%: {eDiscount must be numeric, got str}")
    output  $100 - 10%: Discount must be numeric, got str
      $100 - 10%: Discount must be numeric, got str
    All 3 passes — pass 1 is the card above
    passdiscountediscount_percent
    110Discount must be numeric, got str
    210Price must be numeric, got str150
    3150Discount must be 0-100%, got 150
  18. if not isinstance(price, (int, float)):

    46def calculate_discount(price, discount_percent):47    if not isinstance(price100, (int, float)):48        raise TypeError(f"Price must be numeric, got {type(price100).__name__}")49    if not isinstance(discount_percent, (int, float)):
  19. if discount_percent < 0 or discount_percent > 100:

    50    raise TypeError(f"Discount must be numeric, got {type(discount_percent).__name__}")51if discount_percent150 < 0 or discount_percent > 100:52    raise ValueError(f"Discount must be 0-100%, got {discount_percent150}")
  20. fruits ← ['apple', 'banana', 'cherry'], indices ← [0, 5, -1, '2']

    83print("\nList access:")8485fruits→ ['apple', 'banana', 'cherry'] = ["apple", "banana", "cherry"]86indices→ [0, 5, -1, '2'] = [0, 5, -1, "2"]
    output
    List access:
  21. for idx in indices:

    pass 1 of 4
    88for idx0 in indices[0, 5, -1, '2']:89    try:90        fruit = get_item(fruits, idx)
    All 4 passes — pass 1 is the card above
    passidxindexitems
    10
    255['apple', 'banana', 'cherry']
    3-1-1
    422
  22. try:

    pass 1 of 4
    88for idx in indices:89    try:90        fruit = get_item(fruits['apple', 'banana', 'cherry'], idx0)91        print(f"  fruits[{idx}] = {fruit}")
    All 4 passes — pass 1 is the card above
    passidxindexitems
    10
    255['apple', 'banana', 'cherry']
    3-1-1
    422
  23. def get_item(items, index):

    pass 1 of 4
    73# Index validation with context74def get_item(items['apple', 'banana', 'cherry'], index0):75    if not isinstance(index, int):76        raise TypeError(f"Index must be integer, got {type(index).__name__}")77    if index < 0:78        raise IndexError(f"Index must be non-negative, got {index}")79    if index >= len(items):80        raise IndexError(f"Index {index} out of range (list size: {len(items)})")81    return items[index]apple
    All 4 passes — pass 1 is the card above
    passindexitems[index]
    10apple
    25
    3-1
    42
  24. fruit ← apple

    89try:90    fruit→ apple = get_item(fruits['apple', 'banana', 'cherry'], idx0)91    print(f"  fruits[{idx0}] = {fruitapple}")92except (TypeError, IndexError) as e:
    output  fruits[0] = apple
  25. if index >= len(items):

    78    raise IndexError(f"Index must be non-negative, got {index}")79if index5 >= len(items['apple', 'banana', 'cherry']):80    raise IndexError(f"Index {index5} out of range (list size: {len(items['apple', 'banana', 'cherry'])})")81return items[index]
  26. except (TypeError, IndexError) as e:

    pass 1 of 3
    91    print(f"  fruits[{idx}] = {fruit}")92except (TypeError, IndexError) as e:93    print(f"  fruits[{idx5}]: {eIndex 5 out of range (list size: 3)}")
    output  fruits[5]: Index 5 out of range (list size: 3)
      fruits[5]: Index 5 out of range (list size: 3)
    All 3 passes — pass 1 is the card above
    passidxeindex
    15Index 5 out of range (list size: 3)-1
    2-1Index must be non-negative, got -12
    32Index must be integer, got str
  27. if index < 0:

    76    raise TypeError(f"Index must be integer, got {type(index).__name__}")77if index-1 < 0:78    raise IndexError(f"Index must be non-negative, got {index-1}")79if index >= len(items):
  28. if not isinstance(index, int):

    74def get_item(items, index):75    if not isinstance(index2, int):76        raise TypeError(f"Index must be integer, got {type(index2).__name__}")77    if index < 0:
  29. main()

    95if __name__ == "__main__":96    main()
  1. balance ← 50

    3def main():4    # Detailed error messages5    def withdraw(balance, amount):6        if amount <= 0:7            raise ValueError(f"Withdrawal amount must be positive, got {amount}")8        if amount > balance:9            raise ValueError(f"Insufficient funds: balance={balance}, requested={amount}")10        return balance - amount11    12    print("Bank withdrawals:\n")13    14    balance→ 50 = 50
    outputBank withdrawals:
  2. try:

    16# Successful withdrawal17try:18    new_balance = withdraw(balance50, 30)19    print(f"  Withdrew $30: New balance ${new_balance}")
  3. def withdraw(balance, amount):

    pass 1 of 3
    4# Detailed error messages5def withdraw(balance50, amount30):6    if amount <= 0:7        raise ValueError(f"Withdrawal amount must be positive, got {amount}")8    if amount > balance:9        raise ValueError(f"Insufficient funds: balance={balance}, requested={amount}")10    return balance50 - amount30
    All 3 passes — pass 1 is the card above
    passbalanceamounte
    15030
    220-10Withdrawal amount must be positive, got -10
    320200Insufficient funds: balance=20, requested=200
  4. new_balance ← 20, balance ← 20

    17try:18    new_balance→ 20 = withdraw(balance50, 30)19    print(f"  Withdrew $30: New balance ${new_balance20}")20    balance→ 20 = new_balance2021except ValueError as e:
    output  Withdrew $30: New balance $20
  5. try:

    24# Invalid amount25try:26    new_balance = withdraw(balance20, -10)27    print(f"  Withdrew $-10: New balance ${new_balance}")
  6. if amount <= 0:

    5def withdraw(balance, amount):6    if amount-10 <= 0:7        raise ValueError(f"Withdrawal amount must be positive, got {amount-10}")8    if amount > balance:
  7. except ValueError as e:

    27    print(f"  Withdrew $-10: New balance ${new_balance}")28except ValueError as e:29    print(f"  Error: {eWithdrawal amount must be positive, got -10}")3031# Insufficient funds
    output  Error: Withdrawal amount must be positive, got -10
      Error: Withdrawal amount must be positive, got -10
  8. try:

    31# Insufficient funds32try:33    new_balance = withdraw(balance20, 200)34    print(f"  Withdrew $200: New balance ${new_balance}")
  9. if amount > balance:

    7    raise ValueError(f"Withdrawal amount must be positive, got {amount}")8if amount200 > balance20:9    raise ValueError(f"Insufficient funds: balance={balance20}, requested={amount200}")10return balance - amount
  10. except ValueError as e:

    34    print(f"  Withdrew $200: New balance ${new_balance}")35except ValueError as e:36    print(f"  Error: {eInsufficient funds: balance=20, requested=200}")37
    output  Error: Insufficient funds: balance=20, requested=200
      Error: Insufficient funds: balance=20, requested=200
  11. test_cases ← [(100, 10), (100, '10'), ('100', 10), (100, 150)]

    50print("\nDiscount calculations:")5152test_cases→ [(100, 10), (100, '10'), ('100', 10), (100, 150)] = [53    (100, 10),54    (100, "10"),55    ("100", 10),56    (100, 150)57]
    output
    Discount calculations:
  12. for price, discount in test_cases:

    pass 1 of 4
    59for price100, discount10 in test_cases[(100, 10), (100, '10'), ('100', 10), (100, 150)]:60    try:61        final_price = calculate_discount(price, discount)
    All 4 passes — pass 1 is the card above
    passdiscountdiscount_percent
    110
    21010
    310
    4150150
  13. try:

    pass 1 of 4
    59for price, discount in test_cases:60    try:61        final_price = calculate_discount(price100, discount10)62        print(f"  ${price} - {discount}% = ${final_price:.2f}")
    All 4 passes — pass 1 is the card above
    passdiscountdiscount_percent
    110
    21010
    310
    4150150
  14. def calculate_discount(price, discount_percent):

    pass 1 of 4
    39# Type-specific error messages40def calculate_discount(price100, discount_percent10):41    if not isinstance(price, (int, float)):42        raise TypeError(f"Price must be numeric, got {type(price).__name__}")43    if not isinstance(discount_percent, (int, float)):44        raise TypeError(f"Discount must be numeric, got {type(discount_percent).__name__}")45    if discount_percent < 0 or discount_percent > 100:46        raise ValueError(f"Discount must be 0-100%, got {discount_percent}")47    48    return price100 * (1 - discount_percent10 / 100)
    All 4 passes — pass 1 is the card above
    passdiscount_percent
    110
    210
    310
    4150
  15. final_price ← 90.0

    60try:61    final_price→ 90.0 = calculate_discount(price100, discount10)62    print(f"  ${price100} - {discount10}% = ${final_price90.0:.2f}")63except (TypeError, ValueError) as e:
    output  $100 - 10% = $90.00
  16. if not isinstance(discount_percent, (int, float)):

    42    raise TypeError(f"Price must be numeric, got {type(price).__name__}")43if not isinstance(discount_percent10, (int, float)):44    raise TypeError(f"Discount must be numeric, got {type(discount_percent10).__name__}")45if discount_percent < 0 or discount_percent > 100:
  17. except (TypeError, ValueError) as e:

    pass 1 of 3
    62    print(f"  ${price} - {discount}% = ${final_price:.2f}")63except (TypeError, ValueError) as e:64    print(f"  ${price100} - {discount10}%: {eDiscount must be numeric, got str}")
    output  $100 - 10%: Discount must be numeric, got str
      $100 - 10%: Discount must be numeric, got str
    All 3 passes — pass 1 is the card above
    passdiscountediscount_percent
    110Discount must be numeric, got str
    210Price must be numeric, got str150
    3150Discount must be 0-100%, got 150
  18. if not isinstance(price, (int, float)):

    40def calculate_discount(price, discount_percent):41    if not isinstance(price100, (int, float)):42        raise TypeError(f"Price must be numeric, got {type(price100).__name__}")43    if not isinstance(discount_percent, (int, float)):
  19. if discount_percent < 0 or discount_percent > 100:

    44    raise TypeError(f"Discount must be numeric, got {type(discount_percent).__name__}")45if discount_percent150 < 0 or discount_percent > 100:46    raise ValueError(f"Discount must be 0-100%, got {discount_percent150}")
  20. fruits ← ['apple', 'banana', 'cherry'], indices ← [0, 5, -1, '2']

    76print("\nList access:")7778fruits→ ['apple', 'banana', 'cherry'] = ["apple", "banana", "cherry"]79indices→ [0, 5, -1, '2'] = [0, 5, -1, "2"]
    output
    List access:
  21. for idx in indices:

    pass 1 of 4
    81for idx0 in indices[0, 5, -1, '2']:82    try:83        fruit = get_item(fruits, idx)
    All 4 passes — pass 1 is the card above
    passidxindexitems
    10
    255['apple', 'banana', 'cherry']
    3-1-1
    422
  22. try:

    pass 1 of 4
    81for idx in indices:82    try:83        fruit = get_item(fruits['apple', 'banana', 'cherry'], idx0)84        print(f"  fruits[{idx}] = {fruit}")
    All 4 passes — pass 1 is the card above
    passidxindexitems
    10
    255['apple', 'banana', 'cherry']
    3-1-1
    422
  23. def get_item(items, index):

    pass 1 of 4
    66# Index validation with context67def get_item(items['apple', 'banana', 'cherry'], index0):68    if not isinstance(index, int):69        raise TypeError(f"Index must be integer, got {type(index).__name__}")70    if index < 0:71        raise IndexError(f"Index must be non-negative, got {index}")72    if index >= len(items):73        raise IndexError(f"Index {index} out of range (list size: {len(items)})")74    return items[index]apple
    All 4 passes — pass 1 is the card above
    passindexitems[index]
    10apple
    25
    3-1
    42
  24. fruit ← apple

    82try:83    fruit→ apple = get_item(fruits['apple', 'banana', 'cherry'], idx0)84    print(f"  fruits[{idx0}] = {fruitapple}")85except (TypeError, IndexError) as e:
    output  fruits[0] = apple
  25. if index >= len(items):

    71    raise IndexError(f"Index must be non-negative, got {index}")72if index5 >= len(items['apple', 'banana', 'cherry']):73    raise IndexError(f"Index {index5} out of range (list size: {len(items['apple', 'banana', 'cherry'])})")74return items[index]
  26. except (TypeError, IndexError) as e:

    pass 1 of 3
    84    print(f"  fruits[{idx}] = {fruit}")85except (TypeError, IndexError) as e:86    print(f"  fruits[{idx5}]: {eIndex 5 out of range (list size: 3)}")
    output  fruits[5]: Index 5 out of range (list size: 3)
      fruits[5]: Index 5 out of range (list size: 3)
    All 3 passes — pass 1 is the card above
    passidxeindex
    15Index 5 out of range (list size: 3)-1
    2-1Index must be non-negative, got -12
    32Index must be integer, got str
  27. if index < 0:

    69    raise TypeError(f"Index must be integer, got {type(index).__name__}")70if index-1 < 0:71    raise IndexError(f"Index must be non-negative, got {index-1}")72if index >= len(items):
  28. if not isinstance(index, int):

    67def get_item(items, index):68    if not isinstance(index2, int):69        raise TypeError(f"Index must be integer, got {type(index2).__name__}")70    if index < 0:
  29. main()

    88if __name__ == "__main__":89    main()
  1. balance ← 200

    3def main():4    # Detailed error messages5    def withdraw(balance, amount):6        if amount <= 0:7            raise ValueError(f"Withdrawal amount must be positive, got {amount}")8        if amount > balance:9            raise ValueError(f"Insufficient funds: balance={balance}, requested={amount}")10        return balance - amount11    12    print("Bank withdrawals:\n")13    14    balance→ 200 = 200
    outputBank withdrawals:
  2. try:

    16# Successful withdrawal17try:18    new_balance = withdraw(balance200, 30)19    print(f"  Withdrew $30: New balance ${new_balance}")
  3. def withdraw(balance, amount):

    pass 1 of 3
    4# Detailed error messages5def withdraw(balance200, amount30):6    if amount <= 0:7        raise ValueError(f"Withdrawal amount must be positive, got {amount}")8    if amount > balance:9        raise ValueError(f"Insufficient funds: balance={balance}, requested={amount}")10    return balance200 - amount30
    All 3 passes — pass 1 is the card above
    passbalanceamounte
    120030
    2170-10Withdrawal amount must be positive, got -10
    3170200Insufficient funds: balance=170, requested=200
  4. new_balance ← 170, balance ← 170

    17try:18    new_balance→ 170 = withdraw(balance200, 30)19    print(f"  Withdrew $30: New balance ${new_balance170}")20    balance→ 170 = new_balance17021except ValueError as e:
    output  Withdrew $30: New balance $170
  5. try:

    24# Invalid amount25try:26    new_balance = withdraw(balance170, -10)27    print(f"  Withdrew $-10: New balance ${new_balance}")
  6. if amount <= 0:

    5def withdraw(balance, amount):6    if amount-10 <= 0:7        raise ValueError(f"Withdrawal amount must be positive, got {amount-10}")8    if amount > balance:
  7. except ValueError as e:

    27    print(f"  Withdrew $-10: New balance ${new_balance}")28except ValueError as e:29    print(f"  Error: {eWithdrawal amount must be positive, got -10}")3031# Insufficient funds
    output  Error: Withdrawal amount must be positive, got -10
      Error: Withdrawal amount must be positive, got -10
  8. try:

    31# Insufficient funds32try:33    new_balance = withdraw(balance170, 200)34    print(f"  Withdrew $200: New balance ${new_balance}")
  9. if amount > balance:

    7    raise ValueError(f"Withdrawal amount must be positive, got {amount}")8if amount200 > balance170:9    raise ValueError(f"Insufficient funds: balance={balance170}, requested={amount200}")10return balance - amount
  10. except ValueError as e:

    34    print(f"  Withdrew $200: New balance ${new_balance}")35except ValueError as e:36    print(f"  Error: {eInsufficient funds: balance=170, requested=200}")37
    output  Error: Insufficient funds: balance=170, requested=200
      Error: Insufficient funds: balance=170, requested=200
  11. test_cases ← [(100, 10), (100, '10'), ('100', 10), (100, 150)]

    50print("\nDiscount calculations:")5152test_cases→ [(100, 10), (100, '10'), ('100', 10), (100, 150)] = [53    (100, 10),54    (100, "10"),55    ("100", 10),56    (100, 150)57]
    output
    Discount calculations:
  12. for price, discount in test_cases:

    pass 1 of 4
    59for price100, discount10 in test_cases[(100, 10), (100, '10'), ('100', 10), (100, 150)]:60    try:61        final_price = calculate_discount(price, discount)
    All 4 passes — pass 1 is the card above
    passdiscountdiscount_percent
    110
    21010
    310
    4150150
  13. try:

    pass 1 of 4
    59for price, discount in test_cases:60    try:61        final_price = calculate_discount(price100, discount10)62        print(f"  ${price} - {discount}% = ${final_price:.2f}")
    All 4 passes — pass 1 is the card above
    passdiscountdiscount_percent
    110
    21010
    310
    4150150
  14. def calculate_discount(price, discount_percent):

    pass 1 of 4
    39# Type-specific error messages40def calculate_discount(price100, discount_percent10):41    if not isinstance(price, (int, float)):42        raise TypeError(f"Price must be numeric, got {type(price).__name__}")43    if not isinstance(discount_percent, (int, float)):44        raise TypeError(f"Discount must be numeric, got {type(discount_percent).__name__}")45    if discount_percent < 0 or discount_percent > 100:46        raise ValueError(f"Discount must be 0-100%, got {discount_percent}")47    48    return price100 * (1 - discount_percent10 / 100)
    All 4 passes — pass 1 is the card above
    passdiscount_percent
    110
    210
    310
    4150
  15. final_price ← 90.0

    60try:61    final_price→ 90.0 = calculate_discount(price100, discount10)62    print(f"  ${price100} - {discount10}% = ${final_price90.0:.2f}")63except (TypeError, ValueError) as e:
    output  $100 - 10% = $90.00
  16. if not isinstance(discount_percent, (int, float)):

    42    raise TypeError(f"Price must be numeric, got {type(price).__name__}")43if not isinstance(discount_percent10, (int, float)):44    raise TypeError(f"Discount must be numeric, got {type(discount_percent10).__name__}")45if discount_percent < 0 or discount_percent > 100:
  17. except (TypeError, ValueError) as e:

    pass 1 of 3
    62    print(f"  ${price} - {discount}% = ${final_price:.2f}")63except (TypeError, ValueError) as e:64    print(f"  ${price100} - {discount10}%: {eDiscount must be numeric, got str}")
    output  $100 - 10%: Discount must be numeric, got str
      $100 - 10%: Discount must be numeric, got str
    All 3 passes — pass 1 is the card above
    passdiscountediscount_percent
    110Discount must be numeric, got str
    210Price must be numeric, got str150
    3150Discount must be 0-100%, got 150
  18. if not isinstance(price, (int, float)):

    40def calculate_discount(price, discount_percent):41    if not isinstance(price100, (int, float)):42        raise TypeError(f"Price must be numeric, got {type(price100).__name__}")43    if not isinstance(discount_percent, (int, float)):
  19. if discount_percent < 0 or discount_percent > 100:

    44    raise TypeError(f"Discount must be numeric, got {type(discount_percent).__name__}")45if discount_percent150 < 0 or discount_percent > 100:46    raise ValueError(f"Discount must be 0-100%, got {discount_percent150}")
  20. fruits ← ['apple', 'banana', 'cherry'], indices ← [0, 5, -1, '2']

    76print("\nList access:")7778fruits→ ['apple', 'banana', 'cherry'] = ["apple", "banana", "cherry"]79indices→ [0, 5, -1, '2'] = [0, 5, -1, "2"]
    output
    List access:
  21. for idx in indices:

    pass 1 of 4
    81for idx0 in indices[0, 5, -1, '2']:82    try:83        fruit = get_item(fruits, idx)
    All 4 passes — pass 1 is the card above
    passidxindexitems
    10
    255['apple', 'banana', 'cherry']
    3-1-1
    422
  22. try:

    pass 1 of 4
    81for idx in indices:82    try:83        fruit = get_item(fruits['apple', 'banana', 'cherry'], idx0)84        print(f"  fruits[{idx}] = {fruit}")
    All 4 passes — pass 1 is the card above
    passidxindexitems
    10
    255['apple', 'banana', 'cherry']
    3-1-1
    422
  23. def get_item(items, index):

    pass 1 of 4
    66# Index validation with context67def get_item(items['apple', 'banana', 'cherry'], index0):68    if not isinstance(index, int):69        raise TypeError(f"Index must be integer, got {type(index).__name__}")70    if index < 0:71        raise IndexError(f"Index must be non-negative, got {index}")72    if index >= len(items):73        raise IndexError(f"Index {index} out of range (list size: {len(items)})")74    return items[index]apple
    All 4 passes — pass 1 is the card above
    passindexitems[index]
    10apple
    25
    3-1
    42
  24. fruit ← apple

    82try:83    fruit→ apple = get_item(fruits['apple', 'banana', 'cherry'], idx0)84    print(f"  fruits[{idx0}] = {fruitapple}")85except (TypeError, IndexError) as e:
    output  fruits[0] = apple
  25. if index >= len(items):

    71    raise IndexError(f"Index must be non-negative, got {index}")72if index5 >= len(items['apple', 'banana', 'cherry']):73    raise IndexError(f"Index {index5} out of range (list size: {len(items['apple', 'banana', 'cherry'])})")74return items[index]
  26. except (TypeError, IndexError) as e:

    pass 1 of 3
    84    print(f"  fruits[{idx}] = {fruit}")85except (TypeError, IndexError) as e:86    print(f"  fruits[{idx5}]: {eIndex 5 out of range (list size: 3)}")
    output  fruits[5]: Index 5 out of range (list size: 3)
      fruits[5]: Index 5 out of range (list size: 3)
    All 3 passes — pass 1 is the card above
    passidxeindex
    15Index 5 out of range (list size: 3)-1
    2-1Index must be non-negative, got -12
    32Index must be integer, got str
  27. if index < 0:

    69    raise TypeError(f"Index must be integer, got {type(index).__name__}")70if index-1 < 0:71    raise IndexError(f"Index must be non-negative, got {index-1}")72if index >= len(items):
  28. if not isinstance(index, int):

    67def get_item(items, index):68    if not isinstance(index2, int):69        raise TypeError(f"Index must be integer, got {type(index2).__name__}")70    if index < 0:
  29. main()

    88if __name__ == "__main__":89    main()

Include the problematic value: raise ValueError(f"Invalid age: {age}").

Re-raise current exception

Catch, do something, then let it propagate.

test_data
reraise.py
Replay: real traced execution (multi-file project)
# Re-raising exceptions after logging

def main():
    # Log and re-raise
    def process_payment(amount):
        if amount <= 0:
            raise ValueError("Amount must be positive")
        print(f"    Processing payment: ${amount}")
        return {"status": "success", "amount": amount}

    def handle_payment(amount):
        try:
            return process_payment(amount)
        except ValueError as e:
            print(f"  [LOG] Payment failed: {e}")
            raise  # Re-raise the same exception

    print("Payment processing:\n")

    try:
        result = handle_payment(100)
        print(f"Result: {result}")
    except ValueError as e:
        print(f"Main caught: {e}")

    print()

    try:
        result = handle_payment(-50)
        print(f"Result: {result}")
    except ValueError as e:
        print(f"Main caught: {e}")


    # Cleanup and re-raise
    def database_operation(query):
        connection = "DB_CONNECTION"
        print(f"  Opened: {connection}")

        try:
            if "invalid" in query:
                raise RuntimeError(f"Bad query: {query}")
            print(f"  Executed: {query}")
            return "RESULT"
        except RuntimeError:
            print(f"  Error occurred, cleaning up...")
            print(f"  Closed: {connection}")
            raise  # Re-raise after cleanup
        finally:
            # This runs regardless
            pass

    print("\nDatabase operations:")

    try:
        result = database_operation("SELECT * FROM users")
        print(f"  Success: {result}\n")
    except RuntimeError as e:
        print(f"  Failed: {e}\n")

    try:
        result = database_operation("invalid syntax")
        print(f"  Success: {result}")
    except RuntimeError as e:
        print(f"  Failed: {e}")

    # Conditional re-raise
    def validate_and_parse(data):
        try:
            value = int(data)
            if value < 0:
                raise ValueError("Value must be non-negative")
            return value
        except ValueError as e:
            if "invalid literal" in str(e):
                # Handle parse errors locally
                print(f"  Parse error, using default")
                return 0
            else:
                # Re-raise validation errors
                raise

    print("\nValidation and parsing:")

    test_data = ["42", "abc", "-10"]

    for data in test_data:
        try:
            result = validate_and_parse(data)
            print(f"  '{data}' → {result}")
        except ValueError as e:
            print(f"  '{data}' → Error: {e}")

    # Transform and re-raise
    def risky_operation():
        items = [1, 2, 3]
        return items[10]  # IndexError

    def wrapped_operation():
        try:
            return risky_operation()
        except IndexError as e:
            print(f"  [LOG] IndexError: {e}")
            # Could transform exception type here
            raise RuntimeError("Operation failed") from e

    print("\nWrapped operation:")

    try:
        wrapped_operation()
    except RuntimeError as e:
        print(f"  Caught RuntimeError: {e}")
        print(f"  Caused by: {e.__cause__}")

if __name__ == "__main__":
    main()
# Re-raising exceptions after logging

def main():
    # Log and re-raise
    def process_payment(amount):
        if amount <= 0:
            raise ValueError("Amount must be positive")
        print(f"    Processing payment: ${amount}")
        return {"status": "success", "amount": amount}

    def handle_payment(amount):
        try:
            return process_payment(amount)
        except ValueError as e:
            print(f"  [LOG] Payment failed: {e}")
            raise  # Re-raise the same exception

    print("Payment processing:\n")

    try:
        result = handle_payment(100)
        print(f"Result: {result}")
    except ValueError as e:
        print(f"Main caught: {e}")

    print()

    try:
        result = handle_payment(-50)
        print(f"Result: {result}")
    except ValueError as e:
        print(f"Main caught: {e}")


    # Cleanup and re-raise
    def database_operation(query):
        connection = "DB_CONNECTION"
        print(f"  Opened: {connection}")

        try:
            if "invalid" in query:
                raise RuntimeError(f"Bad query: {query}")
            print(f"  Executed: {query}")
            return "RESULT"
        except RuntimeError:
            print(f"  Error occurred, cleaning up...")
            print(f"  Closed: {connection}")
            raise  # Re-raise after cleanup
        finally:
            # This runs regardless
            pass

    print("\nDatabase operations:")

    try:
        result = database_operation("SELECT * FROM users")
        print(f"  Success: {result}\n")
    except RuntimeError as e:
        print(f"  Failed: {e}\n")

    try:
        result = database_operation("invalid syntax")
        print(f"  Success: {result}")
    except RuntimeError as e:
        print(f"  Failed: {e}")

    # Conditional re-raise
    def validate_and_parse(data):
        try:
            value = int(data)
            if value < 0:
                raise ValueError("Value must be non-negative")
            return value
        except ValueError as e:
            if "invalid literal" in str(e):
                # Handle parse errors locally
                print(f"  Parse error, using default")
                return 0
            else:
                # Re-raise validation errors
                raise

    print("\nValidation and parsing:")

    test_data = ["10", "xyz", "-5"]

    for data in test_data:
        try:
            result = validate_and_parse(data)
            print(f"  '{data}' → {result}")
        except ValueError as e:
            print(f"  '{data}' → Error: {e}")

    # Transform and re-raise
    def risky_operation():
        items = [1, 2, 3]
        return items[10]  # IndexError

    def wrapped_operation():
        try:
            return risky_operation()
        except IndexError as e:
            print(f"  [LOG] IndexError: {e}")
            # Could transform exception type here
            raise RuntimeError("Operation failed") from e

    print("\nWrapped operation:")

    try:
        wrapped_operation()
    except RuntimeError as e:
        print(f"  Caught RuntimeError: {e}")
        print(f"  Caused by: {e.__cause__}")

if __name__ == "__main__":
    main()
# Re-raising exceptions after logging

def main():
    # Log and re-raise
    def process_payment(amount):
        if amount <= 0:
            raise ValueError("Amount must be positive")
        print(f"    Processing payment: ${amount}")
        return {"status": "success", "amount": amount}

    def handle_payment(amount):
        try:
            return process_payment(amount)
        except ValueError as e:
            print(f"  [LOG] Payment failed: {e}")
            raise  # Re-raise the same exception

    print("Payment processing:\n")

    try:
        result = handle_payment(100)
        print(f"Result: {result}")
    except ValueError as e:
        print(f"Main caught: {e}")

    print()

    try:
        result = handle_payment(-50)
        print(f"Result: {result}")
    except ValueError as e:
        print(f"Main caught: {e}")


    # Cleanup and re-raise
    def database_operation(query):
        connection = "DB_CONNECTION"
        print(f"  Opened: {connection}")

        try:
            if "invalid" in query:
                raise RuntimeError(f"Bad query: {query}")
            print(f"  Executed: {query}")
            return "RESULT"
        except RuntimeError:
            print(f"  Error occurred, cleaning up...")
            print(f"  Closed: {connection}")
            raise  # Re-raise after cleanup
        finally:
            # This runs regardless
            pass

    print("\nDatabase operations:")

    try:
        result = database_operation("SELECT * FROM users")
        print(f"  Success: {result}\n")
    except RuntimeError as e:
        print(f"  Failed: {e}\n")

    try:
        result = database_operation("invalid syntax")
        print(f"  Success: {result}")
    except RuntimeError as e:
        print(f"  Failed: {e}")

    # Conditional re-raise
    def validate_and_parse(data):
        try:
            value = int(data)
            if value < 0:
                raise ValueError("Value must be non-negative")
            return value
        except ValueError as e:
            if "invalid literal" in str(e):
                # Handle parse errors locally
                print(f"  Parse error, using default")
                return 0
            else:
                # Re-raise validation errors
                raise

    print("\nValidation and parsing:")

    test_data = ["100", "0", "abc"]

    for data in test_data:
        try:
            result = validate_and_parse(data)
            print(f"  '{data}' → {result}")
        except ValueError as e:
            print(f"  '{data}' → Error: {e}")

    # Transform and re-raise
    def risky_operation():
        items = [1, 2, 3]
        return items[10]  # IndexError

    def wrapped_operation():
        try:
            return risky_operation()
        except IndexError as e:
            print(f"  [LOG] IndexError: {e}")
            # Could transform exception type here
            raise RuntimeError("Operation failed") from e

    print("\nWrapped operation:")

    try:
        wrapped_operation()
    except RuntimeError as e:
        print(f"  Caught RuntimeError: {e}")
        print(f"  Caused by: {e.__cause__}")

if __name__ == "__main__":
    main()
  1. def main(): # Log and re-raise

    3def main():4    # Log and re-raise5    def process_payment(amount):6        if amount <= 0:7            raise ValueError("Amount must be positive")8        print(f"    Processing payment: ${amount}")9        return {"status": "success", "amount": amount}10    11    def handle_payment(amount):12        try:13            return process_payment(amount)14        except ValueError as e:15            print(f"  [LOG] Payment failed: {e}")16            raise  # Re-raise the same exception17    18    print("Payment processing:\n")
    outputPayment processing:
  2. def handle_payment(amount):

    pass 1 of 2
    11def handle_payment(amount100):12    try:13        return process_payment(amount)
  3. try:

    pass 1 of 2
    11def handle_payment(amount):12    try:13        return process_payment(amount100)14    except ValueError as e:
  4. def process_payment(amount):

    pass 1 of 2
    4# Log and re-raise5def process_payment(amount100):6    if amount <= 0:7        raise ValueError("Amount must be positive")8    print(f"    Processing payment: ${amount100}")9    return {"status": "success", "amount": amount100}
    output    Processing payment: $100
  5. result ← {'status': 'success', 'amount': 100}

    20try:21    result→ {'status': 'success', 'amount': 100} = handle_payment(100)22    print(f"Result: {result{'status': 'success', 'amount': 100}}")23except ValueError as e:
    outputResult: {'status': 'success', 'amount': 100}
  6. print()

    26print()
  7. def handle_payment(amount):

    pass 2 of 2
    11def handle_payment(amount-50):12    try:13        return process_payment(amount)
  8. try:

    pass 2 of 2
    11def handle_payment(amount):12    try:13        return process_payment(amount-50)14    except ValueError as e:
  9. def process_payment(amount):

    pass 2 of 2
    4# Log and re-raise5def process_payment(amount-50):6    if amount <= 0:7        raise ValueError("Amount must be positive")
  10. if amount <= 0:

    5def process_payment(amount):6    if amount-50 <= 0:7        raise ValueError("Amount must be positive")8    print(f"    Processing payment: ${amount}")
  11. except ValueError as e:

    13    return process_payment(amount)14except ValueError as e:15    print(f"  [LOG] Payment failed: {eAmount must be positive}")16    raise  # Re-raise the same exception
    output  [LOG] Payment failed: Amount must be positive
  12. except ValueError as e:

    30    print(f"Result: {result}")31except ValueError as e:32    print(f"Main caught: {eAmount must be positive}")3334#@help h1
    outputMain caught: Amount must be positive
    Main caught: Amount must be positive
  13. print(" Database operations:")

    58print("\nDatabase operations:")
    output
    Database operations:
  14. connection ← DB_CONNECTION

    pass 1 of 2
    40# Cleanup and re-raise41def database_operation(querySELECT * FROM users):42    connection→ DB_CONNECTION = "DB_CONNECTION"43    print(f"  Opened: {connectionDB_CONNECTION}")
    output  Opened: DB_CONNECTION
  15. try:

    pass 1 of 2
    45try:46    if "invalid" in query:47        raise RuntimeError(f"Bad query: {query}")48    print(f"  Executed: {querySELECT * FROM users}")49    return "RESULT"50except RuntimeError:
    output  Executed: SELECT * FROM users
  16. result ← RESULT

    60try:61    result→ RESULT = database_operation("SELECT * FROM users")62    print(f"  Success: {resultRESULT}\n")63except RuntimeError as e:
    output  Success: RESULT
  17. connection ← DB_CONNECTION

    pass 2 of 2
    40# Cleanup and re-raise41def database_operation(queryinvalid syntax):42    connection→ DB_CONNECTION = "DB_CONNECTION"43    print(f"  Opened: {connectionDB_CONNECTION}")
    output  Opened: DB_CONNECTION
  18. try:

    pass 2 of 2
    45try:46    if "invalid" in query:47        raise RuntimeError(f"Bad query: {query}")
  19. if "invalid" in query:

    45try:46    if "invalid" in queryinvalid syntax:47        raise RuntimeError(f"Bad query: {queryinvalid syntax}")48    print(f"  Executed: {query}")
  20. except RuntimeError:

    49    return "RESULT"50except RuntimeError:51    print(f"  Error occurred, cleaning up...")52    print(f"  Closed: {connectionDB_CONNECTION}")53    raise  # Re-raise after cleanup54finally:
    output  Error occurred, cleaning up...
      Closed: DB_CONNECTION
  21. except RuntimeError as e:

    68    print(f"  Success: {result}")69except RuntimeError as e:70    print(f"  Failed: {eBad query: invalid syntax}")7172# Conditional re-raise
    output  Failed: Bad query: invalid syntax
      Failed: Bad query: invalid syntax
  22. test_data ← ['42', 'abc', '-10']

    88print("\nValidation and parsing:")8990test_data→ ['42', 'abc', '-10'] = ["42", "abc", "-10"]91#@test_data=["10", "xyz", "-5"], ["100", "0", "abc"]
    output
    Validation and parsing:
  23. for data in test_data:

    pass 1 of 3
    93for data42 in test_data['42', 'abc', '-10']:94    try:95        result = validate_and_parse(data)
    All 3 passes — pass 1 is the card above
    passdataevalue
    142
    2abcinvalid literal for int() with base 10: 'abc'
    3-10Value must be non-negative-10
  24. try:

    pass 1 of 3
    93for data in test_data:94    try:95        result = validate_and_parse(data42)96        print(f"  '{data}' → {result}")
    All 3 passes — pass 1 is the card above
    passdataevalue
    142
    2abcinvalid literal for int() with base 10: 'abc'
    3-10Value must be non-negative-10
  25. def validate_and_parse(data):

    pass 1 of 3
    72# Conditional re-raise73def validate_and_parse(data42):74    try:75        value = int(data)
    All 3 passes — pass 1 is the card above
    passdataevalue
    142
    2abcinvalid literal for int() with base 10: 'abc'
    3-10Value must be non-negative-10
  26. value ← 42

    pass 1 of 3
    73def validate_and_parse(data):74    try:75        value→ 42 = int(data42)76        if value < 0:77            raise ValueError("Value must be non-negative")78        return value4279    except ValueError as e:
    All 3 passes — pass 1 is the card above
    passdataevalue
    14242
    2abcinvalid literal for int() with base 10: 'abc'
    3-10Value must be non-negative-10
  27. result ← 42

    94try:95    result→ 42 = validate_and_parse(data42)96    print(f"  '{data42}' → {result42}")97except ValueError as e:
    output  '42' → 42
  28. if "invalid literal" in str(e): # Handle parse errors …

    79except ValueError as e:80    if "invalid literal" in str(einvalid literal for int() with base 10: 'abc'):81        # Handle parse errors locally82        print(f"  Parse error, using default")83        return 084    else:
    output  Parse error, using default
  29. result ← 0

    94try:95    result→ 0 = validate_and_parse(dataabc)96    print(f"  '{dataabc}' → {result0}")97except ValueError as e:
    output  'abc' → 0
  30. if value < 0:

    75value = int(data)76if value-10 < 0:77    raise ValueError("Value must be non-negative")78return value
  31. except ValueError as e:

    96    print(f"  '{data}' → {result}")97except ValueError as e:98    print(f"  '{data-10}' → Error: {eValue must be non-negative}")
    output  '-10' → Error: Value must be non-negative
      '-10' → Error: Value must be non-negative
  32. print(" Wrapped operation:")

    113print("\nWrapped operation:")
    output
    Wrapped operation:
  33. items ← [1, 2, 3]

    100# Transform and re-raise101def risky_operation():102    items→ [1, 2, 3] = [1, 2, 3]103    return items[10](empty)  # IndexError
  34. except IndexError as e:

    107    return risky_operation()108except IndexError as e:109    print(f"  [LOG] IndexError: {elist index out of range}")110    # Could transform exception type here111    raise RuntimeError("Operation failed") from elist index out of range
    output  [LOG] IndexError: list index out of range
  35. except RuntimeError as e:

    116    wrapped_operation()117except RuntimeError as e:118    print(f"  Caught RuntimeError: {eOperation failed}")119    print(f"  Caused by: {e.__cause__list index out of range}")
    output  Caught RuntimeError: Operation failed
      Caused by: list index out of range
      Caused by: list index out of range
  36. main()

    121if __name__ == "__main__":122    main()
  1. def main(): # Log and re-raise

    3def main():4    # Log and re-raise5    def process_payment(amount):6        if amount <= 0:7            raise ValueError("Amount must be positive")8        print(f"    Processing payment: ${amount}")9        return {"status": "success", "amount": amount}10    11    def handle_payment(amount):12        try:13            return process_payment(amount)14        except ValueError as e:15            print(f"  [LOG] Payment failed: {e}")16            raise  # Re-raise the same exception17    18    print("Payment processing:\n")
    outputPayment processing:
  2. def handle_payment(amount):

    pass 1 of 2
    11def handle_payment(amount100):12    try:13        return process_payment(amount)
  3. try:

    pass 1 of 2
    11def handle_payment(amount):12    try:13        return process_payment(amount100)14    except ValueError as e:
  4. def process_payment(amount):

    pass 1 of 2
    4# Log and re-raise5def process_payment(amount100):6    if amount <= 0:7        raise ValueError("Amount must be positive")8    print(f"    Processing payment: ${amount100}")9    return {"status": "success", "amount": amount100}
    output    Processing payment: $100
  5. result ← {'status': 'success', 'amount': 100}

    20try:21    result→ {'status': 'success', 'amount': 100} = handle_payment(100)22    print(f"Result: {result{'status': 'success', 'amount': 100}}")23except ValueError as e:
    outputResult: {'status': 'success', 'amount': 100}
  6. print()

    26print()
  7. def handle_payment(amount):

    pass 2 of 2
    11def handle_payment(amount-50):12    try:13        return process_payment(amount)
  8. try:

    pass 2 of 2
    11def handle_payment(amount):12    try:13        return process_payment(amount-50)14    except ValueError as e:
  9. def process_payment(amount):

    pass 2 of 2
    4# Log and re-raise5def process_payment(amount-50):6    if amount <= 0:7        raise ValueError("Amount must be positive")
  10. if amount <= 0:

    5def process_payment(amount):6    if amount-50 <= 0:7        raise ValueError("Amount must be positive")8    print(f"    Processing payment: ${amount}")
  11. except ValueError as e:

    13    return process_payment(amount)14except ValueError as e:15    print(f"  [LOG] Payment failed: {eAmount must be positive}")16    raise  # Re-raise the same exception
    output  [LOG] Payment failed: Amount must be positive
  12. except ValueError as e:

    30    print(f"Result: {result}")31except ValueError as e:32    print(f"Main caught: {eAmount must be positive}")33
    outputMain caught: Amount must be positive
    Main caught: Amount must be positive
  13. print(" Database operations:")

    53print("\nDatabase operations:")
    output
    Database operations:
  14. connection ← DB_CONNECTION

    pass 1 of 2
    35# Cleanup and re-raise36def database_operation(querySELECT * FROM users):37    connection→ DB_CONNECTION = "DB_CONNECTION"38    print(f"  Opened: {connectionDB_CONNECTION}")
    output  Opened: DB_CONNECTION
  15. try:

    pass 1 of 2
    40try:41    if "invalid" in query:42        raise RuntimeError(f"Bad query: {query}")43    print(f"  Executed: {querySELECT * FROM users}")44    return "RESULT"45except RuntimeError:
    output  Executed: SELECT * FROM users
  16. result ← RESULT

    55try:56    result→ RESULT = database_operation("SELECT * FROM users")57    print(f"  Success: {resultRESULT}\n")58except RuntimeError as e:
    output  Success: RESULT
  17. connection ← DB_CONNECTION

    pass 2 of 2
    35# Cleanup and re-raise36def database_operation(queryinvalid syntax):37    connection→ DB_CONNECTION = "DB_CONNECTION"38    print(f"  Opened: {connectionDB_CONNECTION}")
    output  Opened: DB_CONNECTION
  18. try:

    pass 2 of 2
    40try:41    if "invalid" in query:42        raise RuntimeError(f"Bad query: {query}")
  19. if "invalid" in query:

    40try:41    if "invalid" in queryinvalid syntax:42        raise RuntimeError(f"Bad query: {queryinvalid syntax}")43    print(f"  Executed: {query}")
  20. except RuntimeError:

    44    return "RESULT"45except RuntimeError:46    print(f"  Error occurred, cleaning up...")47    print(f"  Closed: {connectionDB_CONNECTION}")48    raise  # Re-raise after cleanup49finally:
    output  Error occurred, cleaning up...
      Closed: DB_CONNECTION
  21. except RuntimeError as e:

    63    print(f"  Success: {result}")64except RuntimeError as e:65    print(f"  Failed: {eBad query: invalid syntax}")6667# Conditional re-raise
    output  Failed: Bad query: invalid syntax
      Failed: Bad query: invalid syntax
  22. test_data ← ['10', 'xyz', '-5']

    83print("\nValidation and parsing:")8485test_data→ ['10', 'xyz', '-5'] = ["10", "xyz", "-5"]
    output
    Validation and parsing:
  23. for data in test_data:

    pass 1 of 3
    87for data10 in test_data['10', 'xyz', '-5']:88    try:89        result = validate_and_parse(data)
    All 3 passes — pass 1 is the card above
    passdataevalue
    110
    2xyzinvalid literal for int() with base 10: 'xyz'
    3-5Value must be non-negative-5
  24. try:

    pass 1 of 3
    87for data in test_data:88    try:89        result = validate_and_parse(data10)90        print(f"  '{data}' → {result}")
    All 3 passes — pass 1 is the card above
    passdataevalue
    110
    2xyzinvalid literal for int() with base 10: 'xyz'
    3-5Value must be non-negative-5
  25. def validate_and_parse(data):

    pass 1 of 3
    67# Conditional re-raise68def validate_and_parse(data10):69    try:70        value = int(data)
    All 3 passes — pass 1 is the card above
    passdataevalue
    110
    2xyzinvalid literal for int() with base 10: 'xyz'
    3-5Value must be non-negative-5
  26. value ← 10

    pass 1 of 3
    68def validate_and_parse(data):69    try:70        value→ 10 = int(data10)71        if value < 0:72            raise ValueError("Value must be non-negative")73        return value1074    except ValueError as e:
    All 3 passes — pass 1 is the card above
    passdataevalue
    11010
    2xyzinvalid literal for int() with base 10: 'xyz'
    3-5Value must be non-negative-5
  27. result ← 10

    88try:89    result→ 10 = validate_and_parse(data10)90    print(f"  '{data10}' → {result10}")91except ValueError as e:
    output  '10' → 10
  28. if "invalid literal" in str(e): # Handle parse errors …

    74except ValueError as e:75    if "invalid literal" in str(einvalid literal for int() with base 10: 'xyz'):76        # Handle parse errors locally77        print(f"  Parse error, using default")78        return 079    else:
    output  Parse error, using default
  29. result ← 0

    88try:89    result→ 0 = validate_and_parse(dataxyz)90    print(f"  '{dataxyz}' → {result0}")91except ValueError as e:
    output  'xyz' → 0
  30. if value < 0:

    70value = int(data)71if value-5 < 0:72    raise ValueError("Value must be non-negative")73return value
  31. except ValueError as e:

    90    print(f"  '{data}' → {result}")91except ValueError as e:92    print(f"  '{data-5}' → Error: {eValue must be non-negative}")
    output  '-5' → Error: Value must be non-negative
      '-5' → Error: Value must be non-negative
  32. print(" Wrapped operation:")

    107print("\nWrapped operation:")
    output
    Wrapped operation:
  33. items ← [1, 2, 3]

    94# Transform and re-raise95def risky_operation():96    items→ [1, 2, 3] = [1, 2, 3]97    return items[10](empty)  # IndexError
  34. except IndexError as e:

    101    return risky_operation()102except IndexError as e:103    print(f"  [LOG] IndexError: {elist index out of range}")104    # Could transform exception type here105    raise RuntimeError("Operation failed") from elist index out of range
    output  [LOG] IndexError: list index out of range
  35. except RuntimeError as e:

    110    wrapped_operation()111except RuntimeError as e:112    print(f"  Caught RuntimeError: {eOperation failed}")113    print(f"  Caused by: {e.__cause__list index out of range}")
    output  Caught RuntimeError: Operation failed
      Caused by: list index out of range
      Caused by: list index out of range
  36. main()

    115if __name__ == "__main__":116    main()
  1. def main(): # Log and re-raise

    3def main():4    # Log and re-raise5    def process_payment(amount):6        if amount <= 0:7            raise ValueError("Amount must be positive")8        print(f"    Processing payment: ${amount}")9        return {"status": "success", "amount": amount}10    11    def handle_payment(amount):12        try:13            return process_payment(amount)14        except ValueError as e:15            print(f"  [LOG] Payment failed: {e}")16            raise  # Re-raise the same exception17    18    print("Payment processing:\n")
    outputPayment processing:
  2. def handle_payment(amount):

    pass 1 of 2
    11def handle_payment(amount100):12    try:13        return process_payment(amount)
  3. try:

    pass 1 of 2
    11def handle_payment(amount):12    try:13        return process_payment(amount100)14    except ValueError as e:
  4. def process_payment(amount):

    pass 1 of 2
    4# Log and re-raise5def process_payment(amount100):6    if amount <= 0:7        raise ValueError("Amount must be positive")8    print(f"    Processing payment: ${amount100}")9    return {"status": "success", "amount": amount100}
    output    Processing payment: $100
  5. result ← {'status': 'success', 'amount': 100}

    20try:21    result→ {'status': 'success', 'amount': 100} = handle_payment(100)22    print(f"Result: {result{'status': 'success', 'amount': 100}}")23except ValueError as e:
    outputResult: {'status': 'success', 'amount': 100}
  6. print()

    26print()
  7. def handle_payment(amount):

    pass 2 of 2
    11def handle_payment(amount-50):12    try:13        return process_payment(amount)
  8. try:

    pass 2 of 2
    11def handle_payment(amount):12    try:13        return process_payment(amount-50)14    except ValueError as e:
  9. def process_payment(amount):

    pass 2 of 2
    4# Log and re-raise5def process_payment(amount-50):6    if amount <= 0:7        raise ValueError("Amount must be positive")
  10. if amount <= 0:

    5def process_payment(amount):6    if amount-50 <= 0:7        raise ValueError("Amount must be positive")8    print(f"    Processing payment: ${amount}")
  11. except ValueError as e:

    13    return process_payment(amount)14except ValueError as e:15    print(f"  [LOG] Payment failed: {eAmount must be positive}")16    raise  # Re-raise the same exception
    output  [LOG] Payment failed: Amount must be positive
  12. except ValueError as e:

    30    print(f"Result: {result}")31except ValueError as e:32    print(f"Main caught: {eAmount must be positive}")33
    outputMain caught: Amount must be positive
    Main caught: Amount must be positive
  13. print(" Database operations:")

    53print("\nDatabase operations:")
    output
    Database operations:
  14. connection ← DB_CONNECTION

    pass 1 of 2
    35# Cleanup and re-raise36def database_operation(querySELECT * FROM users):37    connection→ DB_CONNECTION = "DB_CONNECTION"38    print(f"  Opened: {connectionDB_CONNECTION}")
    output  Opened: DB_CONNECTION
  15. try:

    pass 1 of 2
    40try:41    if "invalid" in query:42        raise RuntimeError(f"Bad query: {query}")43    print(f"  Executed: {querySELECT * FROM users}")44    return "RESULT"45except RuntimeError:
    output  Executed: SELECT * FROM users
  16. result ← RESULT

    55try:56    result→ RESULT = database_operation("SELECT * FROM users")57    print(f"  Success: {resultRESULT}\n")58except RuntimeError as e:
    output  Success: RESULT
  17. connection ← DB_CONNECTION

    pass 2 of 2
    35# Cleanup and re-raise36def database_operation(queryinvalid syntax):37    connection→ DB_CONNECTION = "DB_CONNECTION"38    print(f"  Opened: {connectionDB_CONNECTION}")
    output  Opened: DB_CONNECTION
  18. try:

    pass 2 of 2
    40try:41    if "invalid" in query:42        raise RuntimeError(f"Bad query: {query}")
  19. if "invalid" in query:

    40try:41    if "invalid" in queryinvalid syntax:42        raise RuntimeError(f"Bad query: {queryinvalid syntax}")43    print(f"  Executed: {query}")
  20. except RuntimeError:

    44    return "RESULT"45except RuntimeError:46    print(f"  Error occurred, cleaning up...")47    print(f"  Closed: {connectionDB_CONNECTION}")48    raise  # Re-raise after cleanup49finally:
    output  Error occurred, cleaning up...
      Closed: DB_CONNECTION
  21. except RuntimeError as e:

    63    print(f"  Success: {result}")64except RuntimeError as e:65    print(f"  Failed: {eBad query: invalid syntax}")6667# Conditional re-raise
    output  Failed: Bad query: invalid syntax
      Failed: Bad query: invalid syntax
  22. test_data ← ['100', '0', 'abc']

    83print("\nValidation and parsing:")8485test_data→ ['100', '0', 'abc'] = ["100", "0", "abc"]
    output
    Validation and parsing:
  23. for data in test_data:

    pass 1 of 3
    87for data100 in test_data['100', '0', 'abc']:88    try:89        result = validate_and_parse(data)
    All 3 passes — pass 1 is the card above
    passdatae
    1100
    20
    3abcinvalid literal for int() with base 10: 'abc'
  24. try:

    pass 1 of 3
    87for data in test_data:88    try:89        result = validate_and_parse(data100)90        print(f"  '{data}' → {result}")
    All 3 passes — pass 1 is the card above
    passdatae
    1100
    20
    3abcinvalid literal for int() with base 10: 'abc'
  25. def validate_and_parse(data):

    pass 1 of 3
    67# Conditional re-raise68def validate_and_parse(data100):69    try:70        value = int(data)
    All 3 passes — pass 1 is the card above
    passdatae
    1100
    20
    3abcinvalid literal for int() with base 10: 'abc'
  26. value ← 100

    pass 1 of 3
    68def validate_and_parse(data):69    try:70        value→ 100 = int(data100)71        if value < 0:72            raise ValueError("Value must be non-negative")73        return value10074    except ValueError as e:
    All 3 passes — pass 1 is the card above
    passdataevalue
    1100100
    200
    3abcinvalid literal for int() with base 10: 'abc'
  27. result ← 100

    88try:89    result→ 100 = validate_and_parse(data100)90    print(f"  '{data100}' → {result100}")91except ValueError as e:
    output  '100' → 100
  28. result ← 0

    88try:89    result→ 0 = validate_and_parse(data0)90    print(f"  '{data0}' → {result0}")91except ValueError as e:
    output  '0' → 0
  29. if "invalid literal" in str(e): # Handle parse errors …

    74except ValueError as e:75    if "invalid literal" in str(einvalid literal for int() with base 10: 'abc'):76        # Handle parse errors locally77        print(f"  Parse error, using default")78        return 079    else:
    output  Parse error, using default
  30. result ← 0

    88try:89    result→ 0 = validate_and_parse(dataabc)90    print(f"  '{dataabc}' → {result0}")91except ValueError as e:
    output  'abc' → 0
  31. print(" Wrapped operation:")

    107print("\nWrapped operation:")
    output
    Wrapped operation:
  32. items ← [1, 2, 3]

    94# Transform and re-raise95def risky_operation():96    items→ [1, 2, 3] = [1, 2, 3]97    return items[10](empty)  # IndexError
  33. except IndexError as e:

    101    return risky_operation()102except IndexError as e:103    print(f"  [LOG] IndexError: {elist index out of range}")104    # Could transform exception type here105    raise RuntimeError("Operation failed") from elist index out of range
    output  [LOG] IndexError: list index out of range
  34. except RuntimeError as e:

    110    wrapped_operation()111except RuntimeError as e:112    print(f"  Caught RuntimeError: {eOperation failed}")113    print(f"  Caused by: {e.__cause__list index out of range}")
    output  Caught RuntimeError: Operation failed
      Caused by: list index out of range
      Caused by: list index out of range
  35. main()

    115if __name__ == "__main__":116    main()

Bare raise in except block re-raises the current exception.

Exception chaining

Preserve original exception when raising a new one.

exception_chaining.py
Replay: real traced execution (multi-file project)
# Exception chaining with 'from'

def main():
    # Chain exceptions with 'from'
    def load_config():
        config_data = {"port": "invalid"}

        try:
            port = int(config_data["port"])
            return port
        except ValueError as e:
            # Chain new exception from original
            raise RuntimeError("Config load failed") from e

    print("Exception chaining:\n")

    try:
        port = load_config()
        print(f"Port: {port}")
    except RuntimeError as e:
        print(f"Exception: {e}")
        print(f"Caused by: {e.__cause__}")
        print(f"Cause type: {type(e.__cause__).__name__}")


    # Multiple layers of chaining
    def parse_number(text):
        try:
            return int(text)
        except ValueError as e:
            raise TypeError("Not a number") from e

    def process_input(text):
        try:
            num = parse_number(text)
            return num * 2
        except TypeError as e:
            raise RuntimeError("Processing failed") from e

    print("\nMulti-layer chaining:")

    try:
        result = process_input("abc")
        print(f"Result: {result}")
    except RuntimeError as e:
        print(f"Final exception: {e}")
        print(f"Direct cause: {e.__cause__}")
        print(f"Root cause: {e.__cause__.__cause__}")

    # Suppress exception chaining
    def operation_with_fallback():
        try:
            result = 10 / 0
        except ZeroDivisionError:
            # Suppress chain with 'from None'
            raise ValueError("Invalid operation") from None

    print("\nSuppressed chain:")

    try:
        operation_with_fallback()
    except ValueError as e:
        print(f"Exception: {e}")
        print(f"Has cause: {e.__cause__ is not None}")

    # Practical example: data pipeline
    def fetch_data(source):
        if source == "database":
            raise ConnectionError("Database unreachable")
        return None

    def transform_data(data):
        if data is None:
            raise ValueError("No data to transform")
        return data.upper()

    def save_results(data):
        if not data:
            raise IOError("Cannot save empty data")
        return "SAVED"

    def run_pipeline(source):
        try:
            try:
                data = fetch_data(source)
            except ConnectionError as e:
                raise RuntimeError("Fetch stage failed") from e

            try:
                transformed = transform_data(data)
            except ValueError as e:
                raise RuntimeError("Transform stage failed") from e

            try:
                result = save_results(transformed)
            except IOError as e:
                raise RuntimeError("Save stage failed") from e

            return result
        except RuntimeError:
            raise

    print("\nPipeline execution:")

    try:
        run_pipeline("database")
    except RuntimeError as e:
        print(f"Pipeline failed: {e}")
        if e.__cause__:
            print(f"  Root cause: {type(e.__cause__).__name__}: {e.__cause__}")

    # Check exception context
    def analyze_exception(exc):
        print(f"\nException analysis:")
        print(f"  Type: {type(exc).__name__}")
        print(f"  Message: {exc}")

        if exc.__cause__:
            print(f"  Has cause: Yes")
            print(f"  Cause: {type(exc.__cause__).__name__}: {exc.__cause__}")
        else:
            print(f"  Has cause: No")

    try:
        raise ValueError("Top level") from TypeError("Bottom level")
    except ValueError as e:
        analyze_exception(e)

if __name__ == "__main__":
    main()
  1. def main(): # Chain exceptions with 'from'

    3def main():4    # Chain exceptions with 'from'5    def load_config():6        config_data = {"port": "invalid"}7        8        try:9            port = int(config_data["port"])10            return port11        except ValueError as e:12            # Chain new exception from original13            raise RuntimeError("Config load failed") from e14    15    print("Exception chaining:\n")
    outputException chaining:
  2. config_data ← {'port': 'invalid'}

    4# Chain exceptions with 'from'5def load_config():6    config_data→ {'port': 'invalid'} = {"port": "invalid"}
  3. try:

    8try:9    port = int(config_data["port"]invalid)10    return port
  4. except ValueError as e: # Chain new exception from origina…

    10    return port11except ValueError as e:12    # Chain new exception from original13    raise RuntimeError("Config load failed") from einvalid literal for int() with base 10: 'invalid'
  5. except RuntimeError as e:

    19    print(f"Port: {port}")20except RuntimeError as e:21    print(f"Exception: {eConfig load failed}")22    print(f"Caused by: {e.__cause__invalid literal for int() with base 10: 'invalid'}")23    print(f"Cause type: {type(e.__cause__invalid literal for int() with base 10: 'invalid').__name__}")2425#@help h1
    outputException: Config load failed
    Caused by: invalid literal for int() with base 10: 'invalid'
    Cause type: ValueError
    Cause type: ValueError
  6. print(" Multi-layer chaining:")

    45print("\nMulti-layer chaining:")
    output
    Multi-layer chaining:
  7. def process_input(text):

    38def process_input(textabc):39    try:40        num = parse_number(text)
  8. try:

    38def process_input(text):39    try:40        num = parse_number(textabc)41        return num * 2
  9. def parse_number(text):

    31# Multiple layers of chaining32def parse_number(textabc):33    try:34        return int(text)
  10. try:

    32def parse_number(text):33    try:34        return int(textabc)35    except ValueError as e:
  11. except ValueError as e:

    34    return int(text)35except ValueError as e:36    raise TypeError("Not a number") from einvalid literal for int() with base 10: 'abc'
  12. except TypeError as e:

    41    return num * 242except TypeError as e:43    raise RuntimeError("Processing failed") from eNot a number
  13. except RuntimeError as e:

    49    print(f"Result: {result}")50except RuntimeError as e:51    print(f"Final exception: {eProcessing failed}")52    print(f"Direct cause: {e.__cause__Not a number}")53    print(f"Root cause: {e.__cause__.__cause__invalid literal for int() with base 10: 'abc'}")5455# Suppress exception chaining
    outputFinal exception: Processing failed
    Direct cause: Not a number
    Root cause: invalid literal for int() with base 10: 'abc'
    Root cause: invalid literal for int() with base 10: 'abc'
  14. print(" Suppressed chain:")

    63print("\nSuppressed chain:")
    output
    Suppressed chain:
  15. except ValueError as e:

    66    operation_with_fallback()67except ValueError as e:68    print(f"Exception: {eInvalid operation}")69    print(f"Has cause: {e.__cause__None is not None}")7071# Practical example: data pipeline
    outputException: Invalid operation
    Has cause: False
    Has cause: False
  16. print(" Pipeline execution:")

    108print("\nPipeline execution:")
    output
    Pipeline execution:
  17. def run_pipeline(source):

    87def run_pipeline(sourcedatabase):88    try:89        try:
  18. try:

    88try:89    try:90        data = fetch_data(sourcedatabase)91    except ConnectionError as e:
  19. def fetch_data(source):

    71# Practical example: data pipeline72def fetch_data(sourcedatabase):73    if source == "database":74        raise ConnectionError("Database unreachable")
  20. if source == "database":

    72def fetch_data(source):73    if sourcedatabase == "database":74        raise ConnectionError("Database unreachable")75    return None
  21. except ConnectionError as e:

    90    data = fetch_data(source)91except ConnectionError as e:92    raise RuntimeError("Fetch stage failed") from eDatabase unreachable
  22. except RuntimeError as e:

    111    run_pipeline("database")112except RuntimeError as e:113    print(f"Pipeline failed: {eFetch stage failed}")114    if e.__cause__:
    outputPipeline failed: Fetch stage failed
  23. if e.__cause__:

    113    print(f"Pipeline failed: {e}")114    if e.__cause__Database unreachable:115        print(f"  Root cause: {type(e.__cause__Database unreachable).__name__}: {e.__cause__}")116117# Check exception context
    output  Root cause: ConnectionError: Database unreachable
      Root cause: ConnectionError: Database unreachable
  24. except ValueError as e:

    130    raise ValueError("Top level") from TypeError("Bottom level")131except ValueError as e:132    analyze_exception(eTop level)
  25. def analyze_exception(exc):

    117# Check exception context118def analyze_exception(excTop level):119    print(f"\nException analysis:")120    print(f"  Type: {type(excTop level).__name__}")121    print(f"  Message: {excTop level}")
    output
    Exception analysis:
      Type: ValueError
      Message: Top level
  26. if exc.__cause__:

    123if exc.__cause__Bottom level:124    print(f"  Has cause: Yes")125    print(f"  Cause: {type(exc.__cause__Bottom level).__name__}: {exc.__cause__}")126else:
    output  Has cause: Yes
      Cause: TypeError: Bottom level
  27. analyze_exception(e)

    131except ValueError as e:132    analyze_exception(eTop level)
  28. main()

    134if __name__ == "__main__":135    main()

raise NewError() from original links exceptions together.

exception chaining `raise X from Y` - new exception X caused by original Y. Full traceback preserved.

Exercise: practical.py

Build a validation system with proper exception raising