liveBook · Manning

Exception propagation

As explained in The Well-Grounded Python Developer: How the pros use Python and Flask

Exception propagation is the movement of an unhandled exception upward through the call stack until a suitable handling context is reached.

When the current function cannot make a useful decision, let the unhandled exception move upward through the call stack. A higher-level context may know whether to retry, choose an alternative path, inform the user, or terminate safely. section 5.2.2

Propagation is not the same as silence. Catch specific exceptions only where corrective action is possible, and preserve useful information through user feedback or stack-trace logging. section 5.2.2 section 5.2.4

A practical propagation pattern is to:

  • Leave lower-level functions unchanged when they cannot resolve the failure. section 5.2.4
  • Catch narrow exception types at a higher level where retry or user guidance makes sense. section 5.2.4
  • Log unhandled failures with their stack traces, while handled events may use informational logging or need no error log. section 5.2.2

Retrying invalid division input

func_c() performs the division, func_b() passes the values through, and func_a() receives the propagated failure. An outer loop catches ZeroDivisionError and ValueError, explains the problem, and retries until the operation succeeds. section 5.2.4

def func_a():
    dividend = float(input("Enter a dividend value: "))
    divisor = float(input("Enter a divisor value: "))
    result = func_b(dividend, divisor)
    print(f"dividing {dividend} by {divisor} = {result}")
 
def func_b(dividend: float, divisor: float) -> float:
    return func_c(dividend, divisor)
 
def func_c(dividend: float, divisor: float) -> float:
    return dividend / divisor
 
successful = False
while not successful:
    try:
        func_a()
    except ZeroDivisionError as e:
        print(f"The divisor can't be a zero value, error:", e)
    except ValueError as e:
        print(
            f"The dividend and divisor must be a string that represents a
             number, error:",
            e,
        )
    else:
        successful = True
    finally:
        if successful:
            print("Thanks for running the program")
        else:
            print("Try entering a dividend and divisor again")

Browse the complete call-stack example, including specific handlers, retry control, success handling, and final user messaging. section 5.2.4

How The Well-Grounded Python Developer: How the pros use Python and Flask covers it

How deep it goes
Explains call-stack movement, handling boundaries, retry behavior, logging, and the dangers of broad silent catches.
Examples
Demonstrates nested division functions, ZeroDivisionError and ValueError handling, retry prompts, and stack-trace logging.
What you can do after
Apply it by allowing unresolved failures upward, catching specific exceptions at useful boundaries, and avoiding silent failure.
How it connects
Connects exception propagation with call stacks, exception handling, retry logic, user feedback, and logging.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of The Well-Grounded Python Developer: How the pros use Python and Flask. The section links point to the parts of the book it draws on.