Computer Science 15-112, Fall 2011
Class Notes:  Exceptions


  1. Required Reading
  2. Handling Exceptions (try/except)
  3. The Stack Trace
  4. Raising Exceptions (raise)
  5. When not to raise exceptions
  6. Clean-up Actions (finally)
  7. Predefined Clean-up Actions (with)
  8. Built-In Exceptions and the Exception Hierarchy

  1. Required Reading
    1. The Python Tutorial, Chapter 8 (Errors and Exceptions), except 8.5
    2. The Python Standard Library, Chapter 6 (Built-In Exceptions)
       
  2. Handling Exceptions (try/except)
    #1) Simplest form
    
    def isFactor(factor, n):
        return (n % factor == 0)
    try:
        if isFactor(0, 2): print "0 is a factor of 2"
        if isFactor(2, 0): print "2 is a factor of 0"
    except:
        print "We just caught an error"
    
    #2) With Exception information
    
    try:
        if isFactor(0, 2): print "0 is a factor of 2"
        if isFactor(2, 0): print "2 is a factor of 0"
    except Exception as error:
        print "We just caught this error:", error
    
    #3) Catching specific exception types
        
    try:
        prompt = "Enter a number to invert: "
        n = float(raw_input(prompt)) # non-float ==> ValueError
        inverse = 1/n                # n==0 ==> ZeroDivisionError
        if (n == 42): ruhRoh()       # no such function!
        print "The inverse of", n, "is", inverse
    except ZeroDivisionError:
        print "Cannot divide by 0."
    except ValueError:
        print "You did not enter a number!"
    except Exception as error:
        print "Unknown error:", error
        print "   Error type:", type(error)
  3. The Stack Trace
    #1) Buggy code that crashes (look at stack trace for clues)
    
    # intentionally buggy code here!
    def isFactor(factor, n):
        return (n % factor == 0)
    
    def digitFactors(n):
        #returns the number of digits of n that are factors of n
        count = 0
        while (n > 0):
            digit = n % 10
            n /= 10
            if isFactor(n, digit):
                #found another digit that divides n
                count += 1
        return count
    
    #8 is a factor of 80, but 0 is not, so digitFactors(80) should return 1
    print digitFactors(80) #crashes
    
    #2) Once again, but catching the exception to print out more information
    #  (but we lost the stack trace!)
    
    def isFactor(factor, n):
        return (n % factor == 0)
    
    def digitFactors(n):
        #returns the number of digits of n that are factors of n
        count = 0
        while (n > 0):
            digit = n % 10
            n /= 10
            try:
                if isFactor(n, digit):
                    #found another digit that divides n
                    count += 1
            except:
                print "crashed on this input:", n, digit
        return count
    
    print digitFactors(80) #does not crash (exception quietly caught)
    
    #3) Yet again, this time explicitly printing the stack trace, and not crashing!
    
    import traceback, sys
    
    def isFactor(factor, n):
        return (n % factor == 0)
    
    def digitFactors(n):
        #returns the number of digits of n that are factors of n
        count = 0
        while (n > 0):
            digit = n % 10
            n /= 10
            try:
                if isFactor(n, digit):
                    #found another digit that divides n
                    count += 1
            except Exception as error:
                print "crashed on this input:", n, digit
                print "Error:", error
                traceback.print_exc(file=sys.stdout)
        return count
    
    print digitFactors(80) #does not crash, but prints stack trace!
    
    #4) Once more, this time re-raising the exception, so yet again crashing
    
    def isFactor(factor, n):
        return (n % factor == 0)
    
    def digitFactors(n):
        #returns the number of digits of n that are factors of n
        count = 0
        while (n > 0):
            digit = n % 10
            n /= 10
            try:
                if isFactor(n, digit):
                    #found another digit that divides n
                    count += 1
            except:
                print "crashed on this input:", n, digit
                raise
        return count
    
    print digitFactors(80) #crashes!
  4. Raising Exceptions (raise)
    def f(n):
        if (n == 42):
            raise Exception("I take exception to 42.")
        return n+1
    
    try:
        print f(5)
        print f(42)
        print f(50)
    except Exception as error:
        print "Caught exception:", error
  5. When not to raise exceptions
    #1) Unnecessary raise
    
    def isFactor(factor, n):
        if (factor == 0):
            raise Exception("Cannot divide by 0")  #wrong!
        return (n % factor == 0)
    
    print isFactor(0, 8)
    
    #2) Here's why...
    
    def isFactor(factor, n):
        return (n % factor == 0)
    
    print isFactor(0, 8)
    
    #3) But you may use assert here, if this violates a pre-condition
    #   (which it presumably does)
    
    def isFactor(factor, n):
        assert(factor != 0)
        return (n % factor == 0)
    
    print isFactor(0, 8)
  6. Clean-Up Actions (finally)
    #1) Wrong way
    
    filename = "non-existent-file.txt"
    try:
        file = open(filename, "r")
        print filename, "has", len(file.readlines()), "lines"
    except IOError:
        print "Cannot open file", filename
    finally:
        file.close()  #error: file not defined here!
    
    #2) Right way
    
    filename = "non-existent-file.txt"
    file = None
    try:
        file = open(filename, "r")
        print filename, "has", len(file.readlines()), "lines"
    except IOError:
        print "Cannot open file", filename
    finally:
        if (file != None): file.close()  #ahh, that's better
  7. Predefined Clean-up Actions (with)
    filename = "non-existent-file.txt"
    try:
        # using "with", no need for file.close() in finally clause
        with open(filename, "r") as file:
            print filename, "has", len(file.readlines()), "lines"
    except IOError:
        print "Cannot open file", filename
  8. Built-In Exception and the Exception Hierarchy
    1. Exception Descriptions
    2. The Exception Hierarchy

carpe diem   -   carpe diem   -   carpe diem   -   carpe diem   -   carpe diem   -   carpe diem   -   carpe diem   -   carpe diem   -   carpe diem