8. Exceptions

 

 

 

 

Why use exceptions?

 

 

     Error handling

     Event notification

     Special-case handling

     Unusual control-flows

 

 

 

 

Exception topics

 

 

     The basics

     Exception idioms

     Exception catching modes

     Class exceptions

     Exception gotchas

 


 

Exception basics

 

 

     A high-level control flow device

     try statements catch exceptions

     raise statements trigger exceptions

     Excep­­tions are raised by Python or programs

 

 

 

Basic forms

 

 

      Python 2.5+: except/finally can now be mixed

      Python 2.5+: with/as context managers

      Python 3.X: “except E as X”, “raise from E”

 

 

     try/except/else

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

 

 

     try/finally

try:

    <statements>

finally:

    <statements>        # always run 'on the way out'

 

 

     raise

raise <name>            # manually trigger an exception

raise <name>, <data>    # pass extra data to catcher too

 

 

     assert

assert <test>, <message>   

 

# if not test: raise AssertionError, message

 

 

     with/as context managers (2.5+)

# alternative to common try/finally idioms

 

from __future__ import with_statement     # till 2.6

 

with open('/etc/passwd', 'r') as f:       # auto-closed after with

    for line in f:                        # even if exception in block

        print line

        ... more processing code ...

 

lock = threading.Lock()

with lock:                                # auto acquired, released

    # critical section of code            # classes may define managers

 

 

 

 

First examples

 

 

 

Builtin exceptions

 

     Python triggers builtin exceptions on errors

     Displays message at top-level if not caught

 

 

def kaboom(list, n):

    print list[n]            # trigger IndexError

 

try:

    kaboom([0, 1, 2], 3)

except IndexError:           # catch exception here

    print 'Hello world!'    

 

 

 

 

User-defined exceptions

 

     Python (and C) programs raise exceptions too

     User-defined exceptions are objects

 

 

MyError = "my error"

 

def stuff(file):

    raise MyError

 

file = open('data', 'r')     # open a file

try:

    stuff(file)              # raises exception

finally:

    file.close()             # always close file

 


 

Exception idioms

 

 

     EOFError sometimes signals end-of-file

 

while 1:

    try:

        line = raw_input()     # read from stdin

    except EOFError:

        break

    else:

        <process next ‘line’ here>

 

 

 

     Searches sometimes signal success by ‘raise’

Found = "Item found"

 

def searcher():

    <raise Found or return>

 

try:

    searcher()

except Found:

    <success>

else:

    <failure>

 

 

 

     Outer ‘try’ statements can be used to debug code

try:

    <run program>       

except:              # all uncaught exceptions come here

    import sys

    print 'uncaught!', sys.exc_info()[:2]     # type, value

 


 

Exception catching modes

 

 

     Try statements nest (are stacked) at runtime

     Python selects first clause that matches exception

     Try blocks can contain a variety of clauses

     Multiple excepts: catch 1-of-N exceptions

     Try can contain ‘except’ or ‘finally’, but not both

 

 

 

 

Try block clauses

 

 

Operation

Interpretation

except:

catch all exception types

except name:

catch a specific exception only

except name, value:

2.X: catch exception and its extra data

except name as value:

3.X: catch exception and its instance

except (name1, name2):

catch any of the listed exceptions

else:

run block if no exceptions raised

finally:

always perform block, exception or not

  


 

     Exceptions nest at run-time

      Runs most recent matching except clause

 

file: nestexc.py

 

def action2():

    print 1 + []           # generate TypeError

 

def action1():

    try:

        action2()

    except TypeError:      # most recent matching try

        print 'inner try'

 

try:

    action1()

except TypeError:          # here iff action1 re-raises

    print 'outer try'

 

 

% python nestexc.py

inner try

 

 

 

 

     Catching 1-of-N exceptions

      Runs first match: top-to-bottom, left-to-right

      See manuals or reference text for a complete list

 

try:
    action()

except NameError:

    ...

except IndexError

    ...

except KeyError:
    ...

except (AttributeError, TypeError, SyntaxError):

    ...

