Computer Science 15-112, Spring 2012
Class Notes:  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")
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")
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: