Lab: Debugging
Preparation
a. Make sure you are booted into the Mac OSX side of the computer.
b. Once you are logged in, double click the icon named courses.ads.carleton.edu
on your desktop. If you encounter any errors, have me or the prefect contact Mike Tie to set up your account.
c. Open up a Terminal
, navigate to your StuWork
directory, create a folder named lab-02-04
, and change your directory to the newly created folder. (Remember that you should replace USERNAME with your actual username!)
$ cd /Volumes/courses/cs111-01-w19/StuWork/USERNAME/
$ mkdir lab-02-04
$ cd lab-02-04
d. Open Brackets
and make sure you have your StuWork/USERNAME
directory opened. (You will see all of its subfolders in the left panel if so.) If the folder is not opened, you can open it by selecting File -> Open Folder
and navigating to courses
, then cs111-01-w19
, then StuWork
, then USERNAME
.
Exercise 1
In the testing lab, you were asked to write a function called reverse_list
that reverses a list via a side effect. Here is an attempted solution to that exercise using a helper function named swap
:
def swap(lst, index1, index2):
"""Modifies the list so that the elements at index1 and index2 are swapped
Parameters:
lst: A list
index1: An integer
index2: An integer
Returns:
Nothing; called for the side effect
Preconditions:
* len(lst) > 0
* 0 <= index1 < len(lst)
* 0 <= index2 < len(lst)
Postconditions:
Let old_lst refer to the list before executing the function. Then
after executing this function:
* len(lst) = len(old_lst)
* lst[index1] = old_lst[index2]
* lst[index2] = old_lst[index1]
* for all other indices, i,
lst[i] = old_lst[i]
"""
lst[index1] = lst[index2]
lst[index2] = lst[index1]
def reverse_list(lst):
"""Reverses the order of the elements of the list lst
Parameters:
lst: A list
Returns:
Nothing; called for the side effect
Postconditions:
Let old_lst refer to the list before executing the function. Then
after executing this function:
* len(lst) = len(old_lst)
* for all reasonable i, lst[i] = old_lst[len(old_lst) - 1 - i]
"""
# Iterates over list and swaps the elements as follows:
# index 0 gets swapped with len(lst) - 1
# index 1 gets swapped with len(lst) - 2
# index 2 gets swapped with len(lst) - 3
# and so on...
for i in range(len(lst)):
swap(lst, i, len(lst)-1-i)
a. Save the above code in a file named reverse.py
and import both functions into the REPL.
>>> from reverse import *
b. Try executing reverse_list
on the following list to see if it works properly.
>>> test_list = [100,200,300,400,500]
>>> reverse_list(test_list)
>>> test_list
c. OK, so it is indeed broken. Try executing it on a couple more lists; then discuss with your partner and make a hypothesis on where the most likely place the bug exists.
d. We will use pudb3
to investigate our hypothesis. To start, add the following main
function to your code and feel free to change the test_list
to one that may be more appropriate for testing your hypothesis.
def main():
test_list = [100,200,300,400,500]
reverse_list(test_list)
print(test_list)
if __name__ == "__main__":
main()
e. Execute the code with pudb3
to start up the Python debugger.
$ pudb3 reverse.py
f. Before we start debugging, there are a couple useful preferences to change. If pudb3
initially opens up the options menu, make sure to make the following changes.
- Make sure Show Line Numbers is checked
- Under Variable Stringifier, make sure that repr is checked
If the preferences did not open initially, you can press Ctrl + P
to open them.
g. To step through your code line by line, type the n
key to execute the next line of code. Note that n
will not enter into a function, but rather it executes the whole function at once. If you’d like to step into a function call, press the s
key. (At the top of the debugger, there is a helpful reference that reminds you what the keys are in case you forget.)
h. Let’s try investigating using breakpoints. Navigate to line swap(lst, i, len(lst)-1-i)
and press b
to set a breakpoint. Now, to continue executing your program until a breakpoint is pressed, you can press the c
key. Try pressing it and watch how the list is updated when the swap
function is called.
i. Try stepping into the swap
function and examine what it does. Does it successfully swap the two values? If not, discuss with your partner why the values are not being swapped properly.
j. Exit out of pudb3
by pressing q
and fix the swap
function so that it properly swaps values. Test it in the REPL to make sure that it is working properly.
k. After fixing the swap
function, try testing the reverse_list
function one more time. Does it work?
l. If the only bug you fixed was in the swap
function, you’ll find that the reverse_list
function still does not work. Discuss with your partner what you think is happening, and investigate your hypothesis using the debugger.
m. Fix the error in the reverse_list
function and make sure it is working properly by running a few tests.
Exercise 2
It is common to verify that the preconditions of your code are met and raise an exception if not. We will do this on the swap
function defined above. Below I have checked that the parameters have the appropriate type and raised an exception otherwise:
def swap(lst, index1, index2):
"""Modifies the list so that the elements at index1 and index2 are swapped
Parameters:
lst: A list
index1: An integer
index2: An integer
Returns:
Nothing; called for the side effect
Preconditions:
* len(lst) > 0
* 0 <= index1 < len(lst)
* 0 <= index2 < len(lst)
Postconditions:
Let old_lst refer to the list before executing the function. Then
after executing this function:
* len(lst) = len(old_lst)
* lst[index1] = old_lst[index2]
* lst[index2] = old_lst[index1]
* for all other indices, i,
lst[i] = old_lst[i]
"""
if type(lst) != list:
raise TypeError("first arg must be a list")
elif type(index1) != int:
raise TypeError("second arg must be an int")
elif type(index2) != int:
raise TypeError("third arg must be an int")
temp = lst[index1]
lst[index1] = lst[index2]
lst[index2] = temp
a. Add another elif
case that checks if the len(lst) > 0
precondition is satisfied and raises a ValueError
exception with an appropriate message if not.
b. Add two more elif
cases that check the last two preconditions and either raises a ValueError
or an IndexError
with an appropriate message if not. (Both ValueError
and IndexError
are appropriate exceptions since passing in invalid indices causes swap to break.)
Exercise 3
Write a program called sqrt.py
that asks the user to enter a number and prints the square root of that number. However, instead of crashing with a TypeError
if the user enters a non-number, your program should handle the error by displaying a helpful error message and asking them to retype the number.
For Those With Extra Time
Start working on the assignment.