else:

    ...


 

     ‘finally’ clause executed on the way out

      useful for ‘cleanup’ actions: closing files,...

      block executed whether exception occurs or not

      Python propagates exception after block finishes

      but exception lost if finally runs a raise, return, or break

 

 

file: finally.py


def divide(x, y):

    return x / y          # divide-by-zero error?

 

def tester(y):

    try:

        print divide(8, y)

    finally:

        print 'on the way out...'

 

print '\nTest 1:'; tester(2)

print '\nTest 2:'; tester(0)     # trigger error

 

 

 

% python finally.py

 

Test 1:

4

on the way out...

 

Test 2:

on the way out...

Traceback (innermost last):

  File "finally.py", line 11, in ?

    print 'Test 2:'; tester(0)

  File "finally.py", line 6, in tester

    print divide(8, y)

  File "finally.py", line 2, in divide

    return x / y          # divide-by-zero error?

ZeroDivisionError: integer division or modulo

 

 

 

 

 

     Optional data

      Provides extra exception details

      Python passes None if no explicit data

 

 

 

file: helloexc.py

 

myException = 'Error'     # string object

 

def raiser1():

    raise myException, "hello"     # raise, pass data

 

def raiser2():

    raise myException              # raise, None implied

 

def tryer(func):

    try:

        func()

    except myException, extraInfo:

        print 'got this:', extraInfo

 

 

 

% python

>>> from helloexc import *

>>> tryer(raiser1)

got this: hello

>>> tryer(raiser2)

got this: None

 

 

 

 


 

Class exceptions

 

 

      Should use classes today: only option in 3.X, per BDFL

      Useful for catching categories of exceptions

      String exception match: same object (‘is’ identity)

      Class exception match: named class or subclass of it

      Class exceptions support exception hierarchies

 

 

 

General raise forms

 

raise string            # matches same string object

raise string, data      # optional extra data (default=None)

raise class, instance   # matches class or its superclass

raise instance          # = instance.__class__, instance

 

 

 

Example

 

file: classexc.py

class Super:      pass

class Sub(Super): pass

 

def raiser1():

    X = Super()           # raise listed class instance

    raise X

 

def raiser2():

    X = Sub()             # raise instance of subclass

    raise X

 

for func in (raiser1, raiser2):

    try:

        func()

    except Super:                # match Super or a subclass

        import sys

        print 'caught:', sys.exc_info()[0]

 

% python classexc.py

caught: <class Super at 770580>

caught: <class Sub at 7707f0>


 

 

Example: numeric library

 

class NumErr: pass

class Divzero(NumErr): pass

class Oflow(NumErr): pass

raise DivZero()

 

import mathlib

try:

    mathlib.func(…)

except mathlib.NumErr:

    …report and recover…


 

 

Example: using raised instance

 

 

class MyBad:

    def __init__(self, file, line):

        self.file = file

        self.line = line

    def display(self):

        print self.file * 2

 

def parser():

    raise MyBad('spam.txt', 5)

 

try:

    parser()

except MyBad, X:

    print X.file, X.line

    X.display()

 

 

 

# built-in file error numbers

 

def parser():

    open('nonesuch')

 

try:

    parser()

except IOError, X:

    print X.errno, '=>', X.strerror

 

 

 

 

Exception gotchas

 

 

 

What to wrap in a try statement?

 

      Things that commonly fail: files, sockets, etc.

      Calls to large functions, not code inside the function

      Anything that shouldn’t kill your script

      Simple top-level scripts often should die on errors

      See also atexit module for shutdown time actions

 

 

 

Catching too much?

 

 

      Empty except clauses catch everything

      But may intercept error expected elsewhere

 

 

try:

    [...]

except:

    [...]   # everything comes here: even sys.exit()!

 

 

 

 

Catching too little?

 

 

      Specific except clauses only catch listed exceptions

      But need to be updated if add new exceptions later

      Class exceptions would help here: category name

 

 

try:

    [...]

except (myerror1, myerror2):   # what if I add a myerror3?

    [...]  # non-errors

else:

    [...]  # assumed to be an error

 

 

 

 

Solution: exception protocol design

 


 

 

 

 

Lab Session 7

 

Click here to go to lab exercises

Click here to go to exercise solutions

Click here to go to solution source files

Click here to go to lecture example files