Complete Python debugging reference covering print debugging, pdb commands, IPython/ipdb, logging, exception handling, VS Code debugging, memory debugging, performance profiling, and common error quick fixes. With examples for every technique.
| Command / Pattern | Description | Example |
print(x) | Print value to stdout | print(result) → 42 |
print(f"{x=}") | Self-documenting f-string (3.8+) | print(f"{name=}") → name='Alice' |
print(f"{expr=}") | Show expression and its value | print(f"{len(data)=}") → len(data)=5 |
print(repr(x)) | Show unambiguous representation | print(repr("hi\n")) → 'hi\n' |
print(type(x)) | Show the type of a variable | print(type([])) → <class 'list'> |
print(vars(obj)) | Show instance attributes as dict | print(vars(user)) → {'name': 'Bob'} |
print(dir(obj)) | List all attributes and methods | dir(str) lists string methods |
from pprint import pprint pprint(data) | Pretty-print nested structures | pprint({"a": [1,2,3]}) |
pprint(data, depth=2) | Limit nesting depth in output | Truncates beyond depth 2 |
print(*lst, sep="\n") | Print list items one per line | print(*[1,2,3], sep="\n") |
| Command / Pattern | Description | Example |
breakpoint() | Set breakpoint (Python 3.7+) | Add in code, runs pdb at that line |
import pdb; pdb.set_trace() | Set breakpoint (older style) | Equivalent to breakpoint() |
python -m pdb script.py | Run script under pdb from start | Stops at first line |
n (next) | Execute next line, skip into functions | Step over function calls |
s (step) | Step into function call | Enter the function body |
c (continue) | Continue until next breakpoint | Resume normal execution |
r (return) | Continue until current function returns | Finish current function |
p expr | Print expression value | p len(data) → 5 |
pp expr | Pretty-print expression value | pp locals() |
l (list) | Show 11 lines around current line | l 1, 20 shows lines 1-20 |
ll (longlist) | Show entire source of current function | See full function body |
w (where) | Print stack trace | Shows call stack with current position |
u (up) | Move up one frame in the stack | Inspect caller's context |
d (down) | Move down one frame in the stack | Return to callee's context |
b 42 | Set breakpoint at line 42 | b mymod.py:10 for other files |
b func_name | Break when function is called | b process_data |
cl (clear) | Clear all breakpoints | cl 1 clears breakpoint #1 |
q (quit) | Quit the debugger | Exits program immediately |
h (help) | List commands or get help | h break shows break usage |
| Command / Pattern | Description | Example |
pip install ipdb | Install ipdb (pdb + IPython) | Tab completion, syntax highlighting |
import ipdb; ipdb.set_trace() | Set ipdb breakpoint explicitly | Enhanced pdb interface |
PYTHONBREAKPOINT=ipdb.set_trace | Use ipdb for breakpoint() | Set env var before running script |
%debug | Post-mortem debug in IPython/Jupyter | Run after an exception to inspect |
%pdb on | Auto-enter debugger on exception | Toggle automatic post-mortem mode |
from IPython import embed embed() | Drop into IPython shell mid-code | Full IPython REPL at that point |
python -m pdb -c continue script.py | Post-mortem on unhandled exception | Runs normally, debugs on crash |
PYTHONBREAKPOINT=0 | Disable all breakpoints | Skip breakpoint() calls in prod |
| Command / Pattern | Description | Example |
import logging logging.debug("msg") | Log at DEBUG level (lowest) | For detailed diagnostic info |
logging.info("msg") | Log at INFO level | Confirm things work as expected |
logging.warning("msg") | Log at WARNING level (default) | Something unexpected happened |
logging.error("msg") | Log at ERROR level | A serious problem occurred |
logging.critical("msg") | Log at CRITICAL level (highest) | Program may not continue |
logging.basicConfig(level=logging.DEBUG) | Set minimum log level | Shows DEBUG and above |
logging.basicConfig( format="%(asctime)s %(levelname)s %(message)s" ) | Custom log format | 2025-01-15 10:30:00 INFO msg |
logging.basicConfig(filename="app.log") | Log to file instead of console | Writes to app.log |
logger = logging.getLogger(__name__) | Create named logger | Best practice for modules |
handler = logging.FileHandler("app.log") logger.addHandler(handler) | Add file handler to logger | Output to file + console |
logging.exception("msg") | Log ERROR with traceback | Use inside except blocks |
logger.setLevel(logging.DEBUG) | Set level on specific logger | Override root logger level |
| Command / Pattern | Description | Example |
try: risky() except Exception as e: print(e) | Catch and print exception message | Basic error handling pattern |
except (TypeError, ValueError) as e: | Catch multiple exception types | Handle related errors together |
import traceback traceback.print_exc() | Print full traceback in except block | Shows file, line, and call stack |
traceback.format_exc() | Get traceback as string | For logging or custom output |
traceback.print_stack() | Print current stack (no exception) | See how you got to this point |
import sys sys.exc_info() | Get (type, value, traceback) tuple | Detailed exception introspection |
raise ValueError("msg") from e | Chain exceptions with cause | Preserves original traceback |
try: ... except: ... else: on_success() | else runs if no exception | Separate success logic |
try: ... finally: cleanup() | finally always runs | Guaranteed cleanup |
import warnings warnings.warn("deprecation") | Issue a warning (non-fatal) | warnings.warn("old API", DeprecationWarning) |
| Command / Pattern | Description | Example |
F5 | Start / continue debugging | Launches debug session |
F9 | Toggle breakpoint on current line | Click gutter to set visually |
F10 | Step over (next line) | Skip into function calls |
F11 | Step into function | Enter function body |
Shift+F11 | Step out of function | Return to caller |
Shift+F5 | Stop debugging | Terminate debug session |
| Conditional breakpoint | Right-click gutter → Expression | i == 50 breaks when true |
| Logpoint | Right-click gutter → Log Message | {x} prints x without stopping |
| Watch panel | Add expressions to monitor | Watch len(data) live |
| Debug Console | Evaluate expressions at breakpoint | Run any Python expression |
launch.json — "args" | Pass CLI arguments to script | "args": ["--verbose", "input.txt"] |
launch.json — "env" | Set environment variables | "env": {"DEBUG": "1"} |
| Command / Pattern | Description | Example |
import tracemalloc tracemalloc.start() | Start tracking memory allocations | Built-in, no install needed |
snap = tracemalloc.take_snapshot() snap.statistics("lineno") | Show memory usage per line | Find top memory consumers |
snap.statistics("traceback") | Show memory by full traceback | Trace allocation origin |
tracemalloc.get_traced_memory() | Get (current, peak) memory usage | Returns bytes tuple |
import sys sys.getsizeof(obj) | Size of single object in bytes | sys.getsizeof([1,2,3]) → 88 |
import objgraph objgraph.show_most_common_types() | List most common object types | Find object accumulation |
objgraph.show_growth() | Show new objects since last call | Detect memory leaks over time |
import gc gc.collect() | Force garbage collection | Returns number of collected objects |
gc.get_objects() | List all tracked objects | Inspect what GC is tracking |
gc.set_debug(gc.DEBUG_LEAK) | Enable GC debug output | Prints uncollectable objects |
| Command / Pattern | Description | Example |
python -m cProfile script.py | Profile entire script | Shows time per function |
python -m cProfile -s cumtime script.py | Sort by cumulative time | Find slowest code paths |
import cProfile cProfile.run("func()") | Profile a specific function call | Inline profiling |
cProfile.run("func()", "output.prof") | Save profile data to file | Analyze with snakeviz or pstats |
python -m timeit "expr" | Time a small code snippet | python -m timeit "''.join(lst)" |
from timeit import timeit timeit("sum(range(100))", number=10000) | Time expression programmatically | Returns total seconds |
import time t0 = time.perf_counter() | Manual timing with high precision | elapsed = time.perf_counter() - t0 |
pip install line_profiler @profile def func(): ... | Line-by-line time profiling | kernprof -l -v script.py |
pip install memory_profiler @profile def func(): ... | Line-by-line memory profiling | python -m memory_profiler script.py |
pip install snakeviz snakeviz output.prof | Visualize cProfile output | Opens interactive browser chart |
| Error | Common Cause | Fix |
TypeError: unsupported operand | Mixing incompatible types | Check types with type(x); cast if needed |
TypeError: 'NoneType' not subscriptable | Function returned None | Ensure function has return; check for None |
TypeError: missing argument | Wrong number of arguments | Check function signature; use help(func) |
ValueError: invalid literal | Bad string-to-number conversion | int("abc") fails; validate input first |
ValueError: too many values to unpack | Mismatched unpacking | Use a, *rest = iterable for variable length |
KeyError: 'key' | Key not in dictionary | Use d.get("key", default) or "key" in d |
AttributeError: no attribute 'x' | Wrong type or misspelled attr | Check type(obj) and dir(obj) |
ImportError / ModuleNotFoundError | Module not installed or wrong env | pip install module; check sys.path |
IndentationError | Mixed tabs and spaces | Use 4 spaces; configure editor to show whitespace |
IndexError: list index out of range | Accessing beyond list length | Check len(lst); use if i < len(lst) |
RecursionError: maximum depth | Infinite recursion, no base case | Add base case; use sys.setrecursionlimit() |
FileNotFoundError | Wrong path or missing file | Use Path.exists(); check os.getcwd() |
UnicodeDecodeError | Wrong file encoding | Use open(f, encoding="utf-8") or "latin-1" |
NameError: name 'x' not defined | Variable not assigned or typo | Check spelling; ensure variable is in scope |
What is the difference between pdb and ipdb in Python?
pdb is Python's built-in debugger included in the standard library. ipdb is a third-party package that wraps pdb with IPython features like syntax highlighting, tab completion, and better introspection. You can use breakpoint() to invoke pdb, or set PYTHONBREAKPOINT=ipdb.set_trace to use ipdb instead. Both support the same core commands (n, s, c, p, etc.), but ipdb provides a more user-friendly interactive experience.
How do I debug Python code in VS Code?
To debug Python in VS Code: 1) Install the Python extension, 2) Set breakpoints by clicking the gutter next to line numbers, 3) Press F5 or use Run > Start Debugging, 4) Use the debug toolbar to step over (F10), step into (F11), step out (Shift+F11), or continue (F5). You can also add a launch.json configuration for custom settings like command-line arguments, environment variables, or Django/Flask debugging.
How do I find memory leaks in Python?
Use tracemalloc (built-in) to track memory allocations: call tracemalloc.start() at the beginning, then tracemalloc.take_snapshot() and snapshot.statistics('lineno') to see which lines allocate the most memory. For object-level analysis, use objgraph.show_most_common_types() to find objects accumulating in memory. The gc module can also help: gc.get_objects() lists all tracked objects, and gc.collect() forces garbage collection to reveal uncollectable cycles.
What is the best way to profile Python code performance?
For function-level profiling, use cProfile: run python -m cProfile -s cumtime script.py to see time spent in each function. For line-by-line profiling, use line_profiler with the @profile decorator and run kernprof -l -v script.py. For quick timing, use timeit: python -m timeit 'expression'. For memory profiling, use memory_profiler with @profile to see memory usage per line.