Python exceptions are a high-level control flow device. They may be
raised either by Python or by our programs; in both cases, they may
be caught by try
statements. Python
try
statements come in two flavors—one that
handles exceptions and one that executes finalization code whether
exceptions occur or not.
The try
is another compound statement; its most
complete form is sketched below. It starts with a
try
header line followed by a block of indented
statements, then one or more optional except
clauses that name exceptions to be caught, and an optional
else
clause at the end:
try: <statements
> # run/call actions except <name
>: <statements
> # if 'name' raised during try block except <name>, <data
>: <statements
> # if 'name' raised; get extra data else: <statements
> # if no exception was raised
Here’s how try
statements work. When a
try
statement is started, Python marks the current
program context, so it can come back if an exception occurs. The
statements nested under the try
header are run
first; what happens next depends on whether exceptions are raised
while the try
block’s statements are running
or not:
If an exception occurs while the try
block’s
statements are running, Python jumps back to the
try
and runs the statements under the first
except
clause that matches the raised exception.
Control continues past the entire try
statement
after the except
block runs (unless the
except
block raises another exception).
If an exception happens in the try
block and no
except
clause matches, the exception is propagated
up to a try
that was entered earlier in the
program, or to the top level of the process (which makes Python kill
the program and print a default error message).
If no exception occurs while the statements under the
try
header run, Python runs the statements under
the else
line (if present), and control then
resumes past the entire try
statement.
In other words, except
clauses catch exceptions
that happen while the try
block is running, and
the else
clause is run only if no exceptions
happen while the try
block runs. The
except
clauses are very focused exception
handlers; they catch exceptions that occur only within the statements
in the associated try
block. However, since the
try
block’s statements can call functions
elsewhere in a program, the source of an exception may be outside the
try
.
The other flavor of the try
statement is a
specialization and has to do with finalization actions. If a
finally
clause is used in a
try
, its block of statements are always run by
Python “on the way out,” whether an exception occurred
while the try
block was running or not:
If no exception occurs, Python runs the try
block,
then the finally
block, and then continues
execution past the entire try
statement.
If an exception does occur during the try
block’s run, Python comes back and runs the
finally
block, but then propagates the exception
to a higher try
(or the top level); control
doesn’t continue past the try
statement.
The try/finally
form is useful when you want to be
completely sure that an action happens after some code runs,
regardless of the exception behavior of the program; we’ll see
an example in a moment. The finally
clause
can’t be used in the same try
statement as
except
and else
, so they are
best thought of as two different statements:
try: <statements
> finally: <statements
> # always run "on the way out"
To trigger
exceptions,
you need to code raise
statements. Their general
form is simple: the word raise
followed by the
name of the exception to be raised. You can also pass an extra data
item (an object) along with the exception, by listing it after the
exception name. If extra data is passed, it can be caught in a
try
by listing an assignment target to receive it:
except
name,
data
:
raise <name
> # manually trigger an exception raise <name
>, <data
> # pass extra data to catcher too
So what’s an exception name? It might be the name of a built-in
exception from the built-in scope (e.g.,
IndexError
), or the name of an arbitrary string
object you’ve assigned in your program. It can also reference a
class or class instance; this form generalizes
raise
statements, but we’ll postpone this
topic till later in this chapter. Exceptions are identified by
objects, and at most one is active at any given time. Once caught by
an except
clause, an exception dies (won’t
propagate to another try
), unless reraised by a
raise
or error.
Exceptions are simpler than they seem. Since control flow through a program is easier to capture in Python than in English, let’s look at some simple examples that illustrate exception basics.
As mentioned,
exceptions not caught by try
statements reach the
top level of a Python process and run Python’s default
exception-handling logic. By default, Python terminates the running
program and prints an error message describing the exception, showing
where the program was when the exception occurred. For example,
running the following module generates a divide-by-zero exception;
since the program ignores it, Python kills the program and prints:
%cat bad.py
def gobad(x, y): return x / y def gosouth(x): print gobad(x, 0) gosouth(1) %python bad.py
Traceback (innermost last): File "bad.py", line 7, in ? gosouth(1) File "bad.py", line 5, in gosouth print gobad(x, 0) File "bad.py", line 2, in gobad return x / y ZeroDivisionError: integer division or modulo
When an uncaught exception occurs, Python ends the program, and
prints a stack trace and the name and extra data of the exception
that was raised. The stack trace shows the
filename, line number, and source code, for each function active when
the exception occurred, from oldest to newest. For example, you can
see that the bad divide happens at the lowest entry in the
trace—line 2 of file bad.py, a
return
statement.
Because Python reports almost all errors at runtime by raising
exceptions, exceptions are intimately bound up with the idea of error
handling in general. For instance, if you’ve worked through the
examples, you’ve almost certainly seen an exception or two
along the way (even typos usually generate a
SyntaxError
exception). By default, you get a
useful error display like the one above, which helps track down the
problem. For more heavy-duty debugging jobs, you can catch exceptions
with try statements.[52]
If you don’t want your program terminated when an exception is
raised by Python, simply catch it by wrapping program logic in a
try
. For example, the following code catches the
IndexError
Python raises when the list is indexed
out of bounds (remember that list indexes are zero-based offsets; 3
is past the end):
def kaboom(list, n): print list[n] # trigger IndexError try: kaboom([0, 1, 2], 3) except IndexError: # catch exception here print 'Hello world!'
When the exception occurs in function kaboom
,
control jumps to the try
statement’s
except
clause, which prints a message. Since an
exception is “dead” after it’s been caught, the
program continues past the whole try
, rather than
being terminated by Python. In effect, you process and ignore the
error.
Python programs can raise exceptions of their own too, using the
raise
statement. In their simplest form,
user-defined exceptions are usually string objects, like the one
MyError
is assigned to in the following:
MyError = "my error" def stuff(file): raise MyError file = open('data', 'r') # open an existing file try: stuff(file) # raises exception finally: file.close() # always close file
User-defined exceptions are caught with try
statements just like built-in exceptions. Here, we’ve wrapped a
call to a file-processing function in a try
with a
finally
clause, to make sure that the file is
always closed, whether the function triggers an exception or not.
This particular function isn’t all that useful (it just raises
an exception!), but wrapping calls in try/finally
statements is a good way to ensure that your closing-time activities
always run.
[52] You can also use Python’s
standard debugger, pdb
, to isolate problems. Like
C debuggers such as dbx
and
gdb
, pdb
lets you step through
Python programs line by line, inspect variable values, set
breakpoints, and so on. pdb
is shipped with Python
as a standard module and is written in Python. See Python’s
library manual or other Python texts for information on
pdb
usage.