6.2 Exception Propagation
When an exception is raised, the
exception-propagation mechanism takes control. The normal control
flow of the program stops, and Python looks for a suitable exception
handler. Python's try statement
establishes exception handlers via its except
clauses. The handlers deal with exceptions raised in the body of the
try clause, as well as exceptions that propagate
from any of the functions called by that code, directly or
indirectly. If an exception is raised within a try
clause that has an applicable except handler, the
try clause terminates and the handler executes.
When the handler finishes, execution continues with the statement
after the try statement.
If the statement raising the exception is not within a
try clause that has an applicable handler, the
function containing the statement terminates, and the exception
propagates upward to the statement that called the function. If the
call to the terminated function is within a try
clause that has an applicable handler, that try
clause terminates, and the handler executes. Otherwise, the function
containing the call terminates, and the propagation process repeats,
unwinding the stack of function calls until an applicable handler is
found.
If Python cannot find such a handler, by
default the program prints an error message to the standard error
stream (the file sys.stderr). The error message
includes a traceback that gives details about functions terminated
during propagation. You can change Python's default
error-reporting behavior by setting sys.excepthook
(covered in Chapter 8). After error reporting,
Python goes back to the interactive session, if any, or terminates if
no interactive session is active. When the exception class is
SystemExit, termination is silent and includes the
interactive session, if any.
Here are some functions that we can use to see exception propagation
at work.
def f( ):
print "in f, before 1/0"
1/0 # raises a ZeroDivisionError exception
print "in f, after 1/0"
def g( ):
print "in g, before f( )"
f( )
print "in g, after f( )"
def h( ):
print "in h, before g( )"
try:
g( )
print "in h, after g( )"
except ZeroDivisionError:
print "ZD exception caught"
print "function h ends"
Calling the h function has the following results:
>>> h( )
in h, before g( )
in g, before f( )
in f, before 1/0
ZD exception caught
function h ends
Function h establishes a try
statement and calls function g within the
try clause. g, in turn, calls
f, which performs a division by
0, raising an exception of class
ZeroDivisionError. The exception propagates all
the way back to the except clause in
h. Functions f and
g terminate during the exception propagation
phase, which is why neither of their
"after" messages is printed. The
execution of h's
try clause also terminates during the exception
propagation phase, so its "after"
message isn't printed either. Execution continues
after the handler, at the end of
h's
try/except block.
|