with one click
python-debugpy
Debug Python: pdb REPL + debugpy remote (DAP).
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Menu
Debug Python: pdb REPL + debugpy remote (DAP).
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Based on SOC occupation classification
Browser automation CLI for AI agents. Use when the user needs to interact with websites, including navigating pages, filling forms, clicking buttons, taking screenshots, extracting data, testing web apps, or automating browser actions.
Reviews and removes unsupported, non-idiomatic code and UI/design changes introduced on a branch. Use when implementing or reviewing a feature or fix before handoff.
Design principles for REST and GraphQL APIs in 2025. Enforces OpenAPI-first, versioning strategies, and standardized error responses.
Use the `fm` CLI (Apple Foundation Models) for on-device and Private Cloud Compute LLM inference on macOS. Use when the user wants to prompt, chat, serve, or count tokens with Apple's built-in foundation models. Also use for structured output generation (`fm schema`), checking model availability (`fm available`), or checking quota usage (`fm quota-usage`). Trigger on phrases like 'run fm', 'Apple Foundation Model', 'on-device LLM', 'Apple AI', 'fm respond', 'fm chat', 'fm serve', 'fm token-count', 'fm schema', or any task involving Apple's local AI models.
This skill should be used when the user asks to "trigger a build", "check build status", "watch a build", "view build logs", "retry a build", "cancel a build", "list builds", "download artifacts", "upload artifacts", "manage secrets", "create a pipeline", "list pipelines", or "interact with Buildkite from the command line". Also use when the user mentions bk commands, bk build, bk job, bk pipeline, bk secret, bk artifact, bk cluster, bk package, bk auth, bk configure, bk use, bk init, bk api, or asks about Buildkite CLI installation, terminal-based Buildkite workflows, or command-line CI/CD operations.
Buildkite CI/CD pipeline setup, migration, validation, hardening, and live pipeline management. Use when Codex needs to create, review, debug, or update Buildkite pipelines, .buildkite/pipeline.yml files, steps, queues, triggers, artifacts, annotations, secrets, OIDC, or deployment stages.
| name | python-debugpy |
| description | Debug Python: pdb REPL + debugpy remote (DAP). |
| version | 1.1.0 |
| author | Adapted from Hermes Agent |
| license | MIT |
| tags | ["debugging","python","pdb","debugpy","breakpoints","dap","post-mortem"] |
Three tools, picked by situation:
| Tool | When |
|---|---|
breakpoint() + pdb | Local, interactive, simplest. Add breakpoint() in the source, run normally, get a REPL at that line. |
python -m pdb | Launch an existing script under pdb with no source edits. Useful for quick poking. |
debugpy | Remote / headless / "attach to already-running process." Talks DAP, scriptable from terminal, works for long-lived processes (servers, daemons, workers). |
Start with breakpoint(). It's the cheapest thing that works.
Don't use for: things print() / logging.debug solve in under a minute, or things pytest -vv --tb=long --showlocals already reveals.
Inside any pdb prompt ((Pdb)):
| Command | Action |
|---|---|
h / h cmd | help |
n | next line (step over) |
s | step into |
r | return from current function |
c | continue |
unt N | continue until line N |
j N | jump to line N (same function only) |
l / ll | list source around current line / full function |
w | where (stack trace) |
u / d | move up / down in the stack |
a | print args of the current function |
p expr / pp expr | print / pretty-print expression |
display expr | auto-print expr on every stop |
b file:line | set breakpoint |
b func | break on function entry |
b file:line, cond | conditional breakpoint |
cl N | clear breakpoint N |
tbreak file:line | one-shot breakpoint |
!stmt | execute arbitrary Python (assignments included) |
interact | drop into full Python REPL in current scope (Ctrl+D to exit) |
q | quit |
The interact command is the most powerful — you can import anything, inspect complex objects, even call methods that mutate state. Locals are read-only by default; use !x = 42 from the (Pdb) prompt to mutate.
Easiest. Edit the file:
def compute(x, y):
result = some_helper(x)
breakpoint() # <-- drops into pdb here
return result + y
Run the code normally. You land at the breakpoint() line with full access to locals.
Don't forget to remove breakpoint() before committing. Use:
rg -n 'breakpoint\\(\\)' --type py
python -m pdb path/to/script.py arg1 arg2
# Lands at first line of script
(Pdb) b path/to/script.py:42
(Pdb) c
# Drop to pdb on failure (or on any raised exception):
pytest tests/path/to/test_file.py::test_name --pdb
# Drop to pdb at the START of the test:
pytest tests/path/to/test_file.py::test_name --trace
# Show locals in tracebacks without pdb:
pytest tests/path/to/test_file.py --showlocals --tb=long
Note: pdb does NOT work under pytest-xdist. Add -p no:xdist or -n 0:
pytest tests/foo_test.py::test_bar --pdb -p no:xdist
import pdb, sys
try:
run_the_thing()
except Exception:
pdb.post_mortem(sys.exc_info()[2])
Or wrap a whole script:
python -m pdb -c continue script.py
# When it crashes, pdb catches it and you're in the frame of the exception
Or set a global hook:
import sys
def excepthook(etype, value, tb):
import pdb; pdb.post_mortem(tb)
sys.excepthook = excepthook
For long-lived processes: servers, daemons, processes that are already misbehaving and can't be restarted clean.
pip install debugpy
Add near the top of the entry point (or inside the function you want to debug):
import debugpy
debugpy.listen(("127.0.0.1", 5678))
print("debugpy listening on 5678, waiting for client...", flush=True)
debugpy.wait_for_client()
debugpy.breakpoint() # optional: pause immediately once attached
Start the process; it blocks on wait_for_client().
-m debugpypython -m debugpy --listen 127.0.0.1:5678 --wait-for-client your_script.py arg1
For module entry:
python -m debugpy --listen 127.0.0.1:5678 --wait-for-client -m your.module
Needs the PID and debugpy preinstalled in the target's environment:
python -m debugpy --listen 127.0.0.1:5678 --pid <pid>
# debugpy injects itself into the process. Then attach a client as below.
Some kernels/security configs block the ptrace-based injection (/proc/sys/kernel/yama/ptrace_scope). Fix with:
echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
From VS Code / Cursor / Zed — add a launch.json:
{
"name": "Attach to Python",
"type": "debugpy",
"request": "attach",
"connect": { "host": "127.0.0.1", "port": 5678 },
"justMyCode": false,
"pathMappings": [
{ "localRoot": "${workspaceFolder}", "remoteRoot": "/path/to/project" }
]
}
From the terminal using remote-pdb — usually what you actually want:
pip install remote-pdb
In your code:
from remote_pdb import set_trace
set_trace(host="127.0.0.1", port=4444) # blocks until connection
Then from the terminal:
nc 127.0.0.1 4444
# You get a (Pdb) prompt exactly as if debugging locally.
remote-pdb is the cleanest terminal-friendly choice. Use debugpy only when you actually need IDE integration.
pdb under pytest-xdist silently does nothing. You won't see the prompt, the test just hangs. Always use -p no:xdist or -n 0.
breakpoint() in CI / non-TTY contexts hangs the process. Safe locally; never commit it. Add a pre-commit grep as a safety net.
PYTHONBREAKPOINT=0 disables all breakpoint() calls. Check the env if your breakpoint isn't hitting:
echo $PYTHONBREAKPOINT
debugpy.listen blocks only if you also call wait_for_client(). Without it, execution continues and your first breakpoint may fire before the client is attached.
Attach to PID fails on hardened kernels. ptrace_scope=1 (Ubuntu default) allows only same-user ptrace of child processes. Workaround: echo 0 > /proc/sys/kernel/yama/ptrace_scope (needs root) or launch under debugpy from the start.
Threads. pdb only debugs the current thread. For multithreaded code, use debugpy (thread-aware DAP) or set threading.settrace() per thread.
asyncio. pdb works in coroutines but await inside pdb requires Python 3.13+ or await from interact mode on older versions.
Forking / multiprocessing. pdb does not follow forks. Each child needs its own breakpoint() or set_trace(). Debug one process at a time.
pip install debugpy, confirm: python -c "import debugpy; print(debugpy.__version__)"ss -tlnp | grep 5678 (Linux) or lsof -i :5678 (macOS)PYTHONBREAKPOINT=0, you're under xdist, or execution finished before attach)where / w shows the expected call stackbreakpoint() / set_trace() in committed code
rg -n 'breakpoint\\(\\)|set_trace\\(|debugpy\\.listen' --type py
"Why is this dict missing a key?"
# add above the KeyError site
breakpoint()
# then in pdb:
(Pdb) pp d
(Pdb) pp list(d.keys())
(Pdb) w # how did we get here
"This test passes in isolation but fails in the suite."
pytest tests/the_test.py --pdb -p no:xdist
# But if it only fails WITH other tests:
python -m pytest tests/ -x --pdb -p no:xdist
# Now it pdb-traps at the exact failing test after state accumulated.
"My async handler deadlocks."
# Add at handler entry
import remote_pdb; remote_pdb.set_trace(host="127.0.0.1", port=4444)
Trigger the handler. nc 127.0.0.1 4444, then w to see the suspended frame, !import asyncio; asyncio.all_tasks() to see what else is pending.
"Post-mortem on a crash."
PYTHONFAULTHANDLER=1 python -m pdb -c continue path/to/entrypoint.py
# On crash, pdb lands at the frame of the exception with full locals