Debugging and the debugger
Be able to use breakpoints, read stack traces and isolate a fault systematically.
Prerequisites
- DPython — functions, scope and exceptionsrequired
- DTesting with pytestrequired
Intuition
Read the stack trace from the bottom up. The bottom line is the error; above it is the route that got you there. The last line in your own code is nearly always where you should start.
Traceback (most recent call last):
File "cli.py", line 12, in main
resultat = trana(X, y) ← your code
File "model.py", line 45, in trana
return X @ w ← your code, this is where it happens
ValueError: matmul: mismatch (100,5) (4,) ← the error
The error says: X has 5 columns but w has 4 elements. Now you know what you are looking for.
The systematic method: reproduce → shrink → hypothesis → test → change one thing.
Code
# 1. A breakpoint — stop and look
def trana(X, y):
breakpoint() # run the program; you land in pdb
return X @ w
# In pdb: p X.shape (show) n (next line) s (step into) c (continue) q (quit)
# 2. Checks that speak up straight away instead of much later
assert X.shape[1] == len(w), f"X has {X.shape[1]} columns but w has {len(w)}"
# 3. Logging instead of print — it can be switched off and it has levels
import logging
log = logging.getLogger(__name__)
log.debug("X=%s y=%s", X.shape, y.shape)
# 4. Bisection in git when something stopped working
# git bisect start; git bisect bad; git bisect good <old-commit>
Shrink the fault. Can it be reproduced with 5 rows of data instead of 50 000? With one function instead of the whole program? Nine times out of ten you find the fault while shrinking — before you have even started the debugger.
Mastery means
- Reads a stack trace and finds the relevant line
- Isolates a fault systematically instead of guessing
- Uses breakpoints
Sign in to do the exercises and build your mastery up.