Debugging
Introduction
By caused the problem, or the line number that contains the next function call
- Function: the name of the function in which the line can be found. Techniques
Running doctests.
Usually, we will provide doctests for you in the starter files. You can add more tests by following the same format. It is often helpful to write additional tests to uncover more details about the shape of the inputs and the expected outputs of the problem, in addition to helping with the implementation of the program itself. A little time spent upfront writing tests can save a lot of time down the line.
Writing your own tests
In addition to doctests, you can write your own tests. There are two ways to do this: (1) write additional doctests, or (2) write testing functions.
To write more doctests, simply follow the style of existing doctests.
You can also write your own functions (much like the
take_turn_test
function from Project 1).
Some advice in writing('DEBUG: result is', result) return other_function(result)
Note: prefixing debug statements with the specific string
"DEBUG: "allows them to be ignored by the
okautograder used by cs61a. Since
okgenerally tests all the output of your code, it will fail if there are debug statements that aren't explicitly marked as such, even if the outputs are identical.('DEBUG: result is', result) tmp = other_function(result) print('DEBUG: other_function returns', tmp) return tmp
Some advice:
Don't just print out a variable -- add some sort of message to make it easier for you to read:
print(tmp) # harder to keep track print('DEBUG: tmp was this:', tmp) # easier
- Use
Use
whileloop to view the state of the counter variables after each iteration:
i = 0 while i < n: i += func(i) print('DEBUG: i is', i)
- Don't just put random
Long-term debugging('DEBUG: i is', i)
Now, whenever we want to do some debugging, we can set the global
debug variable to
True, and when we don't want to see any
debugging input, we can turn it to
False (such a variable is called
a "flag").
Interactive Debugging
One way a lot of programmers like to investigate their code is by use of an interactive REPL. That is, a terminal where you can directly run functions and inspect their outputs.
Typically, to accomplish this, you can run
python -i file.py
and one then has a session of python where all the definitions of
file.py have already been executed.
If you are using the
ok autograder, it has a specific tool that enables you to jump into the middle of
a failing test case. Just run
python ok -q <question name> -i
and if you have a failing test case for that question, the setup code and doctest will be printed on the screen and run, and you will then have access to a terminal where you can execute commands related to the program.
PythonTutor Debugging
Sometimes the best way to understand what is going on with a given
piece of python code is to create an environment diagram. While creating
an environment diagram by hand can sometimes be tedious, the tool PythonTutor
creates environment diagrams automatically. To use this tool, you can copy-paste your code
into it, and run it. Alternatively, if you are working on an assignment using
the
ok autograder, you can run
python ok -q <question name> --trace
and a browser window should open up with your code.
Using
assert statements
Python has a feature known as an
assert statement, which lets you test that a condition is true, and print an error
message otherwise in a single line. This is useful if you know that certain conditions need to be true at certain points
in your program. For example, if you are writing a function that takes in an integer and doubles it, it might be useful
to ensure that your input is in fact an integer. You can then write the following code
def double(x): assert isinstance(x, int), "The input to double(x) must be an integer" return 2 * x
Note that we aren't really debugging the
double function here, what we're doing is ensuring that anyone who calls
double is doing so with the right arguments. For example, if we have a function
g that takes in a string and a number
and adds the length of the string to twice the number, and it is implemented like so:
def g(x, y): return double(x) + y # should be double(y) + len(x)
Instead of getting a weird error about not being able to add a string and a number, we get a clean error that the
argument to
double must be an integer. This allows us to quickly narrow down the problem.
One major benefit of assert statements is that they are more than a debugging tool, you can leave them in code permanantly. A key principle in software development is that it is generally better for code to crash than produce an incorrect result, and having asserts in your code makes it far more likely that your code will crash if it has a bug in it. raised spaces! (It's just less of a headache in general in Python to use spaces and all cs61a content uses spaces)..
Missing close quotes
This is similar to the previous bug, but much easier to catch. Python will actually tell you the line that is missing the quote:.