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
- Identify and differentiate between syntax, runtime, and logical errors.
- Use Thonny's built-in debugger to inspect and control code execution.
- Apply other debugging techniques such as print statements and commenting.
- Write clear pseudocode to plan algorithms before implementation.
- Test Python programs using valid, invalid, and boundary data.
Types of Python Errors
Python programs can fail in different ways. The main error categories are:
- Syntax errors - mistakes in structure or grammar of the code.
- Runtime errors - problems that occur while the program is running.
- Logical errors - code runs but produces incorrect results.
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
- Open your script in Thonny (e.g.,
pet.py). - Save the file (Ctrl + S).
- Click the Debug current script button on the toolbar, or go to Run → Debug current script.
- 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:
| Button | Shortcut | Description |
|---|---|---|
| ↩ Step Over | F6 | Execute the current line; if it calls a function, run the entire function without stepping inside it. |
| ↱ Step into | F7 | Move into a function call to debug it line by line. |
| ↱ Step out | Shift + F7 | Finish the current function and return to the caller. |
| ▶ Resume | F8 | Run the program until the next breakpoint or the end of the script. |
| ⏹ Stop/Reset | Ctrl + F2 | Immediately 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
- The Variables pane (usually top-right. To unable it, click View -> Variables) shows all current variables and their values.
- Watch how variables change as you step through each line.
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:
- Set a breakpoint on
if pet == "dog":. - Click the Debug button.
- Use F7 (Step into) to move line by line.
- Watch
indexandpetschange in the Variables pane. - Notice how removing an item shifts the list left - but
indexstill 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
- Step slowly through loops and conditions to observe flow.
- Use print statements in combination with the debugger for extra clarity.
- Experiment with single stepping and visualization modes to fully understand what the code does.
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 Execution → Next to step through the program and observe variable values and list updates visually.
Other Debugging Techniques
- Print statements: Insert
print()near suspicious lines to display variable values. - Commenting out code: Disable certain parts temporarily to isolate the cause of the error.
- Tests: Use unit or functional tests to confirm that changes don't break functionality.
- Ask others: Collaborate with peers - a second pair of eyes can often spot what you missed.
Pseudocode
Pseudocode is a simplified, human-readable way of describing an algorithm. It helps plan a program's logic before actual coding.
Advantages
- Improves readability and understanding.
- Bridges the gap between design and implementation.
- Helps identify logic errors before coding begins.
Writing guidelines
- Use clear and concise English statements.
- Use indentation to represent structure and nesting.
- Use uppercase for control structures (
IF,FOR,WHILE,END). - Use descriptive action words like GET, DISPLAY, SET, INCREMENT.
- Always end multi-line structures with keywords like
ENDIForENDWHILE.
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
- Valid data: Values within expected range.
- Invalid data: Values outside allowed range.
- Boundary data: Edge-case values (e.g., minimum and maximum limits).
- Incorrect data: Wrong data type (e.g., string instead of integer).
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.