ICTPRG435 - Session 5: Debugging and Pseudocode

Overview: In this session, you'll explore different types of Python errors, learn to use debugging tools in Thonny and Python Tutor, and understand how to write pseudocode for algorithm design. You'll also practice identifying and fixing logical errors in Python programs.

Learning outcomes

Types of Python Errors

Python programs can fail in different ways. The main error categories are:

Syntax Errors

Syntax errors occur when the interpreter encounters invalid code - like missing colons, misspelled keywords, or incorrect indentation. The program will not run until these are fixed.

# Example 1: invalid comparison operator
name = 'Fred'
if name = 'Fred':   # -> SyntaxError: invalid syntax. Maybe you meant '==' or ':=' instead of '='?       
    print("That's my name".)  

# Example 2: capitalization and missing commas
x = 10
while X < 20:                    # -> NameError: name 'X' is not defined
    Print(x 'is less than 20.")  # -> print() misspelled, missing +, mismatched quotes
    x += 1

Hint: Read error messages carefully - they often tell you exactly where the issue occurs.

Runtime Errors

Runtime errors (or exceptions) occur while the program is executing. Common causes include dividing by zero, accessing out-of-range elements, or opening a missing file.

Locating runtime errors is easier as an error message usually provide information with which line of code is causing the issue.

Note: Runtime errors are also referred to as exceptions. More information on exceptions and exception handling is located under the Session 5 Files heading on the resources page.

# Example 1: IndexError
a = ['a', 'b', 'c']
print(a[3])  # -> IndexError: list index out of range

# Example 2: FileNotFoundError
x = open('a.txt', 'r')  # -> FileNotFoundError: [Errno 2] No such file or directory: 'a.txt'
content = x.read()
print(content)
x.close()

Python's error messages specify the line number and type of error, which helps with debugging.

Logical Errors

Logical errors occur while a program runs without crashing. Logical errors produce incorrect results with no error messages. Logical errors may include the use of incorrect variable names, errors in the program's algorithm logic, incorrect order of operator precedence and errors in Boolean expressions.

As the program does not crash and no error messages are produced, logical errors are the most challenging to locate.

# Example 1
def adder(x, y):
    x + y  # -> missing 'return' statement
    
print("5 + 5 =", adder(5, 5))
# Output: 5 + 5 = None


# Example 2
a = "abc"
c = ""
for x in a:
    b = a[-1]
    c = c + a
    a = a[:-1]

print(c) # Expected: cba -> Actual: abcaba

Tip: Logical errors usually arise from incorrect algorithm design or misunderstanding program flow. Debugging tools and print statements can help locate the issue.

Debugging Python Scripts

Let's debug this simple script using Thonny's debugger. The script should remove all 'dog' entries from a list, but it doesn't work as expected.

# pet.py
pets = ["cat", "dog", "dog", "rabbit"]
index = 0

while index < len(pets):
    pet = pets[index]
    if pet == "dog":
        pets.pop(index)
    index += 1

print(pets) # Output: ['cat', 'dog', 'rabbit'] -> Bug: One 'dog' remains!

Using Thonny's Debugger

Thonny includes a simple, visual debugger designed for beginners. It lets you step through your program line by line and observe how variables change in real time - ideal for understanding loops, conditions, and logical flow.

Starting the Debugger

  1. Open your script in Thonny (e.g., pet.py).
  2. Save the file (Ctrl + S).
  3. Click the Debug current script button on the toolbar, or go to Run → Debug current script.
  4. Thonny will highlight the first executable line, showing that execution is paused before that line.

Debugger Controls

Use the buttons in the toolbar to control execution:

ButtonShortcutDescription
Step OverF6Execute the current line; if it calls a function, run the entire function without stepping inside it.
Step intoF7Move into a function call to debug it line by line.
↱ Step outShift + F7Finish the current function and return to the caller.
▶ ResumeF8Run the program until the next breakpoint or the end of the script.
⏹ Stop/ResetCtrl + F2Immediately stop execution and reset the environment.

Breakpoints

To pause at a specific line, click in the left margin next to it - a red dot (🔴) will appear. Thonny will stop execution on that line next time you run code in debugging mode.

Click the dot again to remove the breakpoint.

Watching Variables

Visualizing the Call Stack

The Call stack pane (bottom-right. To unable it, click View -> Stack) displays which functions are active and who called them - helpful when debugging nested or recursive code.

Example - Debugging pet.py

# pet.py
pets = ["cat", "dog", "dog", "rabbit"]
index = 0

while index < len(pets):
    pet = pets[index]
    if pet == "dog":
        pets.pop(index)
    index += 1

print(pets)
# Output: ['cat', 'dog', 'rabbit']

Steps to debug:

  1. Set a breakpoint on if pet == "dog":.
  2. Click the Debug button.
  3. Use F7 (Step into) to move line by line.
  4. Watch index and pets change in the Variables pane.
  5. Notice how removing an item shifts the list left - but index still increments, causing one “dog” to remain.

Now try adjusting the logic (for example, decrementing index after pop() or iterating backwards) and rerun the debugger to confirm the fix.

Tips for Effective Debugging

Thonny's debugger is excellent for learning - it visually highlights each execution step, helping you see precisely what your program does and why.

Python Tutor

Python Tutor is a free online tool that visualizes Python execution step by step. It helps you see variable changes, memory states, and flow of control.

a = "any"
b = ["one", "two", "three"]
c = a + b[0]
print(c)
b.pop()
# Output: anyone

Use Visualize ExecutionNext to step through the program and observe variable values and list updates visually.

Other Debugging Techniques

Pseudocode

Pseudocode is a simplified, human-readable way of describing an algorithm. It helps plan a program's logic before actual coding.

Advantages

Writing guidelines

Pseudocode example

Goal: A program that lets the user guess a number.

Pseudocode:
PRINT "Guess the Number Game"
SET number TO 25
PROMPT user for guess
GET guess and CONVERT to integer
IF guess = number THEN
    PRINT "Correct"
ELSE IF guess > number THEN
    PRINT "Too High"
ELSE
    PRINT "Too Low"
ENDIF
Python equivalent:
print("Guess the Number Game")
number = 25
guess = int(input("Enter number: "))

if guess == number:
    print("Correct")
elif guess > number:
    print("Too High")
else:
    print("Too Low")

Logical Errors and Testing

Logic errors produce incorrect results. They often arise from flawed logic or misapplied conditions. The best defense is planning (pseudocode) and testing.

Testing methods

Example - Adding print and comments to locate logic errors

# Create a countdown function
def countDown(x):
    # Initialize empty string
    countdown = ""
    while x > 0:
        num = str(x)
        countdown += num + ", "
        x -= 1
    countdown += "Blast Off"
    return countdown

print(countDown(10))
# Output: 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, Blast Off

Common logic error examples

# 1. Loop variable misuse
x = "ABC"
for i in x:
    print(x)  # ❌ prints full string each time
# Expected: A B C

# 2. Missing type conversion
x = input("Enter a value: ")
y = input("Enter a value: ")
print(x + y)  # ❌ concatenation, not addition

# 3. Wrong condition
num = 3
mod = num % 2
if mod != 1:
    print("Odd Number")  # ❌ reversed logic
else:
    print("Even Number")

# 4. Incorrect order of conditions
x = 21
if x % 3 == 0:
    print("Multiple of 3")
elif x % 7 == 0:
    print("Multiple of 7")
elif x % 3 == 0 and x % 7 == 0:
    print("Multiple of 3 and 7")  # ❌ never reached

Key insight: Write expected and actual results side-by-side to spot logic flaws quickly. Use breakpoints, watches, and hypothesis testing to verify assumptions.