Skip to main content

08.3 - Debugging Techniques

Theory 20 min Intermediate

Reading a Traceback

When an exception occurs, Python prints a traceback showing the call stack:

Traceback (most recent call last):
File "app.py", line 15, in main ← outermost call
result = process_data(raw_input)
File "app.py", line 8, in process_data ← called by main
return int(data.strip())
ValueError: invalid literal for int() with base 10: 'abc'
↑ ↑
exception type message

Read from bottom to top: the last line shows the error; the stack shows how you got there.


breakpoint() — Built-in Debugger

Python 3.7+ has a built-in breakpoint() function:

def calculate(a, b):
result = a * 2
breakpoint() # execution pauses here → opens pdb
return result + b

calculate(5, 3)

pdb Commands

When the debugger pauses execution:

CommandShortAction
helphShow all commands
nextnExecute next line (step over)
stepsStep into function call
returnrRun until current function returns
continuecContinue until next breakpoint
quitqQuit debugger
print xp xPrint value of x
listlShow source code around current line
wherewShow call stack
up/downu/dMove up/down the call stack

VS Code Debugger

  1. Click the gutter to set a breakpoint (red dot)
  2. Press F5 and select "Python File"
  3. Use the toolbar: Continue (F5), Step Over (F10), Step Into (F11), Step Out (Shift+F11)
  4. Inspect variables in the Variables panel

launch.json for custom debug configurations:

{
"version": "0.2.0",
"configurations": [
{
"name": "Python: Current File",
"type": "python",
"request": "launch",
"program": "${file}",
"console": "integratedTerminal",
"justMyCode": false,
"env": {"DEBUG": "1"}
}
]
}

assert Statements

Assertions catch programming errors (not user input errors):

def process(items):
assert isinstance(items, list), f"Expected list, got {type(items).__name__}"
assert len(items) > 0, "Items cannot be empty"
return [item.upper() for item in items]

# Python can disable assertions with -O flag
# Never use assert for security or input validation!

Useful Debugging Techniques

# 1. Print with context
print(f"DEBUG: {variable_name=}") # prints "variable_name=value"

# 2. Check object state
import pprint
pprint.pprint(complex_dict) # pretty-printed dict

# 3. Inspect types
print(type(obj).__mro__) # class hierarchy
print(dir(obj)) # all attributes

# 4. Measure performance
import cProfile
cProfile.run("my_function()")

# 5. Memory usage
import tracemalloc
tracemalloc.start()
# ... your code ...
snapshot = tracemalloc.take_snapshot()

Key Vocabulary

TermDefinition
TracebackStack trace showing the sequence of function calls that led to an exception
breakpoint()Built-in function to pause execution and enter the debugger
pdbPython Debugger — interactive source code debugger
Step overExecute next line without entering function bodies
Step intoEnter the function being called
assertStatement that raises AssertionError if condition is False
Call stackThe sequence of active function calls at a point in execution

Summary

  • Read tracebacks bottom-to-top: the last line shows the error, the stack shows the path
  • breakpoint() (Python 3.7+) pauses execution and opens pdb interactively
  • Key pdb commands: n (next), s (step into), c (continue), p x (print x), q (quit)
  • VS Code debugger provides a visual interface with the same capabilities
  • Use assert to check programming invariants — never for user input validation
  • f"{var=}" is a quick debug print that shows both variable name and value