Debugging Tools¶
Effective debugging techniques for Python and frontend development.
Python Debugging¶
Built-in pdb¶
# Insert breakpoint
breakpoint() # Python 3.7+
import pdb; pdb.set_trace() # Older Python
# Conditional breakpoint
if user.id == 42:
breakpoint()
pdb Commands¶
h(elp) Show help
n(ext) Execute next line
s(tep) Step into function
c(ontinue) Continue until next breakpoint
r(eturn) Continue until function returns
q(uit) Quit debugger
l(ist) Show source code around current line
ll Show entire function
w(here) Show stack trace
u(p) Move up in stack
d(own) Move down in stack
p expr Print expression
pp expr Pretty print expression
a(rgs) Show function arguments
b lineno Set breakpoint at line
b func Set breakpoint at function
cl(ear) Clear breakpoints
ipdb (Enhanced pdb)¶
Benefits over pdb: - Tab completion - Syntax highlighting - Better tracebacks
pudb (Visual TUI Debugger)¶
# Install
pip install pudb
# Run script with pudb
python -m pudb script.py
# Set breakpoint
import pudb; pudb.set_trace()
Navigation:
- n — Next line
- s — Step into
- c — Continue
- q — Quit
- ? — Help
Async Debugging¶
import asyncio
async def debug_async():
# Breakpoint works in async
breakpoint()
await some_coroutine()
# Run with debug mode
asyncio.run(debug_async(), debug=True)
Post-Mortem Debugging¶
# Debug after exception
import pdb
try:
buggy_function()
except Exception:
pdb.post_mortem()
# Or use decorator
import functools
def debug_on_error(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except Exception:
import pdb
pdb.post_mortem()
return wrapper
@debug_on_error
def buggy_function():
pass
Remote Debugging¶
# Using remote-pdb
import remote_pdb
remote_pdb.set_trace(host='0.0.0.0', port=4444)
# Connect
telnet localhost 4444
pytest Debugging¶
Interactive Debugging¶
# Drop into debugger on failure
uv run pytest --pdb
# Drop into debugger at start of test
uv run pytest -s --pdb
# Run specific test with debugging
uv run pytest tests/test_api.py::test_create_user --pdb
Debugging Fixtures¶
import pytest
@pytest.fixture
def user(db):
breakpoint() # Debug fixture setup
return create_user(db)
def test_user(user):
breakpoint() # Debug test
assert user.is_active
Capturing Output¶
# Show print statements
uv run pytest -s
# Show local variables on failure
uv run pytest --showlocals
# Verbose output
uv run pytest -vv
Logging for Debugging¶
Quick Debug Logging¶
import logging
# Quick setup
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
def complex_function(data):
logger.debug(f"Input: {data}")
result = process(data)
logger.debug(f"Output: {result}")
return result
Structured Logging¶
import structlog
log = structlog.get_logger()
def process_order(order):
log.info("processing_order", order_id=order.id, items=len(order.items))
# ...
log.debug("order_validated", order_id=order.id, total=order.total)
Temporary Debug Logging¶
# Context manager for temporary debug level
import logging
from contextlib import contextmanager
@contextmanager
def debug_logging(logger_name=None):
logger = logging.getLogger(logger_name)
old_level = logger.level
logger.setLevel(logging.DEBUG)
try:
yield
finally:
logger.setLevel(old_level)
# Usage
with debug_logging("sqlalchemy.engine"):
db.execute(query) # SQL will be logged
Browser DevTools¶
Console¶
// Basic logging
console.log('Value:', value);
console.info('Info message');
console.warn('Warning');
console.error('Error');
// Formatted output
console.log('%c Important', 'color: red; font-weight: bold');
// Tables
console.table([{a: 1, b: 2}, {a: 3, b: 4}]);
// Groups
console.group('User Details');
console.log('Name:', user.name);
console.log('Email:', user.email);
console.groupEnd();
// Timing
console.time('fetch');
await fetch(url);
console.timeEnd('fetch'); // fetch: 123.45ms
// Stack trace
console.trace('Trace point');
// Assertions
console.assert(user.id > 0, 'User ID must be positive');
Breakpoints¶
Source Panel:
- Click line number to set breakpoint
- Right-click for conditional breakpoint
- debugger; statement in code
Types: - Line breakpoints - Conditional breakpoints - DOM breakpoints (on element changes) - XHR/Fetch breakpoints - Event listener breakpoints
Network Panel¶
Filtering:
- fetch — Only fetch requests
- xhr — Only XHR requests
- domain:api.example.com — By domain
- -domain:cdn.example.com — Exclude domain
- status-code:500 — By status code
Features: - Throttling (Slow 3G, Offline) - Preserve log across navigation - Disable cache
React DevTools¶
// Highlight component renders
// Components tab → Settings → Highlight updates
// Profile renders
// Profiler tab → Record → Interact → Stop
// Search components
// Components tab → Search box (Ctrl+F)
// Edit props in DevTools
// Components tab → Select component → Edit props
Vue DevTools¶
// Inspect component state
// Components tab → Select component → View state
// Track Vuex/Pinia state
// Vuex/Pinia tab → View mutations/actions
// Timeline
// Timeline tab → View component events
Network Debugging¶
curl¶
# Basic request
curl https://api.example.com/users
# With headers
curl -H "Authorization: Bearer token" https://api.example.com/users
# POST JSON
curl -X POST \
-H "Content-Type: application/json" \
-d '{"name": "test"}' \
https://api.example.com/users
# Verbose (see headers)
curl -v https://api.example.com/users
# Only response headers
curl -I https://api.example.com/users
# Follow redirects
curl -L https://example.com
# Save to file
curl -o output.json https://api.example.com/users
# With timing
curl -w "@curl-format.txt" -o /dev/null -s https://api.example.com/users
curl-format.txt:
time_namelookup: %{time_namelookup}s\n
time_connect: %{time_connect}s\n
time_appconnect: %{time_appconnect}s\n
time_pretransfer: %{time_pretransfer}s\n
time_redirect: %{time_redirect}s\n
time_starttransfer: %{time_starttransfer}s\n
----------\n
time_total: %{time_total}s\n
httpie¶
# Install
pip install httpie
# GET request
http https://api.example.com/users
# POST JSON
http POST https://api.example.com/users name=test email=test@example.com
# With auth
http -a user:pass https://api.example.com/users
http --bearer token https://api.example.com/users
# Headers
http https://api.example.com/users X-Custom-Header:value
# Form data
http -f POST https://api.example.com/login username=test password=secret
mitmproxy¶
Intercept and modify HTTP/HTTPS traffic.
# Install
pip install mitmproxy
# Start proxy
mitmproxy
# Or web interface
mitmweb
# Configure app to use proxy
export HTTP_PROXY=http://localhost:8080
export HTTPS_PROXY=http://localhost:8080
Memory Debugging¶
Python Memory Profiling¶
# Install
pip install memory_profiler
# Profile script
python -m memory_profiler script.py
# Profile function
from memory_profiler import profile
@profile
def memory_intensive():
large_list = [i for i in range(1000000)]
return sum(large_list)
Finding Memory Leaks¶
import tracemalloc
tracemalloc.start()
# Run suspicious code
result = memory_intensive_function()
snapshot = tracemalloc.take_snapshot()
top_stats = snapshot.statistics('lineno')
print("Top 10 memory allocations:")
for stat in top_stats[:10]:
print(stat)
objgraph (Object Reference Graphs)¶
import objgraph
# Find objects
objgraph.show_most_common_types(limit=10)
# Find growth between calls
objgraph.show_growth()
# Show references to object
objgraph.show_backrefs([obj], filename='refs.png')
Performance Debugging¶
Python Profiling¶
# cProfile
python -m cProfile -s cumtime script.py
# Line profiler
pip install line_profiler
kernprof -l -v script.py
Timing Code¶
import time
from contextlib import contextmanager
@contextmanager
def timer(name="Block"):
start = time.perf_counter()
try:
yield
finally:
elapsed = time.perf_counter() - start
print(f"{name}: {elapsed:.4f}s")
# Usage
with timer("Database query"):
results = db.execute(query)
VS Code Debugging¶
Python Configuration¶
.vscode/launch.json:
{
"version": "0.2.0",
"configurations": [
{
"name": "Python: Current File",
"type": "debugpy",
"request": "launch",
"program": "${file}",
"console": "integratedTerminal"
},
{
"name": "Python: FastAPI",
"type": "debugpy",
"request": "launch",
"module": "uvicorn",
"args": ["app.main:app", "--reload"],
"jinja": true
},
{
"name": "Python: pytest",
"type": "debugpy",
"request": "launch",
"module": "pytest",
"args": ["tests/", "-v"]
}
]
}
Node.js Configuration¶
{
"version": "0.2.0",
"configurations": [
{
"name": "Next.js",
"type": "node",
"request": "launch",
"cwd": "${workspaceFolder}",
"runtimeExecutable": "bun",
"runtimeArgs": ["dev"],
"skipFiles": ["<node_internals>/**"]
}
]
}
Quick Debug Techniques¶
# 1. Print debugging (sometimes fastest)
print(f"DEBUG: {variable=}") # Python 3.8+
# 2. Quick type check
print(f"Type: {type(obj)}, Value: {obj!r}")
# 3. Stack trace without exception
import traceback
traceback.print_stack()
# 4. Interactive exploration
import code
code.interact(local=locals())
# 5. Quick JSON dump
import json
print(json.dumps(data, indent=2, default=str))