Python Debugging (fixing problems)

A guide for beginner programmers.
by John Magee

Objective

This guide is intended to help you learn to fix your own programming problems. As you learn to program, you will encounter various bugs or errors. Some of these are easy to identify and fix, while others will take some more time.

This document will be updated with further information and tips during the semester.

Syntax Errors

Syntax errors can be detected before your program begins to run. These types of errors are usually typing mistakes, but more generally it means that there is some problem with the structure of your program.

Syntax Error

    Syntax errors in Python will pop up a dialog box like the one below. The message in this box is Syntax Error. There was an error in your program: EOL while scanning single-quoted string.

    Syntax Error. There was an error in your program: EOL while scanning single-quoted string.

    EOL stands for End Of Line. This error means that there was an open quote somewhere, but the line ended before a closing quote was found. Another type of syntax error will simply say invalid syntax. An invalid syntax error means that there is a line that python doesn't know what to do with. The last common type of syntax error you will likely encounter has to do with indention. You may see unindent does not match any outer indention level unexpected indent.

    Examples:
      print "hello world
      a = 3 + 5 7

    Solution: When you press OK on the dialog box. Python will attempt to highlight the offending line in your source code. You should use this location as a hint for where to start looking for your problem. First check the area highlighted. Then check the entire line. Lastly, check the line or lines before the line highlighted. The location marked is where Python noticed there was a problem, so the actual problem could come before!

    If you get an indention error, you should check that all of your lines of code are properly aligned in the correct columns. You can place you cursor at the start of each line and look at the col: indicator at the bottom right of IDLE to check this.

