Reading Tracebacks
# Read tracebacks BOTTOM to TOP
# Bottom = actual error, Top = call entry point
def calculate(data):
return sum(data) / len(data)
calculate([]) # ZeroDivisionError
# Traceback (most recent call last):
# File "app.py", line 6, in <module>
# calculate([])
# File "app.py", line 2, in calculate
# return sum(data) / len(data)
# ZeroDivisionError: division by zerobreakpoint() β Interactive Debugger
def process_order(order):
total = 0
for item in order["items"]:
breakpoint() # Execution pauses here
# pdb commands: n=next, s=step, c=continue, p x=print, q=quit
total += item["price"] * item["qty"]
return totalCommon Error Types
# NameError β variable doesn't exist
print(x) # NameError: name 'x' is not defined
# TypeError β wrong type
"2" + 2 # TypeError: can only concatenate str to str
# KeyError β dict key missing
d = {"a": 1}
d["b"] # KeyError: 'b'
d.get("b", None) # Safe alternative
# AttributeError β bad attribute
None.upper() # AttributeError: 'NoneType' has no attribute 'upper'
# IndexError β out of range
[1, 2][5] # IndexError: list index out of range
# ValueError β right type, bad value
int("abc") # ValueError: invalid literalDebug Techniques
# f-string debugging (Python 3.8+)
x = compute_something()
print(f"{x=}") # prints: x=42
# Early assertions
assert isinstance(data, list), f"Expected list, got {type(data)}"
assert len(data) > 0, "Data cannot be empty"
# Post-mortem in shell
import pdb; pdb.pm() # after uncaught exceptionDebugging with breakpoint(): Beyond print()
Sprinkling print() works but is slow and messy. Python's built-in debugger lets you pause execution and inspect everything live β variables, the call stack, and step through line by line. Drop breakpoint() anywhere and the program stops there in an interactive prompt.
def process(data):
total = 0
for item in data:
breakpoint() # execution pauses here β interactive pdb prompt
total += item
return total
| pdb command | Does |
|---|---|
n (next) | run current line, don't step into calls |
s (step) | step into a function call |
c (continue) | resume until next breakpoint |
p x | print variable x |
l (list) | show surrounding code |
Why it wins over print: you inspect any variable on demand (not just the ones you remembered to print), change values to test fixes, and walk up the call stack (w) to see how you got here β all without re-running. Set PYTHONBREAKPOINT=0 to disable every breakpoint() in production. IDEs (VS Code, PyCharm) wrap the same engine in a visual debugger with clickable breakpoints β same concepts, nicer UI.
ποΈ Practical Exercise
Practice debugging skills:
- Trigger an exception on purpose and read the traceback from bottom to top.
- Insert a
breakpoint()and step through code interactively, inspecting variables. - Identify which line and which error type a traceback points to.
- Add a few strategic
print()orlogging.debug()statements to trace a bug.
π₯ Challenge Exercise
Take a small buggy function (e.g. one that miscomputes an average or has an off-by-one error) and debug it methodically: read the traceback, reproduce the issue with a minimal example, use breakpoint() to inspect state, form a hypothesis, fix it, and confirm with a test. Bonus: replace your debugging prints with proper logging at the right levels so the diagnostics can stay in the code.
π Summary
- Read tracebacks from the bottom: the last line names the error type and message, and the frames above show the call path.
breakpoint()drops into the interactive pdb debugger to step through code and inspect variables.- Common errors include
TypeError,ValueError,KeyError,IndexError, andAttributeError. - A debugger is more powerful than scattered prints, letting you pause, step, and examine state.
- Reproduce bugs with a minimal example to isolate the cause.
- Prefer
loggingover temporary prints so diagnostics are controllable and removable.
Interview Questions on Debugging
- How do you read a Python traceback?
- What is the difference between a syntax error and a runtime error?
- What does
breakpoint()do? - What are common Python error types and their causes?
- What is the difference between debugging with prints and using a debugger?
- How do you debug a problem you cannot reproduce locally?
- What is a minimal reproducible example and why is it useful?
Related Topics
FAQ
Start at the bottom: the final line states the exception type and message. The lines above it form the call stack, newest last, showing the file, line number, and code that led to the error. The bottom-most frame in your own code is usually where to look first.
print debugging and a debugger? +Prints show specific values but require editing and rerunning. A debugger like pdb (via breakpoint()) lets you pause execution, step line by line, inspect and change variables, and explore the call stack interactively β far more efficient for tricky bugs.
Add structured logging to capture context when it happens, gather the exact inputs and environment, and try to build a minimal reproducible example. Intermittent bugs often stem from timing, external state, or unhandled edge cases the logs will reveal.
It is the smallest, self-contained snippet that still triggers the bug. Stripping away unrelated code isolates the real cause, makes the problem easier to reason about, and is what others need to help you.
