This document covers debugging Python programs using PDB interactive debugger including setting breakpoints, stepping through code, inspecting and modifying variables, and post-mortem debugging. Python's built-in interactive debugger.
This document explores debugging Python programs using PDB (Python DeBugger), the built-in interactive debugger that allows setting breakpoints, stepping through code, inspecting and modifying variables, evaluating expressions interactively, and performing post-mortem debugging after crashes.
Imagine developing a Python application to analyze vast amounts of textual data for sentiment scores. As the application processes data, it occasionally encounters unexpected formats, causing crashes. Given the data volume and application complexity, identifying root causes using simple print() statements becomes increasingly challenging. This is where Python’s built-in interactive debugger, PDB, becomes essential.
PDB stands for “Python DeBugger.” It is an interactive debugger for Python programs, providing capabilities to:
| Feature | Print Debugging | PDB Debugger |
|---|---|---|
| Interactive inspection | No | Yes |
| Modify variables | No | Yes |
| Step through code | No | Yes |
| Conditional execution | No | Yes |
| Stack inspection | No | Yes |
| Post-mortem debugging | No | Yes |
| Code modification needed | Yes | Minimal |
1import pdb
A breakpoint is a spot in code that tells PDB to pause execution at that specific line:
1import pdb
2
3def add_numbers(a, b):
4 pdb.set_trace() # This will set a breakpoint in the code
5 result = a + b
6 return result
7
8print(add_numbers(3, 4))
1def add_numbers(a, b):
2 breakpoint() # Preferred method in Python 3.7+
3 result = a + b
4 return result
5
6print(add_numbers(3, 4))
When a breakpoint is hit, execution pauses and an interactive debugger session starts:
1> /path/to/script.py(6)add_numbers()
2-> result = a + b
3(Pdb)
| Component | Meaning |
|---|---|
> | Debugger prompt indicator |
/path/to/script.py | Current file path |
(6) | Current line number |
add_numbers() | Current function name |
-> result = a + b | Next line to execute |
(Pdb) | Command prompt waiting for input |
| Command | Shortcut | Description | Example |
|---|---|---|---|
next | n | Execute next line (don’t enter functions) | n |
step | s | Execute line and enter functions | s |
continue | c | Resume execution until next breakpoint | c |
return | r | Continue until current function returns | r |
until | unt | Continue until line greater than current | unt 10 |
jump | j | Jump to specific line | j 15 |
| Command | Shortcut | Description | Example |
|---|---|---|---|
print | p | Print expression value | p variable_name |
pp | pp | Pretty-print expression | pp complex_dict |
args | a | Show function arguments | a |
list | l | Show source code around current line | l |
longlist | ll | Show entire function source | ll |
where | w | Show stack trace | w |
up | u | Move up one stack frame | u |
down | d | Move down one stack frame | d |
| Command | Description | Example |
|---|---|---|
break / b | Set breakpoint | b 10 (line 10) |
tbreak | Set temporary breakpoint | tbreak function_name |
condition | Add condition to breakpoint | condition 1 x > 5 |
clear | Clear breakpoints | clear 1 |
disable | Disable breakpoint | disable 1 |
enable | Enable breakpoint | enable 1 |
| Command | Description | Example |
|---|---|---|
! prefix | Execute Python statement | !variable = 10 |
| Direct assignment | Modify variable | !sentiment_score = 0.9 |
| Command | Shortcut | Description |
|---|---|---|
quit | q | Exit debugger and terminate program |
exit | Exit debugger and terminate program | |
help | h | Show help for commands |
alias | Create command aliases |
1import pdb
2
3def helper_function(x):
4 return x * 2
5
6def main_function(a, b):
7 pdb.set_trace()
8 result1 = helper_function(a) # Line 9
9 result2 = helper_function(b) # Line 10
10 return result1 + result2
11
12print(main_function(3, 4))
1(Pdb) n
2> /script.py(10)main_function()
3-> result2 = helper_function(b)
Next executes line 9 completely without entering helper_function.
1(Pdb) s
2> /script.py(5)helper_function()
3-> return x * 2
Step enters into helper_function to debug it line by line.
1import pdb
2
3def calculate_sentiment(text):
4 words = text.split()
5 pdb.set_trace()
6 sentiment_score = len(words) * 0.1
7 return sentiment_score
8
9result = calculate_sentiment("This is a test sentence")
At the breakpoint:
1(Pdb) p text
2'This is a test sentence'
3
4(Pdb) p words
5['This', 'is', 'a', 'test', 'sentence']
6
7(Pdb) p sentiment_score
8*** NameError: name 'sentiment_score' is not defined
9
10(Pdb) n
11> /script.py(7)calculate_sentiment()
12-> return sentiment_score
13
14(Pdb) p sentiment_score
150.5
1import pdb
2
3def process_data():
4 data = {
5 'users': [{'id': 1, 'name': 'Alice'}, {'id': 2, 'name': 'Bob'}],
6 'settings': {'theme': 'dark', 'notifications': True}
7 }
8 pdb.set_trace()
9 return data
10
11process_data()
At the breakpoint:
1(Pdb) p data
2{'users': [{'id': 1, 'name': 'Alice'}, {'id': 2, 'name': 'Bob'}], 'settings': {'theme': 'dark', 'notifications': True}}
3
4(Pdb) pp data
5{'settings': {'notifications': True, 'theme': 'dark'},
6 'users': [{'id': 1, 'name': 'Alice'}, {'id': 2, 'name': 'Bob'}]}
1import pdb
2
3def calculate_total(price, quantity, tax_rate):
4 pdb.set_trace()
5 subtotal = price * quantity
6 total = subtotal * (1 + tax_rate)
7 return total
8
9result = calculate_total(10.50, 3, 0.08)
At the breakpoint:
1(Pdb) a
2price = 10.5
3quantity = 3
4tax_rate = 0.08
1import pdb
2
3def process_score(score):
4 pdb.set_trace()
5 if score < 0:
6 score = 0
7 elif score > 100:
8 score = 100
9 return score
10
11result = process_score(150)
At the breakpoint:
1(Pdb) p score
2150
3
4(Pdb) !score = 95
5
6(Pdb) p score
795
8
9(Pdb) c
1095
1import pdb
2
3def calculate_discount(price, customer_type):
4 pdb.set_trace()
5 if customer_type == 'premium':
6 discount = 0.20
7 elif customer_type == 'standard':
8 discount = 0.10
9 else:
10 discount = 0.0
11
12 final_price = price * (1 - discount)
13 return final_price
14
15result = calculate_discount(100, 'standard')
At the breakpoint:
1(Pdb) p customer_type
2'standard'
3
4(Pdb) !customer_type = 'premium'
5
6(Pdb) c
7# Now calculates with premium discount
1import pdb
2
3def complex_function():
4 for i in range(10):
5 result = i * 2
6 print(result)
7
8complex_function()
Run with PDB:
1python -m pdb script.py
Set breakpoint at line 5:
1(Pdb) b 5
2Breakpoint 1 at /path/to/script.py:5
3
4(Pdb) c
5> /path/to/script.py(5)complex_function()
6-> result = i * 2
1(Pdb) b 5, i > 5
2Breakpoint 2 at /path/to/script.py:5
3
4(Pdb) c
5# Only stops when i > 5
1(Pdb) b
2Num Type Disp Enb Where
31 breakpoint keep yes at /path/to/script.py:5
4 breakpoint already hit 6 times
52 breakpoint keep yes at /path/to/script.py:5
6 stop only if i > 5
7
8(Pdb) disable 1
9(Pdb) enable 1
10(Pdb) clear 1
11Deleted breakpoint 1
1(Pdb) l
2 1 import pdb
3 2
4 3 def calculate_total(items):
5 4 pdb.set_trace()
6 5 -> total = 0
7 6 for item in items:
8 7 total += item['price']
9 8 return total
10 9
11 10 result = calculate_total([{'price': 10}, {'price': 20}])
12
13(Pdb) l 1, 20
14# Shows lines 1-20
1(Pdb) ll
2 3 def calculate_total(items):
3 4 pdb.set_trace()
4 5 -> total = 0
5 6 for item in items:
6 7 total += item['price']
7 8 return total
8# Shows entire function
1import pdb
2
3def level_3():
4 pdb.set_trace()
5 return "Level 3"
6
7def level_2():
8 return level_3()
9
10def level_1():
11 return level_2()
12
13level_1()
At the breakpoint:
1(Pdb) w
2 /script.py(11)<module>()
3-> level_1()
4 /script.py(9)level_1()
5-> return level_2()
6 /script.py(6)level_2()
7-> return level_3()
8> /script.py(4)level_3()
9-> return "Level 3"
1(Pdb) up
2> /script.py(6)level_2()
3-> return level_3()
4
5(Pdb) up
6> /script.py(9)level_1()
7-> return level_2()
8
9(Pdb) down
10> /script.py(6)level_2()
11-> return level_3()
When a program crashes, use PDB to inspect its state at crash time:
1python -m pdb script.py
If an exception occurs, PDB automatically starts and allows inspection.
1# script.py
2def divide_numbers(a, b):
3 return a / b
4
5def main():
6 result = divide_numbers(10, 0)
7 print(result)
8
9if __name__ == '__main__':
10 main()
Run with PDB:
1python -m pdb script.py
Output:
1> /path/to/script.py(1)<module>()
2-> def divide_numbers(a, b):
3(Pdb) c
4Traceback (most recent call last):
5 File "/usr/lib/python3.9/pdb.py", line 1723, in main
6 pdb._runscript(mainpyfile)
7 ...
8ZeroDivisionError: division by zero
9Uncaught exception. Entering post mortem debugging
10> /path/to/script.py(3)divide_numbers()
11-> return a / b
12(Pdb) p a
1310
14(Pdb) p b
150
Alternatively, call pdb.post_mortem() after exception:
1import pdb
2import sys
3import traceback
4
5try:
6 result = 10 / 0
7except:
8 type, value, tb = sys.exc_info()
9 traceback.print_exc()
10 pdb.post_mortem(tb)
1import pdb
2
3def analyze_sentiment(text):
4 """Analyze sentiment of text."""
5 words = text.split()
6
7 # Calculate sentiment score
8 positive_words = ['good', 'great', 'excellent', 'happy']
9 negative_words = ['bad', 'terrible', 'awful', 'sad']
10
11 pdb.set_trace() # Set breakpoint here
12
13 positive_count = sum(1 for word in words if word.lower() in positive_words)
14 negative_count = sum(1 for word in words if word.lower() in negative_words)
15
16 if len(words) == 0:
17 return 0.0
18
19 sentiment_score = (positive_count - negative_count) / len(words)
20 return sentiment_score
21
22# Test the function
23text = "This is a great and excellent day"
24result = analyze_sentiment(text)
25print(f"Sentiment score: {result}")
1> /script.py(14)analyze_sentiment()
2-> positive_count = sum(1 for word in words if word.lower() in positive_words)
3
4(Pdb) p words
5['This', 'is', 'a', 'great', 'and', 'excellent', 'day']
6
7(Pdb) p positive_words
8['good', 'great', 'excellent', 'happy']
9
10(Pdb) n
11> /script.py(15)analyze_sentiment()
12-> negative_count = sum(1 for word in words if word.lower() in negative_words)
13
14(Pdb) p positive_count
152
16
17(Pdb) n
18> /script.py(17)analyze_sentiment()
19-> if len(words) == 0:
20
21(Pdb) p negative_count
220
23
24(Pdb) c
25Sentiment score: 0.2857142857142857
| Scenario | Use PDB | Alternative |
|---|---|---|
| Complex logic bugs | Yes | Logging |
| Intermittent issues | Yes | Logging |
| Production debugging | No | Logging |
| Quick value checks | No | Print statements |
| Algorithm verification | Yes | Unit tests |
| Understanding code flow | Yes | Code review |
breakpoint() (Python 3.7+) for cleaner syntax| Pitfall | Problem | Solution |
|---|---|---|
| Too many breakpoints | Slows debugging | Use conditional breakpoints |
| Forgetting breakpoints in code | Production issues | Code review process |
Not using ll | Missing context | Use ll to see full function |
| Ignoring stack traces | Missing call chain | Use w command regularly |
Important
Always remove or comment out
pdb.set_trace()andbreakpoint()calls before deploying code to production. Use environment variables to conditionally enable debugging in development.
1(Pdb) alias pl pp locals()
2(Pdb) pl
3# Pretty-prints all local variables
Create .pdbrc in home directory:
1# ~/.pdbrc
2# Aliases
3alias pl pp locals()
4alias pg pp globals()
5alias pd pp dir()
6
7# Auto-execute commands
8c
1(Pdb) !import math
2(Pdb) !result = math.sqrt(16)
3(Pdb) p result
44.0
PDB is Python’s built-in interactive debugger for diagnosing and resolving issues in Python applications. With PDB, breakpoints can be set, code can be stepped through, variables can be inspected and modified, and Python expressions can be evaluated interactively. PDB allows for in-depth understanding of code execution flow and aids in identifying root causes more efficiently than traditional print-based debugging. Post-mortem debugging enables inspection of program state after crashes, making it invaluable for diagnosing production issues.
PDB provides powerful interactive debugging capabilities built into Python. By setting strategic breakpoints, stepping through code execution, inspecting and modifying variables in real-time, and performing post-mortem analysis of crashes, developers can diagnose complex issues that would be difficult or impossible to debug with print statements alone. Mastering PDB commands and workflows significantly enhances debugging efficiency and code understanding.