Token Error (missing parenthesis

    Token errors in Python will pop up a dialog box like the one below. The message in this box is Tabnanny Tokenizing Error. Token Error: EOF in multi-line statement

    Tabnanny Tokenizing Error.
         Token Error: EOF in multi-line statement.

    EOF stands for End Of File. This error usually means that there was an open parenthesis somewhere on a line, but not a matching closing parenthesis. Python reached the end of the file while looking for the closing parenthesis.

    Example:
      a = 3 + (4 + 5

    Solution: When you press OK on the dialog box. Python will attempt to highlight the offending line in your source code. However, since it had reached the end of the file, it will highlight the last line in the file! You can type a right parenthesis at the end of the file, and IDLE will highlight the matching opening parenthesis. You should then be able to find out where your missing parenthesis should be. Remember to remove the extra closing parenthesis at the end of your file.

    If this doesn't work, you may have to scan your entire file to look for the problem. The best solution is to avoid this problem by running your program after you write every few lines of code. If you encounter this error, you can then check your most recent changes as a likely suspect.

Runtime Errors

Runtime errors occur as your program executes. Since Python is an interpreted language, these errors will not occur until the flow of control in your program reaches the line with the problem.

Name Error


This will be a common error you encounter. It will give you a Traceback message like this:
    Traceback (most recent call last):
      File "C:/Users/John/Documents/Teaching-BU/Python-debugging/test.py", line 7, in 
        main()
      File "C:/Users/John/Documents/Teaching-BU/Python-debugging/test.py", line 5, in main
        print hello
    NameError: global name 'hello' is not defined
    
These messages can seem hard to understand at first. The first part tells you which file had the error. In the example above, the file is test.py and the error occurs on line 7. The next line shows the actual line of code where the error occured. This line executes the main() function. Similarly, the next two lines say that the error occurred on line 5, within main , and that the line with the error is print hello. Lastly, the actual NameError says that global name 'hello' is not defined.

A NameError means that Python tried to use a variable or function name, such as hello based on a previous definition. If it hasn't been defined at this point, you get the error.

Usual Causes:
  • A mistyped variable or function name.
  • Using a variable before it is defined.
  • The name was intended to be enclosed in quotes.
Examples:
print hello
pring "hello"
The following example leaves the s off dollars in the second line:
dollars = input("Enter dollars: ")
print "You have %d dollars" % dollar


Solution:
Usually, you can look at the last line mentioned in the TraceBack error. Place your cursor within idle and move it until you are on the correct line as indicated by the Ln: indicator in the bottom right of the editor. You should find the specific line quoted by the error message. In the case of a NameError, you can check if you typed the variable or function name correctly, if it should be in quotes, or if you should have defined it somewhere prior to the line.

TypeError


A TypeError you might encounter may look like this:
 File "C:/Users/John/Documents/Teaching-BU/Python-debugging/test.py", line 2, in 
    print "I am %d feet %d inches tall" % (5, 2, 5)
TypeError: not all arguments converted during string formatting

or
  File "C:/Users/John/Documents/Teaching-BU/Python-debugging/test.py", line 2, in 
    print "I am %d feet %d inches tall" % (5)
TypeError: not enough arguments for format string

These errors are caused by not matching the correct number of substitutions into a string format. The string format in both cases requires 2 substitutions. The first error is caused by attempting to substitute 3 arguments, and the second error is caused by trying to substitute 1 argument.

Logic (semantic) errors

Semantic or logic errors are problems with the design of your program. These usually do not produce any error message, but instead cause your program to behave incorrectly. These types of errors can be tricky to track down.

Logic errors


These errors are often caused by accidentally using one variable in a place where a different variable is intended, or by simply doing some math incorrectly.

Consider the following example:
apples = 0
pickedApples = input("Pick some apples: ")
apples = apples + pickedApples
pickedApples = input("Pick some more apples: ")
apples = apples + pickedApples
print "You have a total of %d apples" % pickedApples

The above code will execute, but it will not output the total number of apples picked. Instead, it will output the amount that was picked the last time! This simple example is easy to fix, but in a more complicated program it can be difficult to find such problems.

Solution:
Consider this hypothetical pseudocode for an input-process-output design pattern program:
  1. Get input for num1
  2. Get input for num2
  3. Get input for num3
  4. Some processing step 1
  5. Some processing step 2
  6. Some processing step 3
  7. Output one or two numbers
In the above pseudocode, you cannot be sure that all of the input and processing code is working correctly. All you see is your incorrect output. You can add extra print statements to your code so that you can see the intermediate processing. If you modify your code to work something like the below example, you will be more easily able to see where there are problems.
  1. Get input for num1
  2. Get input for num2
  3. Get input for num3
  4. Print num1, num2, num3 to make sure that values are input and stored correctly
  5. Some processing step 1
  6. print some intermediate result from step 1 for verification
  7. Some processing step 2
  8. print some intermediate result from step 2 for verification
  9. Some processing step 3
  10. Output one or two numbers
Once you are sure your program is working correctly, you can comment out the extra print statements.

Example: Using print to debug your code

Using extra print statements to display the value of your program's variables is a useful way to figure out what's going on with your program. The following example only has one intermediate calculation, but the same concept applies to more complicated programs.

pv example


Consider this example from class: pv.py

Here are the important parts of the code with an error introduced:
    fv = input("Enter the amount to be received in the future: ")
    r = input("Enter the rate of return (e.g. 0.05 for 5 percent): ")
    n = input("Enter the number of years: ")
    
    # calculate the pvfactor = 1 / (1+r)^n
    pvfactor = 1 / (1+r) * n
    # calcualte the pv = fv * pvfactor
    pv = fv * pvfactor
    # output the present value
    print "That's worth $", pv, "today."

When we run this code, all we see is the resulting output:
Enter the amount to be received in the future: 100
Enter the rate of return (e.g. 0.05 for 5 percent): 0.05
Enter the number of years: 10
That's worth $ 952.380952381 today.
The final result is obviously completely wrong because the value today should be less than the future value! To try to find the problem, we can add a number of extra print statements to try to see what's going on within our program:
    fv = input("Enter the amount to be received in the future: ")
    r = input("Enter the rate of return (e.g. 0.05 for 5 percent): ")
    n = input("Enter the number of years: ")
    print "fv =", fv, "r = ", r, "n=", n

    # calculate the pvfactor = 1 / (1+r)^n
    pvfactor = 1 / (1+r) * n
    print "pvfactor = ", pvfactor
    
    # calcualte the pv = fv * pvfactor
    pv = fv * pvfactor
    # output the present value
    print "That's worth $", pv, "today."

Now we can see the values inside our variables at each step:
Enter the amount to be received in the future: 100
Enter the rate of return (e.g. 0.05 for 5 percent): 0.05
Enter the number of years: 10
fv = 100 r =  0.05 n= 10
pvfactor =  9.52380952381
That's worth $ 952.380952381 today.

The first thing to check is that your inputs are properly stored in their correct variables. We see that the values for fv, r, and n are what we entered.

Next, we look at the intermediate calculation of pvfactor. We know this should be a number smaller than 1, but for some reason, it is 9.5238. We now know to focus on this line of code to look for the problem. Now you see that the exponent operator was not typed correctly, resulting in an incorrect calculation. The line should be:

pvfactor = 1 / (1+r) ** n

Now, with the change, we can see:
Enter the amount to be received in the future: 100
Enter the rate of return (e.g. 0.05 for 5 percent): 0.05
Enter the number of years: 10
fv = 100 r =  0.05 n= 10
pvfactor =  0.613913253541
That's worth $ 61.3913253541 today.

One more thing you can notice with this program if you experiment a bit more with other inputs. The pvfactor calculation assumes that the user enters a floating point number. What happens if the user enters an integer such as 1, or 2? This line of code then calculates using integer division and the result is zero! You never want to leave such decisions up to the user. Let's make that change and then comment out the extra print statements to get our final solution.
    fv = input("Enter the amount to be received in the future: ")
    r = input("Enter the rate of return (e.g. 0.05 for 5 percent): ")
    n = input("Enter the number of years: ")
    # print "fv =", fv, "r = ", r, "n=", n

    # calculate the pvfactor = 1 / (1+r)^n
    pvfactor = 1.0 / (1+r) ** n
    # print "pvfactor = ", pvfactor
    
    # calcualte the pv = fv * pvfactor
    pv = fv * pvfactor
    # output the present value
    print "That's worth $", pv, "today."
    
   
        

When all else fails...

You may encounter times when you cannot track down an error that is causing your program to fail dramatically. There are a few steps you can take to recover:
  1. Avoid - Hopefully you will not get to this point. You should try to write only a few lines of code and then execute your program. Write a few more, and execute again. If you encounter a problem, you will be able to look at the last few things you modified to find the problem.
  2. Prepare - You should save your programs everytime you begin a new part. If you reach a milestone, make a bakeup copy. All you have to do is select Save As... and name your file filename2.py, then filename3.py, etc. If you are working on filename7.py and cannot fix something you have broken, you can just go back to filename6.py. When you are done, you can copy all of the old files into a subdirectory and rename your final solution as you see fit.
  3. Comment out - You can attempt to comment out a troublesome section of code to see if that is causing the problem. You can use the Format menu inside IDLE to place the comment character in front of a section of code after highlighting it. Try isolating different sections of code so that you can narrow down the problem.
  4. Start fresh - When all else fails, you can start with a new empty file. Then copy and paste in small sections of your code one at a time. Each time you add a section, run your program to see if the newly added section causes the problem.

CS108 Home