Debugging – Programs without Compile-time and Run-time Errors

Code written needs to be debugged.  Remind your students

  • If code doesn’t work as expected, then by definition one does not understand what is going on.
  • Don’t write more code.  Debug what you have written.
  • The hard part is often finding the bug. Once one knows where it is in the code, it can be easy to fix.

Trace the execution

When the program has an error and it is not clear what causes it, ask your students to trace through the execution of the program using pencil and paper.  Make sure they execute the program and not what they think the program is doing. This process can get tedious when there are longer loops. However, many errors can be recreated on small input sets.  In particular, errors are often caused by incorrect handling of boundary situations.   Alternatively, students can use PythonTutor (http://pythontutor.com) to follow the execution.

Tracing is most effective for small programs. It often makes students realize what is really happening and helps them understand how to correct the code.

Use print statements

Print statements provide a record of the history of the execution. Common errors like “list index out of range” caused by incorrect use of the range function can often be detected by adding print statements. However, too much output generated by print statements can be overwhelming.

Print statements show what is happening and not whether there’s a problem or not. However, taking a careful look at what is printed can help one understand why things are not going as expected.  Printing does not stop execution and may not be helpful when there is an infinite loop.

A common tendency is to print too much. Not all values printed shed light into what causes the problem.  When testing code incrementally we may add print statements only for the parts being tested. Remind student to remove print statements when no longer needed.

Use a debug switch to enable/disable print statements

Instead of removing debug messages, have a switch to enable or disable the debug messages. It is likely that this is not going to be the last time you will debug the program. It is convenient to have a debug switch that you can turn on when you are debugging to get extra information. Only do this for the most important messages. Too many messages may be overwhelming.

Example:

# Euclid's algorithm for computing the gcd
# define the switch
debug = True 

a = 144
b = 27

while b != 0:
    gcd = b
    b = a % b
    a = gcd
    if debug:
        print 'The values of a and b are',a,b
print 'The GCD of a and b is', gcd

Use assert

Assertions are a systematic way to check that the internal state of a program is it should be.  If the program is not doing what you expect, it most likely has a bug.  Python’s “assert” checks a condition; if it is true, it does nothing; if it is false, it raises an AssertionError with an optional error message.

For example: (code is from M&C4:  Infinite Loops)

i = 1
while i != 100:
    print i
    i = i + 7
    assert 100 % i == 0, 'The loop will not be terminated correctly.'

In the program above, every time we add 7 to variable i and the loop will be terminated when i is equal to 100. However, if i never becomes the value 100, it will lead to an infinite loop. We add an assert statement after we increase the value of i to see if there will be an infinite loop.

AssertionError

The assert statement raises an exception if some condition is not met, and does nothing if everything works.

Debugging Tools: