5.4 KiB
Stepping through code in ipdb gets you pdb with IPython's tab completion. Python debugging tools cover slow code too: py-spy profiles a running Python program.
How to choose:
- pdb with tab completion and syntax highlighting: ipdb
- Finding where a running program spends its time, without changing it: py-spy
- A full-screen debugger in your terminal: PuDB
- A timeline of every function call: VizTracer
- Memory use and leaks, native extensions included: Memray
- A readable call tree of where your script spends wall-clock time: pyinstrument
- CPU, GPU, and memory use, line by line: Scalene
- SQL queries and request data on Django pages: Django Debug Toolbar
- Every Django request and its SQL queries, recorded to inspect later: Silk
- Print debugging that labels each value: IceCream
- The same toolbar on Flask pages: Flask-DebugToolbar
ipdb gives you IPython's debugger, with tab completion, syntax highlighting, and better tracebacks, behind the same interface as pdb. Drop into it with import ipdb; ipdb.set_trace(), or call ipdb.pm() for post-mortem debugging after an exception.
py-spy is a sampling profiler that shows what a program spends its time on without restarting it or modifying its code. It runs outside the profiled process, which makes it safe to use on production code. py-spy record writes a flame graph of a process ID or a command you hand it, and py-spy dump prints the call stack where a program is hung.
PuDB keeps the source, the stack, breakpoints, and variables all visible at once in one full-screen terminal view. Start it from your code with from pudb import set_trace; set_trace(), or run a whole script with python -m pudb my-script.py.
VizTracer records every function's entry and exit time across a whole run, which helps catch sporadic performance issues. Run your script with viztracer my_script.py instead of python, then open the result.json it writes with vizviewer.
Memray tracks allocations in Python code, native extension modules, and the interpreter itself, to find memory leaks and the code behind high memory use. Profiling takes two steps: memray run saves the allocations to a file, then memray flamegraph builds a report from it.
pyinstrument measures wall-clock time, so time spent downloading data, reading files, and talking to databases counts too. Type pyinstrument script.py instead of python script.py, or wrap a block in with pyinstrument.profile():.
Scalene profiles CPU, GPU, and memory line by line and per function. It separates time in Python from time in native code, so you can focus on code you can change. Run scalene run your_prog.py, then scalene view opens the profile in your browser.
Django Debug Toolbar adds panels to your pages for the current request's SQL queries, templates, cache calls, and more. Its setup adds the app, its URLs, and its middleware, and shows the toolbar only to IP addresses in INTERNAL_IPS.
Silk intercepts and stores HTTP requests and database queries, then shows them in its own UI: each request's time, query count, headers, and bodies. Its setup adds its middleware and app to your settings, includes silk.urls in your URLconf, and runs migrate. The UI then lives at /silk/.
IceCream's ic() prints each expression along with its value. With no arguments, it prints the file, line, and function it runs in. Since ic() returns its arguments, you can wrap an expression in existing code without rewriting the line.
Flask-DebugToolbar is a port of Django Debug Toolbar to Flask. Wrap your app in DebugToolbarExtension(app), and the toolbar shows up in HTML responses while debug mode is on.
Both debuggers also open from the built-in breakpoint(): set the PYTHONBREAKPOINT environment variable to ipdb.set_trace or pudb.set_trace. Among profilers, sampling ones like py-spy, pyinstrument, and Scalene cost much less overhead than tracing ones, while a tracer like VizTracer records every call and gives you more information.