Next / Previous / Contents / TCC Help System / NM Tech homepage

23.8. The try statement: Anticipate exceptions

The purpose of a “try” construct is to specify what actions to take in the event of errors. For an introduction to Python's exception mechanism, see Section 25, “Exceptions: Error signaling and handling”.

When an exception is raised, two items are associated with it:

Here is the most general form of a try construct. Symbols like B0 and B1 represent indented blocks of statements. Each except clause names some exception class Ei (or a tuple of exception classes), and optionally a variable vi that will be set to the exception value.

try:
    B0
except E1 [as v1]:
    B1
except E2 [as v2]:
    B2
except ...:
    ...
else:
    Be
finally:
    Bf

The else: and finally: blocks are optional. There must be at least one except block, but there may be any number.

Here is a simplified description of the execution of a try block in general:

  1. If B0 executes without raising any exceptions, the else block Be is executed, then the finally block Bf.

  2. If the execution of block B0 raises some exception with type E0, that type is compared against each except clause until one of them matches the raised exception or there are no more except clauses.

    The matching condition is slightly complicated: for some clause “except Ei as vi:”, expression Ei is either an exception class or a tuple of exception classes.

    • If Ei is a single class, it is considered a match if E0 is either the same class as Ei or a subclass of Ei.

    • If Ei is a tuple of exception classes, the raised exception is compared to each to see if it is the same class or a subclass, as in the single-class case.

    If multiple except clauses match, the first matching clause is said to handle the exception. The corresponding variable vi (if present) is bound to the raised exception instance, and control passes to the corresponding block Bi.

  3. If there is a finally clause, it is executed, whether the exception was caught or not. If the exception was not caught, it is re-raised after the end of the finally clause.

Examples:

>>> try:
...     raise ValueError("Strike three!")
... except IOError as x:
...     print "I/O error caught:", x
... except ValueError as x:
...     print "Value error caught:", x
... except SyntaxError as x:
...     print "Syntax error caught:", x
... else:
...     print "This is the else clause"
... finally:
...     print "This is the finally clause"
... 
Value error caught: Strike three!
This is the finally clause
>>> try:
...     raise ValueError("Uncaught!")
... except IOError as x:
...     print "I/O error:", x
... else:
...     print "This is the else clause"
... finally:
...     print "This is the finally clause"
... 
This is the finally clause
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ValueError: Uncaught!
>>> try:
...     print "No exceptions raised"
... except ValueError as x:
...     print "ValueError:", x
... else:
...     print "This is the else clause"
... finally:
...     print "This is the finally clause"
... 
No exceptions raised
This is the else clause
This is the finally clause

For those of you who are interested in the gory details, the fun begins when a second or even a third exception is raised inside an except, else, or finally clause. The results are well-defined, and here is a pseudocode description of the edge cases. In this procedure, we'll use two internal variables named pending and detail.

  1. Set pending to None.

  2. Attempt to execute block B0. If this block raises an exception E0 with detail d0, set pending to E0 and set detail to d0.

  3. If pending is None, go to Step 8.

  4. Find the first block except Ei, vi: such that issubclass(E0, Ei ).

    If there is no such match, go to Step 10.

  5. Set vi to detail.

  6. Attempt to execute block Bi.

    If this block raises some new exception En with detail dn, set pending to En and set detail to dn.

    However, if block Bi executes without exception, set pending to None. In this case, the original exception is said to have been caught or handled.

  7. Go to Step 10.

  8. If there is no else: clause, go to Step 10.

  9. Attempt to execute the else: block Be.

    If this block raises some new exception En with detail dn, set pending to En and set detail to dn.

  10. If there is no finally: clause, proceed to Step 12.

  11. Attempt to execute the finally: block Ef.

    If this block raises some new exception En with detail dn, set pending to En and set detail to dn.

  12. If pending is not None, re-raise the exception as in this statement:

    raise pending, detail
    

    If pending is None, fall through to the statement following the try: block.