A finally block runs as a function leaves a try statement, including when an exception is pending.
Python interview: a return from finally replaces a pending exception
Operation contract
The unsafe function deliberately returns from finally. That return hides the ValueError raised in try, so the caller receives a success-looking marker. The safe function performs cleanup in finally without returning; its ValueError still reaches the caller. The two traces make the failure mode observable without relying on a real external resource.
Failure and ownership boundary
Cleanup itself can raise and replace an earlier error, so production cleanup needs an explicit failure policy. This exercise demonstrates language control flow only. Python exceptions: translate an input error without hiding its cause, Python context manager exit: returning true can hide a failure and Python ExitStack: unwind partially acquired resources cover related ownership choices.
Working program
events = []
def unsafe_result():
try:
raise ValueError("invalid receipt")
finally:
events.append("unsafe cleaned")
return "accepted"
def safe_result():
try:
raise ValueError("invalid receipt")
finally:
events.append("safe cleaned")
print("unsafe:", unsafe_result())
try:
safe_result()
except ValueError:
print("safe propagated")
print("events:", events)Output
unsafe: accepted
safe propagated
events: ['unsafe cleaned', 'safe cleaned']Costs and limits
The control-flow example performs constant application work. Real cleanup may have I/O cost and failure of its own. A return in finally changes the caller-visible result even though the try body already failed.
Common Mistakes
- Do not return from finally when callers need a pending failure.
- A cleanup failure can also displace the original exception.
Connected lessons
Python exceptions: translate an input error without hiding its cause, Python context manager exit: returning true can hide a failure, Python ExitStack: unwind partially acquired resources.
