Python Debugging Cheat Sheet

Core Python debugging commands, traceback inspection, runtime introspection, and practical troubleshooting workflows.

View
StandardDetailedCompact
Export
Copy the compact sheet, download it, or print it.
Download
`D` dense toggle · `C` copy all
## Breakpoints and pdb
Insert a built-in breakpoint
breakpoint()

# Pause execution and enter the configured debugger.

Drop into pdb at a specific line
import pdb; pdb.set_trace()

# Classic explicit breakpoint using pdb.

Run a script under pdb
python -m pdb app.py

# Start Python's debugger from the command line.

Run a script with pdb and arguments
python -m pdb app.py --env dev --port 8000

# Pass normal program arguments when starting under pdb.

pdb next
n

# Step over the current line without entering function calls.

pdb step
s

# Step into a called function.

pdb continue
c

# Resume execution until the next breakpoint.

pdb until
until

# Continue until a later line in the current frame.

pdb list source
l

# Show nearby source lines.

Print a variable in pdb
p variable_name

# Inspect the value of an expression.

Pretty-print in pdb
pp complex_object

# Print structured data in a readable format.

Show stack trace in pdb
w

# Display the current call stack.

Move up and down stack frames
u
d

# Navigate the traceback to inspect caller frames.

Set a breakpoint in pdb
b app.py:42

# Break on a file and line number from within the debugger.

## Tracebacks and Exceptions
Print the current exception traceback
import traceback

try:
    risky_call()
except Exception:
    traceback.print_exc()

# Dump the active exception traceback in an except block.

Format exception as a string
import traceback

try:
    risky_call()
except Exception:
    error_text = traceback.format_exc()

# Capture traceback output for logs or APIs.

Get exception info tuple
import sys

try:
    risky_call()
except Exception:
    exc_type, exc_value, exc_tb = sys.exc_info()

# Access exception type, value, and traceback explicitly.

Preserve context with raise from
try:
    parse_config()
except ValueError as exc:
    raise RuntimeError("Invalid configuration") from exc

# Chain exceptions to keep original failure context.

Enable faulthandler
import faulthandler
faulthandler.enable()

# Print Python tracebacks on fatal errors like segfaults.

Enable faulthandler from CLI
python -X faulthandler app.py

# Turn on fatal error tracebacks without code changes.

Show default warnings
python -Wd app.py

# Enable warnings that may help reveal bad behavior.

Run in Python development mode
python -X dev app.py

# Enable extra runtime checks and debug-friendly behavior.

## Runtime Inspection
Inspect object type and attributes
print(type(obj))
print(dir(obj))

# Quickly see what an object is and what it exposes.

Inspect an object's __dict__
print(vars(obj))

# Show instance attributes stored on an object.

Pretty print nested data
from pprint import pprint

pprint(payload)

# Display dicts and lists more readably.

Inspect a callable signature
import inspect

print(inspect.signature(func))

# See function parameters at runtime.

Print source code for a function
import inspect

print(inspect.getsource(func))

# Display source when available.

Show import search path
import sys

for path in sys.path:
    print(path)

# Debug why imports resolve incorrectly.

Show the file backing an imported module
import package_name

print(package_name.__file__)

# Confirm which module file was actually imported.

Inspect environment variables
import os

print(os.environ.get("APP_ENV"))

# Print environment values affecting program behavior.

## Logging and Diagnostics
Enable basic debug logging
import logging

logging.basicConfig(level=logging.DEBUG)
logging.debug("Starting debug run")

# Turn on debug-level logs quickly.

Use a readable log format
import logging

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s %(levelname)s %(name)s %(message)s',
)

# Add timestamps and levels to logs.

Log an exception with traceback
import logging

logger = logging.getLogger(__name__)

try:
    risky_call()
except Exception:
    logger.exception("Request processing failed")

# Emit the stack trace automatically in an except block.

Attach request or job context to logs
import logging

base_logger = logging.getLogger(__name__)
logger = logging.LoggerAdapter(base_logger, {"request_id": request_id})
logger.info("Processing request")

# Add contextual identifiers that make debugging easier.

Time a block of code
import time

start = time.perf_counter()
result = expensive_call()
elapsed = time.perf_counter() - start
print(f"Elapsed: {elapsed:.4f}s")

# Measure elapsed time around suspicious code paths.

Benchmark small snippets with timeit
python -m timeit "sum(range(1000))"

# Measure a small expression from the command line.

## pytest and unittest Debugging
Stop after first failure
pytest -x

# Fail fast and debug one issue at a time.

Drop into pdb on failure
pytest --pdb

# Open the debugger automatically when a test fails.

Disable output capture
pytest -s

# See print statements and standard output live.

Run one test function
pytest tests/test_api.py::test_create_user

# Narrow execution to one failing test quickly.

Re-run only last failed tests
pytest --lf

# Speed up debugging after a failing run.

Show local variables in tracebacks
pytest -vv --showlocals

# Reveal local state in failing assertions and exceptions.

Run unittest in verbose mode
python -m unittest -v tests.test_service

# See each test case as it runs.

## Profiling and Memory
Profile a script with cProfile
python -m cProfile app.py

# Collect function-level runtime statistics.

Sort cProfile output by total time
python -m cProfile -s tottime app.py

# Surface slow functions first.

Profile code inside Python
import cProfile
import pstats

profiler = cProfile.Profile()
profiler.enable()
expensive_call()
profiler.disable()
pstats.Stats(profiler).sort_stats('tottime').print_stats(20)

# Wrap a hot path in cProfile and print stats.

Track memory allocations with tracemalloc
import tracemalloc

tracemalloc.start()
# run code
snapshot = tracemalloc.take_snapshot()
for stat in snapshot.statistics('lineno')[:10]:
    print(stat)

# Capture memory allocation snapshots.

Inspect process memory and CPU with psutil
import os
import psutil

process = psutil.Process(os.getpid())
print(process.memory_info())
print(process.cpu_percent(interval=1.0))

# Check runtime resource usage from inside the process.

## Async, Web, and Production Troubleshooting
Enable asyncio debug mode
PYTHONASYNCIODEBUG=1 python app.py

# Expose slow callbacks and event-loop issues.

Enable debug mode programmatically
import asyncio

async def main():
    ...

asyncio.run(main(), debug=True)

# Turn on event loop debugging in code.

Start a simple HTTP server for quick testing
python -m http.server 8000

# Serve files locally to reproduce client behavior.

Profile import time
python -X importtime app.py

# See which imports are slowing startup.

Dump stack traces for all threads
import faulthandler
import signal

faulthandler.register(signal.SIGUSR1)

# Useful when an app appears hung or deadlocked.

Recommended next

No recommendations yet.