Computer ScienceChapter 3: Programming Fundamentals (Python)
3.12 Debugging
Debugging
Explanation level
Introduction
Even experienced programmers make mistakes. These mistakes, called bugs, can cause a program to behave unexpectedly, produce wrong results, or crash. Finding and fixing these bugs is called debugging. Python provides several tools and techniques to help you find and fix bugs efficiently.
Definition
Debugging is the process of finding, analyzing, and fixing errors (bugs) in a program.
A bug is an unanticipated error which is generally logical in nature.
In simple words, debugging means finding out why your program is not working correctly and fixing it.
Main Idea
Some bugs are simple, as we discussed earlier in the example where we swapped the values of 2 variables. And to fix it we dry ran the code. However, for complex ones which exist in a code having large numbers of lines of code and many variables of various data types, manual dry-run will be tedious and hard. Hence, the solution is to debug the code.
Core Concept
Python provides several debugging tools:
- Debugger in IDE (like IDLE)
- Command-line debugger (pdb)
- Print statements
- assert keyword
Each tool has its own strengths, and the choice depends on the complexity of the bug.
Detailed Explanation
Debugger in IDE
In the Python IDLE menu, locate the Debug and select Debugger. Correspondingly, you will notice a Debug On message on the shell prompt before the cursor.
Next, run your program and a window titled Debug Control will pop up. This window allows you to monitor and examine the defined variables and the corresponding values, after each instruction.
The Debug Control Window
The pop-up window comprises some buttons that will help you in debugging the code:
| Button | Purpose |
|---|---|
| Go | Jump to the breakpoint directly |
| Step | Execute the code line-by-line |
| Over | Execute a function call completely without stepping into it |
| Out | Skip the remaining portion of a function and come out of it |
| Quit | Exit the debugger |
Step Button: The Step button is the one that you will use initially, quite frequently; as it allows you to execute the code line-by-line. Every time you press it, the next line of code will execute.
Go Button: The Go button allows you to jump to the breakpoint directly. And after that you can press step iteratively to seek the bug.
Over Button: The Over button is used when there is a function call and you want to avoid it completely. This way, the function is fully executed and value is returned but the debugger skips it.
Out Button: The Out button is used, when you have reached in a function and are examining it in debugger, but wants to skip the remaining portion of the function and come out of it.
Checkboxes in Debug Control
Besides the buttons, there are some checkboxes as well:
| Checkbox | Purpose |
|---|---|
| Globals | Shows global data |
| Locals | Shows local data |
| Source | Shows the file currently running in the debugger |
| Stack | Highlights the function currently executed in the debugger |
Breakpoints
The most important element, we have kept for the end, is the Breakpoint. A breakpoint is the line in the code which needs close examination, so the debugger will make a stopover there definitely.
Although there may be multiple breakpoints, and debugger will make a stopover on each one of them.
- Activate the debugger
- Right-click on the line of code you want to set as a breakpoint
- Select Set Breakpoint
The said line will be highlighted yellow and is now marked as a breakpoint.
Example — Breakpoint in Code: ```python x = 5 y = 3 temp = 0 print('Values of x: ', x, 'and y: ', y) print("Let's Swap the value of x & y") temp = x y = temp x = y print('Values of x: ', x, 'and y: ', y) ```
If you set a breakpoint on line 7 (`y = temp`), the debugger will pause there, and you can examine the values of all variables at that moment.
Command-Line Debugger (pdb)
Python also provides a command-line debugger called pdb (Python Debugger).
Running a program with pdb: ``` >python -m pdb swap_var.py ```
Adding the `-m` switch allows Python to run the imported module as a script.
Thereafter, you can execute through the code line by line using `step` or `next` command.
Common pdb commands:
| Command | Purpose |
|---|---|
| `step` | Execute current line, step into functions |
| `next` | Execute current line, do not step into functions |
| `continue` | Continue execution until next breakpoint |
| `list` | Show the code around the current line |
| `break line_number` | Set a breakpoint at the specified line |
| `print variable` | Show the value of a variable |
| `help` | Show help for pdb commands |
| `quit` | Exit the debugger |
Example — Setting a breakpoint: ``` (Pdb) break 7 Breakpoint 1 at c:\python37-32\swap_var.py:7 (Pdb) list 1 x = 5 2 y = 3 3 temp = 0 4 print('Values of x: ', x, 'and y: ', y) 5 print("Let's Swap the value of x & y") 6 temp = x 7 B y = temp 8 x = y 9 print('Values of x: ', x, 'and y: ', y) (Pdb) continue > c:\python37-32\swap_var.py(7)<module>() -> y = temp (Pdb) ```
The `B` next to line 7 shows that a breakpoint is set there. The `continue` command runs the program until the breakpoint.
Debugging via pdb.set_trace()
Alternatively, Python also gives the option to import pdb in the code, as the first line of the program. When the program executes, the debugger stops at the first line. Next, `step` command can be used.
You may assign breakpoints in the code, using `pdb.set_trace()` instruction. As soon as the program executes, it stops on the very first line after `pdb.set_trace()` instruction allowing the code to be inspected and executed line-by-line.
Example — Using pdb.set_trace(): ```python import pdb
x = 5 y = 3 temp = 0 print('Values of x: ', x, 'and y: ', y) print("Let's Swap the value of x & y") pdb.set_trace() temp = x y = temp x = y print('Values of x: ', x, 'and y: ', y) ```
When the program runs, it will pause at the `pdb.set_trace()` line and you can examine variables.
Debugging via Print Statements
While writing a program, the first statement you will learn is how to print. If used adequately, print statements are the easiest way to debug a program. By placing multiple print statements thoughtfully, a smooth traceability of any bug can be achieved.
Example — Debug Using Print Statements: ```python x = 5 y = 3 temp = 0 print('Values of x: ', x, 'and y: ', y) print("Let's Swap the value of x & y") temp = x print('Values of x: ', x, 'and y: ', y, 'and temp: ', temp) y = temp print('Values of x: ', x, 'and y: ', y, 'and temp: ', temp) x = y print('Values of x: ', x, 'and y: ', y, 'and temp: ', temp) ```
Output: ``` Values of x: 5 and y: 3 Let's Swap the value of x & y Values of x: 5 and y: 3 and temp: 5 Values of x: 5 and y: 5 and temp: 5 Values of x: 5 and y: 5 and temp: 5 ```
The corresponding output allows us to identify the bug — the value `3` is lost because `y = temp` overwrites `y` before `x = y` can copy the original `y`.
The assert Keyword
Python also provides the `assert` keyword which just like a boolean checks for a condition and returns `True` or `False`. If the assert statement is `True`, the statement will execute normally otherwise an AssertionError is returned.
Mainly, `assert` is used to locate logical bugs in code, or any illogical suppositions taken into consideration.
Example — Using assert: ```python x = 5 # initialize x = 5 * -1 # may be typing error assert x > 0 print(x) ```
Output: ``` AssertionError ```
- `x = 5` is correct
- `x = 5 * -1` makes `x = -5` (possibly a typing error)
- `assert x > 0` checks if `x` is positive
- Since `x` is negative, an `AssertionError` is raised
- The program stops, and the error is reported
Using assert to pinpoint errors: ```python def calculate_average(nums): assert len(nums) > 0, "List cannot be empty" return sum(nums) / len(nums)
print(calculateaverage([10, 20, 30])) # 20.0 print(calculateaverage([])) # AssertionError: List cannot be empty ```
Help in pdb
You can type in `help` on the shell prompt, at any point in time, and a list of available debugging commands will be available. You may type in specifically `help "command_name"` for further guidance.
Example: ``` (Pdb) help step s(tep) Execute the current line, stop at the first possible occasion (either in a function that is called or in the current function). ```
Intermediate Level
Types of Errors
| Error Type | Description | Example |
|---|---|---|
| Syntax Error | Violates the rules of the language | `print("Hello"` (missing parenthesis) |
| Runtime Error | Occurs while the program is running | `100` (division by zero) |
| Logical Error | Program runs but gives wrong output | Using `-` instead of `+` |
Syntax errors are usually caught by the interpreter before the program runs. Runtime errors occur during execution and cause the program to crash. Logical errors are the hardest to find because the program runs without crashing but produces wrong results.
Debugging Strategies
1. Print Statements: Add `print()` at key points to see variable values.
2. Dry Run: Manually trace the code on paper, tracking variable values.
3. Debugger: Use an IDE debugger (like IDLE's Debug Control) to step through code.
4. Breakpoints: Set breakpoints at suspicious lines to pause execution and examine variables.
5. Rubber Duck Debugging: Explain your code line-by-line to someone (or something) else. Often, you'll find the bug yourself.
6. Divide and Conquer: Comment out parts of the code to isolate the bug.
7. Check Boundaries: Test with edge cases (0, 1, negative numbers, empty lists).
8. Read Error Messages: Python's error messages tell you the line number and type of error.
Common Bugs and How to Fix Them
| Bug | Cause | Fix |
|---|---|---|
| `NameError` | Using a variable that is not defined | Check spelling and definition |
| `TypeError` | Using wrong data type | Check data types with `type()` |
| `IndexError` | Accessing an index that does not exist | Check list length with `len()` |
| `KeyError` | Accessing a key that does not exist in a dictionary | Check keys with `.keys()` |
| `ZeroDivisionError` | Dividing by zero | Add a check before division |
| `IndentationError` | Incorrect indentation | Check indentation carefully |
| `SyntaxError` | Violating Python syntax rules | Check for missing parentheses, colons, etc. |
Debugging with Print — A Systematic Approach
Step 1: Identify where the bug might be. Step 2: Add print statements before and after suspect code. Step 3: Run the program and see the output. Step 4: Compare expected vs actual values. Step 5: Narrow down the location of the bug. Step 6: Fix the bug. Step 7: Remove the print statements.
Example:
```python def calculatediscount(price, discountpercent): print("Debug: price =", price, "discount =", discountpercent) discount = price * discountpercent / 100 print("Debug: discount =", discount) finalprice = price - discount print("Debug: finalprice =", finalprice) return finalprice
print(calculate_discount(1000, 20)) ```
Output: ``` Debug: price = 1000 discount = 20 Debug: discount = 200.0 Debug: final_price = 800.0 800.0 ```
If the output were wrong, the debug prints would help pinpoint where.
Formula / Mathematical Form
Not applicable for this topic.
Solved Example
Example — Debug the Following Code
Buggy Code: ```python def calculate_average(nums): total = 0 for i in range(len(nums)): total = total + nums[i] average = total / len(nums) # Bug: division by zero if nums is empty return average
nums = [] print(calculate_average(nums)) ```
Error: ``` ZeroDivisionError: division by zero ```
Debugging Process:
Step 1: Identify the error — `ZeroDivisionError`. Step 2: The error occurs because `len(nums)` is 0 when the list is empty. Step 3: Add a check before division.
Fixed Code: ```python def calculate_average(nums): if len(nums) == 0: return 0 # Or raise an error total = 0 for i in range(len(nums)): total = total + nums[i] average = total / len(nums) return average
nums = [] print(calculateaverage(nums)) # 0 nums = [10, 20, 30] print(calculateaverage(nums)) # 20.0 ```
Output: ``` 0 20.0 ```
Important Points
- - Debugging is the process of finding and fixing bugs in a program.
- - A bug is an unanticipated error, generally logical in nature.
- - Python provides several debugging tools: IDE debugger, pdb, print statements, assert.
- - The IDLE debugger has buttons: Go, Step, Over, Out, Quit.
- - A breakpoint is a line where the debugger pauses execution.
- - The `step` command executes code line-by-line.
- - The `next` command executes the current line without stepping into functions.
- - The `continue` command runs until the next breakpoint.
- - `pdb.set_trace()` allows inserting a breakpoint directly in code.
- - `print()` statements are the simplest debugging tool.
- - The `assert` keyword checks a condition and raises `AssertionError` if false.
- - Syntax errors are caught by the interpreter; logical errors are not.
- - Logical errors are the hardest to find.
Remember
- - Debugging = Finding and fixing bugs.
- - Bug = Unanticipated error, usually logical.
- - IDLE Debugger = Debug → Debugger.
- - Step = Execute line-by-line.
- - **Go** = Jump to breakpoint.
- - Over = Execute function call without stepping into it.
- - Out = Skip remaining portion of function.
- - Breakpoint = Line where execution pauses.
- - pdb = Command-line debugger.
- - `pdb.set_trace()` = Insert breakpoint in code.
- - Print statements = Simplest debugging tool.
- - assert = Check condition, raise error if false.
- - Syntax Error = Violates language rules.
- - Logical Error = Wrong output, no crash.
Common Mistakes
- - Not using the debugger: Many beginners only use print statements, missing the power of step-by-step debugging.
- - Ignoring error messages: Python's error messages tell you the line number and type of error.
- - Setting too many breakpoints: Too many breakpoints slow down debugging.
- - Forgetting to remove print statements: Debug prints left in the code can clutter output.
- - Confusing `=` with `==`: This is a common logical bug.
- - Off-by-one errors: `range(10)` gives 0-9, not 1-10.
- - Modifying a list while iterating: Causes elements to be skipped.
- - Assuming variables hold the right value: Always verify with print or debugger.
- - Not testing edge cases: Empty lists, zero, negative numbers often reveal bugs.
- - Giving up too soon: Debugging is a skill that improves with practice.
Quick Revision
Bug is an unanticipated error which is generally logical in nature. Some are simple, as we discussed earlier in the example where we swapped the values of 2 variables. And to fix it we dry ran the code. However, for complex ones which exist in a code having large number of lines of code and many variables of various data types, manual dry-run will be tedious and hard. Hence, the solution is to debug the code.
Debugger in IDE: In the Python IDLE menu, locate the Debug and select 'Debugger'. Correspondingly, you will notice a 'Debug On' message on the shell prompt. Next, run your program and a window titled 'Debug Control' will pop-up which allows you to monitor and examine the defined variables and the corresponding values, after each instruction.
- Step — allows you to execute the code line-by-line
- Go — allows you to jump to the breakpoint directly
- Over — used when there is a function call and you want to avoid it completely
- Out — used when you have reached in a function and are examining it in debugger, but wants to skip the remaining portion of the function and come out of it
There are some checkboxes: 'Globals' and 'Locals' reflect the global and local data respectively. 'Source' is the file currently running in the debugger. 'Stack' highlights the function currently executed in the debugger.
A breakpoint is the line in the code which needs close examination, so the debugger will make a stopover there definitely.
- Debugging via print statements:
- By placing multiple print statements thoughtfully, a smooth traceability of any bug can be achieved.
assert keyword: Python also provides 'assert' keyword which just like a boolean checks for a condition and returns True or False. If the assert statement is True, the statement will execute normally otherwise an 'AssertionError' is returned. Mainly, assert is used to locate logical bugs in code.
60-Second Revision
- Debugging = Finding and fixing bugs.
- Bug = Unanticipated error, usually logical.
- IDLE Debugger = Debug → Debugger.
- Step = Line-by-line execution.
- **Go** = Jump to breakpoint.
- Over = Skip into function.
- **Out** = Skip out of function.
- Breakpoint = Pause point.
- pdb = Command-line debugger.
- Print statements = Simplest debugging.
- assert = Check condition, raise error if false.
- Warning: Logical errors are hardest to find — always test with multiple inputs.
Did you understand this topic?
Your answer is saved with your learning record. It does not mark the topic as mastered.
Quick revision
Revision Zone
Review the key facts, formulas and checks before you move on.3.12 Debugging
Sticky Notes · Quick Revision
Key Point 1
- Debugging = Finding and fixing bugs.
Key Point 2
- Bug = Unanticipated error, usually logical.
Key Point 3
- IDLE Debugger = Debug → Debugger.
Key Point 4
- Step = Line-by-line execution.
Key Point 5
- **Go** = Jump to breakpoint.
Key Point 6
- Over = Skip into function.
Key Point 7
- **Out** = Skip out of function.
Key Point 8
- Breakpoint = Pause point.
Key Point 9
- pdb = Command-line debugger.
Key Point 10
- Print statements = Simplest debugging.
Key Point 11
- assert = Check condition, raise error if false.
Key Point 12
- Syntax Error = Language rule violation.
Same supplied content as this Notes page (shared canonical source).