| name | py-debug |
|---|---|
| description | Python debugging expert. Use when the user needs to debug, profile, or trace Python code — e.g. "how do I debug this", "find the memory leak", "why is this slow", "add a breakpoint", "profile this function". |
Not sure what's wrong? Start with ipdb — step through the code and observe. Once you can characterize the problem (slow, leaking memory, crashing), switch to the matching profiler.
| Problem | Tool |
|---|---|
| Need to step through execution interactively | ipdb |
| Which function is slow? (whole-program overview) | cProfile (stdlib) |
| Identifying slow lines in a function | line-profiler |
| Profile a running process without code changes | py-spy |
| Investigating memory growth or leaks | memory-profiler |
| Hard to read debug output (nested dicts, long lists, tracebacks) | rich |
- Lock the bug with a failing test once reproduced — before patching incorrect behavior, capture the faulty behavior as a regression test (through the public interface where one exists), so the fix is verified and the bug can't silently regress; skip this for pure performance or memory investigations, where a test through the public interface is rarely the right artifact
- Suggest only one tool per problem — don't list all options and ask the user to choose
- Explain the fit in one sentence before showing usage — e.g. "Since you want line-by-line timing,
line-profileris the right tool here." - Prefer
ipdbas the default for general debugging; only reach for profilers when the problem is clearly performance- or memory-related - For performance, start broad then zoom in — use
cProfileto find the slow function, thenline-profilerto find the slow line - Use
py-spywhen you can't modify the code — running process, production, or quick triage - Place
set_trace()just before the suspect line, not at the top of the function - Never add profiling decorators to production code paths — always remind the user to remove them before deploying
Interactive debugger with IPython features (tab completion, syntax highlighting).
import ipdb
ipdb.set_trace() # Pause execution herePost-mortem debugging — drop into the debugger at the point of an unhandled exception:
try:
risky_operation()
except Exception:
import ipdb; ipdb.post_mortem()Tip:
breakpoint()(Python 3.7+) is equivalent toimport pdb; pdb.set_trace(). SetPYTHONBREAKPOINT=ipdb.set_traceto make it launch ipdb instead.
Function-level CPU profiler — use to find which functions are slow before drilling into lines with line-profiler.
python -m cProfile -s cumtime script.py | head -20import cProfile, pstats
profiler = cProfile.Profile()
profiler.enable()
main()
profiler.disable()
stats = pstats.Stats(profiler)
stats.sort_stats("cumulative")
stats.print_stats(10) # Top 10 functionsSampling profiler that attaches to running processes — no code changes needed. Use for production profiling or when you can't modify the source.
# Profile a script
py-spy record -o profile.svg -- python script.py
# Attach to a running process
py-spy top --pid 12345
# Generate flamegraph from running process
py-spy record -o profile.svg --pid 12345Line-by-line memory usage profiling to find memory leaks.
from memory_profiler import profile
@profile
def my_func():
data = [1] * 10**6 # Shows memory per lineLine-by-line execution time profiling to identify bottlenecks.
from line_profiler import profile
@profile
def slow_func():
total = sum(range(10**6)) # Shows time per lineUse rich to replace ad-hoc print debugging when output is hard to read — nested dicts, long lists, exceptions with stack traces. It's not a debugger; use it when the problem is visibility of data, not stepping through logic.
from rich import print
print(some_nested_dict) # Pretty-printed with syntax highlightingFor better tracebacks across the whole program:
from rich.traceback import install
install(show_locals=True) # All exceptions now show local variables