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
