Asserts and Error Handling
Lecture 11
Announcements for Today
(Optional) Videos
• Lesson 13, 14 for today• Videos 15.1-15.7 next time
Assignments
• Finishing Assignment 1§ We are going to score it§ Get one more chance Sun.
• Assignment 2 due tonight§ Will grade it this weekend§ Solutions posted on Monday
• Assignment 3 due next week§ Before you leave for break§ Same “length” as A1
9/30/21 Asserts & Error Handling 2
• Prelim, Oct 19th 7:30-9:00§ Material up October 5th§ Study guide next week
• Conflict with Prelim time?§ Submit to Prelim 1 Conflict
assignment on CMS§ Do not submit if no conflict
Using Color Objects in A3
• New classes in introcs§ RGB, CMYK, and HSL
• Each has its own attributes§ RGB: red, blue, green§ CMYK: cyan, magenta,
yellow, black§ HSL: hue, saturation, light
• Attributes have invariants§ Limits the attribute values§ Example: red is int in 0..255§ Get an error if you violate
>>> import introcs>>> c = introcs.RGB(128,0,0)>>> r = c.red>>> c.red = 500 # out of rangeAssertionError: 500 outside [0,255]
9/30/21 Asserts & Error Handling 3
id1c
128r
id1
red 128
green 0
blue 0
RGB
>>> import introcs>>> c = introcs.RGB(128,0,0)>>> r = c.red>>> c.red = 500 # out of rangeAssertionError: 500 outside [0,255]
Using Color Objects in A3
• New classes in introcs§ RGB, CMYK, and HSL
• Each has its own attributes§ RGB: red, blue, green§ CMYK: cyan, magenta,
yellow, black§ HSL: hue, saturation, light
• Attributes have invariants§ Limits the attribute values§ Example: red is int in 0..255§ Get an error if you violate
9/30/21 Asserts & Error Handling 4
id1c
128r
id1
red 128
green 0
blue 0
RGB
Constructor function.To make a new color.
AccessingAttribute
Recall: The Call Stack
• Functions are stacked§ Cannot remove one above
w/o removing one below§ Sometimes draw bottom up
(better fits the metaphor)
• Stack represents memory as a high water mark§ Must have enough to keep the
entire stack in memory§ Error if cannot hold stack
9/30/21 Asserts & Error Handling 5
Frame 1
Frame 2
Frame 3
Frame 4
Frame 6Frame 5
calls
calls
calls
calls
Error Messages
Not An Error Message
ZeroDivisionError: division by zero
An Error Message
Traceback (most recent call last):File "error.py", line 41, in <module>print(function_1(1,0))
File "error.py", line 16, in function_1return function_2(x,y)
File "error.py", line 26, in function_2return function_3(x,y)
File "error.py", line 36, in function_3return x/y
ZeroDivisionError: division by zero
Everything starting with the Traceback
9/30/21 Asserts & Error Handling 6
Errors and the Call Stack
# error.py
def function_1(x,y):return function_2(x,y)
def function_2(x,y):return function_3(x,y)
def function_3(x,y):return x/y # crash here
if __name__ == '__main__':print(function_1(1,0))
9/30/21 Asserts & Error Handling 7
calls
calls
calls
Errors and the Call Stack
# error.py
def function_1(x,y):return function_2(x,y)
def function_2(x,y):return function_3(x,y)
def function_3(x,y):return x/y # crash here
if __name__ == '__main__':print(function_1(1,0))
Crashes produce the call stack:Traceback (most recent call last):File "error.py", line 20, in <module>print(function_1(1,0))
File "error.py", line 8, in function_1return function_2(x,y)
File "error.py", line 12, in function_2return function_3(x,y)
File "error.py", line 16, in function_3return x/y
9/30/21 Asserts & Error Handling 8
Make sure you can see line numbers in Atom.
Errors and the Call Stack
# error.py
def function_1(x,y):return function_2(x,y)
def function_2(x,y):return function_3(x,y)
def function_3(x,y):return x/y # crash here
if __name__ == '__main__':print function_1(1,0)
9/30/21 Asserts & Error Handling 9
Crashes produce the call stack:Traceback (most recent call last):File "error.py", line 20, in <module>print(function_1(1,0))
File "error.py", line 8, in function_1return function_2(x,y)
File "error.py", line 12, in function_2return function_3(x,y)
File "error.py", line 16, in function_3return x/y
Make sure you can see line numbers in Atom.
Where error occurred (or where was found)
Script code.Global space
Recall: Assigning Responsibility
FunctionDeveloper 1 Developer 2
Defines Calls
BROKEN
Whose fault is it?Who must fix it?
9/30/21 Asserts & Error Handling 10
Determining Responsibility
def function_1(x,y):"""Returns: result of function_2
Precondition: x, y numbers"""return function_2(x,y)
def function_2(x,y):"""Returns: x divided by y
Precondition: x, y numbers"""return x/y
print(function_1(1,0))
Traceback (most recent call last):
File "error1.py", line 32, in <module>print(function_1(1,0))
File "error1.py", line 18, in function_1return function_2(x,y)
File "error1.py", line 28, in function_2return x/y
ZeroDivisionError: division by zeroWhere is the error?
9/30/21 Asserts & Error Handling 11
Approaching the Error Message
• Start from the top• Look at function call
§ Examine arguments§ (Print if you have to)§ Verify preconditions
• Violation? Error found§ Else go to next call§ Continue until bottom
Traceback (most recent call last):
File "error1.py", line 32, in <module>print(function_1(1,0))
File "error1.py", line 18, in function_1return function_2(x,y)
File "error1.py", line 28, in function_2return x/y
ZeroDivisionError: division by zero
9/30/21 Asserts & Error Handling 12
Determining Responsibility
def function_1(x,y):"""Returns: result of function_2
Precondition: x, y numbers"""return function_2(x,y)
def function_2(x,y):"""Returns: x divided by y
Precondition: x, y numbers"""return x/y
print(function_1(1,0))
Traceback (most recent call last):
File "error1.py", line 32, in <module>print(function_1(1,0))
File "error1.py", line 18, in function_1return function_2(x,y)
File "error1.py", line 28, in function_2return x/y
ZeroDivisionError: division by zeroWhere is the error?
A
B
C
9/30/21 Asserts & Error Handling 13
Determining Responsibility
def function_1(x,y):"""Returns: result of function_2
Precondition: x, y numbers"""return function_2(x,y)
def function_2(x,y):"""Returns: x divided by y
Precondition: x, y numbers"""return x/y
print(function_1(1,0))
Traceback (most recent call last):
File "error1.py", line 32, in <module>print(function_1(1,0))
File "error1.py", line 18, in function_1return function_2(x,y)
File "error1.py", line 28, in function_2return x/y
ZeroDivisionError: division by zero
Error!
9/30/21 Asserts & Error Handling 14
Determining Responsibility
def function_1(x,y):"""Returns: result of function_2
Precondition: x, y numbers"""return function_2(x,y)
def function_2(x,y):"""Returns: x divided by y
Precondition: x, y numbs, y > 0"""return x/y
print(function_1(1,0))
Traceback (most recent call last):
File "error1.py", line 32, in <module>print(function_1(1,0))
File "error1.py", line 18, in function_1return function_2(x,y)
File "error1.py", line 28, in function_2return x/y
ZeroDivisionError: division by zeroWhere is the error?
A
B
C
9/30/21 Asserts & Error Handling 15
Determining Responsibility
def function_1(x,y):"""Returns: result of function_2
Precondition: x, y numbers"""return function_2(x,y)
def function_2(x,y):"""Returns: x divided by y
Precondition: x, y numbs, y > 0"""return x/y
print(function_1(1,0))
Traceback (most recent call last):
File "error1.py", line 32, in <module>print(function_1(1,0))
File "error1.py", line 18, in function_1return function_2(x,y)
File "error1.py", line 28, in function_2return x/y
ZeroDivisionError: division by zero
Error!
9/30/21 Asserts & Error Handling 16
Determining Responsibility
def function_1(x,y):"""Returns: result of function_2
Precondition: x, y numbs, y > 0"""return function_2(x,y)
def function_2(x,y):"""Returns: x divided by y
Precondition: x, y numbs, y > 0"""return x/y
print(function_1(1,0))
Traceback (most recent call last):
File "error1.py", line 32, in <module>print(function_1(1,0))
File "error1.py", line 18, in function_1return function_2(x,y)
File "error1.py", line 28, in function_2return x/y
ZeroDivisionError: division by zero
Error!
9/30/21 Asserts & Error Handling 17
Aiding the Search Process
• Responsibility is “outside of Python”§ Have to step through the error message§ Compare to specification at each step
• How can we make this easier?§ What if we could control the error messages?§ Write responsibility directly into error?§ Then only need to look at error message
• We do this with assert statements9/30/21 Asserts & Error Handling 18
Assert Statements
• Form 1: assert <boolean>§ Does nothing if boolean is True§ Creates an error is boolean is False
• Form 2: assert <boolean>, <string> § Very much like form 2§ But error message includes the string
• Statement to verify a fact is true§ Similar to assert_equals used in unit tests § But more versatile with complete stack trace
9/30/21 Asserts & Error Handling 19
Why Do This?
• Enforce preconditions!§ Put precondition as assert.§ If violate precondition,
the program crashes• Provided code in A3
uses asserts heavily§ First slide of lecture!
def exchange(from_c, to_c, amt)"""Returns: amt from exchange
Precondition: amt a float…"""assert type(amt) == float…
9/30/21 Asserts & Error Handling 20
assert <boolean> # Creates error if <boolean> falseassert <boolean>, <string> # As above, but displays <String>
Will do yourself in A4.
Example: Anglicizing an Integer
def anglicize(n):"""Returns: the anglicization of int n.
Precondition: n an int, 0 < n < 1,000,000"""assert type(n) == int, repr(n)+' is not an int'assert 0 < n and n < 1000000, repr(n)+' is out of range'# Implement method here…
9/30/21 Asserts & Error Handling 21
Example: Anglicizing an Integer
def anglicize(n):"""Returns: the anglicization of int n.
Precondition: n an int, 0 < n < 1,000,000"""assert type(n) == int, repr(n)+' is not an int'assert 0 < n and n < 1000000, repr(n)+' is out of range'# Implement method here…
9/30/21 Asserts & Error Handling 22
Check (part of)the precondition
Error message when violated
Aside: Using repr Instead of str
>>> msg = str(var)+' is invalid'>>> print(msg)2 is invalid
• Looking at this output, what is the type of var?
9/30/21 Asserts & Error Handling 23
A: intB: floatC: strD: Impossible to tell
Aside: Using repr Instead of str
>>> msg = str(var)+' is invalid'>>> print(msg)2 is invalid
• Looking at this output, what is the type of var?
9/30/21 Asserts & Error Handling 24
A: intB: floatC: strD: Impossible to tell CORRECT
Aside: Using repr Instead of str
>>> msg = str(var)+' is invalid'>>> print(msg)2 is invalid
>>> msg = repr(var)+' is invalid'>>> print(msg)'2' is invalid
9/30/21 Asserts & Error Handling 25
Clear that varis really a string
Enforcing Preconditions is Tricky!
def lookup_netid(nid):"""Returns: name of student with netid nid.
Precondition: nid is a string, which consists of 2 or 3 letters and a number"""assert ?????
9/30/21 Asserts & Error Handling 26
Assert use expressions only.Cannot use if-statements.
Each one must fit on one line.
Sometimes we will only enforce part of
the precondition
Enforcing Preconditions is Tricky!
def lookup_netid(nid):"""Returns: name of student with netid nid.
Precondition: nid is a string, which consists of 2 or 3 letters and a number"""assert type(nid) == str, repr(nid) + ' is not a string'assert nid.isalnum(), nid+' is not just letters/digits'
9/30/21 Asserts & Error Handling 27
Returns True if s contains only letters, numbers.
Does this catchall violations?
Using Functions to Enforce Preconditions
def exchange(src, dst, amt):"""Returns: amount of src received.Precondition: src is a valid currency codePrecondition: dst is a valid currency codePrecondition: amt is a float"""
assert ??????, repr(src) + ' not valid'assert ??????, repr(dst) + ' not valid'assert type(amt)==float, repr(amt)+' not a float'
9/30/21 Asserts & Error Handling 28
Using Functions to Enforce Preconditions
def exchange(src, dst, amt):"""Returns: amount of src received.Precondition: src is a valid currency codePrecondition: dst is a valid currency codePrecondition: amt is a float"""
assert is_currency(src), repr(src) + ' not valid'assert is_currency(dst), repr(dst) + ' not valid'assert type(amt)==float, repr(amt)+' not a float'
9/30/21 Asserts & Error Handling 29
Recovering from Errors
• Suppose we have this code:result = input('Number: ') # get number from userx = float(result) # convert string to floatprint('The next number is '+str(x+1))
• What if user mistypes?Number: 12aTraceback (most recent call last):File "prompt.py", line 13, in <module>x = float(result)
ValueError: could not convert string to float: '12a'9/30/21 Asserts & Error Handling 30
Ideally Would Handle with Conditional
result = input('Number: ') # get number from userif isfloat(result):
x = float(result) # convert to floatprint('The next number is '+str(x+1))
else:print('That is not a number!')
Does not Exist
9/30/21 Asserts & Error Handling 31
Using Try-Except
try:result = input('Number: ') # get numberx = float(result) # convert to floatprint('The next number is '+str(x+1))
except:print('That is not a number!')
Similar to if-else§ But always does the try block§ Might not do all of the try block
9/30/21 Asserts & Error Handling 32
Using Try-Except
try:result = input('Number: ') # get numberx = float(result) # convert to floatprint('The next number is '+str(x+1))
except:print('That is not a number!')
Similar to if-else§ But always does the try block§ Might not do all of the try block
9/30/21 Asserts & Error Handling 33
Conversion may crash!
Execute if crashes
Try-Except is Very Versatile
def isfloat(s):"""Returns: True if string s represents a float"""try:
x = float(s)return True
except:return False
9/30/21 Asserts & Error Handling 34
Conversion to a float might fail
If attempt succeeds,string s is a float
Otherwise, it is not
Try-Except and the Call Stack
# recover.py
def function_1(x,y):try:
return function_2(x,y)except:
return float('inf')
def function_2(x,y):return function_3(x,y)
def function_3(x,y):return x/y # crash here
• Error “pops” frames off stack§ Starts from the stack bottom§ Continues until it sees that
current line is in a try-block§ Jumps to except, and then
proceeds as if no error
9/30/21 Asserts & Error Handling 35
function_1
function_2
function_3pops
popsline in a try
Try-Except and the Call Stack
# recover.py
def function_1(x,y):try:
return function_2(x,y)except:
return float('inf')
def function_2(x,y):return function_3(x,y)
def function_3(x,y):return x/y # crash here
• Error “pops” frames off stack§ Starts from the stack bottom§ Continues until it sees that
current line is in a try-block§ Jumps to except, and then
proceeds as if no error • Example:
>>> print function_1(1,0)inf>>>
9/30/21 Asserts & Error Handling 36
No traceback!
How to return∞ as a float.
Tracing Control Flow
def first(x):print('Starting first.')try:
second(x)except:
print('Caught at first')print('Ending first')
def second(x):print('Starting second.')try:
third(x)except:
print('Caught at second')print('Ending second')
def third(x):print('Starting third.')assert x < 1print('Ending third.')
What is the output of first(2)?
9/30/21 Asserts & Error Handling 37
Tracing Control Flow
def first(x):print('Starting first.')try:
second(x)except:
print('Caught at first')print('Ending first')
def second(x):print('Starting second.')try:
third(x)except:
print('Caught at second')print('Ending second')
def third(x):print('Starting third.')assert x < 1print('Ending third.')
What is the output of first(2)?
9/30/21 Asserts & Error Handling 38
'Starting first.''Starting second.''Starting third.''Caught at second''Ending second''Ending first'
Tracing Control Flow
def first(x):print('Starting first.')try:
second(x)except:
print('Caught at first')print('Ending first')
def second(x):print('Starting second.')try:
third(x)except:
print('Caught at second')print('Ending second')
def third(x):print('Starting third.')assert x < 1print('Ending third.')
What is the output of first(0)?
9/30/21 Asserts & Error Handling 39
Tracing Control Flow
def first(x):print('Starting first.')try:
second(x)except:
print('Caught at first')print('Ending first')
def second(x):print('Starting second.')try:
third(x)except:
print('Caught at second')print('Ending second')
def third(x):print('Starting third.')assert x < 1print('Ending third.')
What is the output of first(0)?
9/30/21 Asserts & Error Handling 40
'Starting first.''Starting second.''Starting third.''Ending third''Ending second''Ending first'