Loading...
Loading...
Debug Python: pdb REPL + debugpy remote (DAP).
npx skill4agent add nousresearch/hermes-agent python-debugpy| Tool | When |
|---|---|
| Local, interactive, simplest. Add |
| Launch an existing script under pdb with no source edits. Useful for quick poking. |
| Remote / headless / "attach to already-running process." Talks DAP, scriptable from terminal, works for long-lived processes (gateway, daemon, PTY children). |
breakpoint()_SlashWorkerprint()logging.debugpytest -vv --tb=long --showlocals(Pdb)| Command | Action |
|---|---|
| help |
| next line (step over) |
| step into |
| return from current function |
| continue |
| continue until line N |
| jump to line N (same function only) |
| list source around current line / full function |
| where (stack trace) |
| move up / down in the stack |
| print args of the current function |
| print / pretty-print expression |
| auto-print expr on every stop |
| set breakpoint |
| break on function entry |
| conditional breakpoint |
| clear breakpoint N |
| one-shot breakpoint |
| execute arbitrary Python (assignments included) |
| drop into full Python REPL in current scope (Ctrl+D to exit) |
| quit |
interact!x = 42(Pdb)def compute(x, y):
result = some_helper(x)
breakpoint() # <-- drops into pdb here
return result + ybreakpoint()breakpoint()git diffrg -n 'breakpoint\(\)' --type pypython -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):
scripts/run_tests.sh tests/path/to/test_file.py::test_name --pdb
# Drop to pdb at the START of the test:
scripts/run_tests.sh tests/path/to/test_file.py::test_name --trace
# Show locals in tracebacks without pdb:
scripts/run_tests.sh tests/path/to/test_file.py --showlocals --tb=longscripts/run_tests.sh-n 4-p no:xdist-n 0scripts/run_tests.sh tests/foo_test.py::test_bar --pdb -p no:xdist
# or
source .venv/bin/activate
python -m pytest tests/foo_test.py::test_bar --pdbimport pdb, sys
try:
run_the_thing()
except Exception:
pdb.post_mortem(sys.exc_info()[2])python -m pdb -c continue script.py
# When it crashes, pdb catches it and you're in the frame of the exceptionimport sys
def excepthook(etype, value, tb):
import pdb; pdb.post_mortem(tb)
sys.excepthook = excepthooksource /home/bb/hermes-agent/.venv/bin/activate
pip install debugpyimport 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 attachedwait_for_client()-m debugpypython -m debugpy --listen 127.0.0.1:5678 --wait-for-client your_script.py arg1python -m debugpy --listen 127.0.0.1:5678 --wait-for-client -m your.modulepython -m debugpy --listen 127.0.0.1:5678 --pid <pid>
# debugpy injects itself into the process. Then attach a client as below./proc/sys/kernel/yama/ptrace_scopeecho 0 | sudo tee /proc/sys/kernel/yama/ptrace_scopedebugpy# /tmp/dap_client.py
import socket, json, itertools, time, sys
HOST, PORT = "127.0.0.1", 5678
s = socket.create_connection((HOST, PORT))
seq = itertools.count(1)
def send(msg):
msg["seq"] = next(seq)
body = json.dumps(msg).encode()
s.sendall(f"Content-Length: {len(body)}\r\n\r\n".encode() + body)
def recv():
header = b""
while b"\r\n\r\n" not in header:
header += s.recv(1)
length = int(header.decode().split("Content-Length:")[1].split("\r\n")[0].strip())
body = b""
while len(body) < length:
body += s.recv(length - len(body))
return json.loads(body)
send({"type": "request", "command": "initialize", "arguments": {"adapterID": "python"}})
print(recv())
send({"type": "request", "command": "attach", "arguments": {}})
print(recv())
send({"type": "request", "command": "setBreakpoints",
"arguments": {"source": {"path": sys.argv[1]},
"breakpoints": [{"line": int(sys.argv[2])}]}})
print(recv())
send({"type": "request", "command": "configurationDone"})
# ... loop reading events and sending continue/stepIn/etc.launch.json{
"name": "Attach to Hermes",
"type": "debugpy",
"request": "attach",
"connect": { "host": "127.0.0.1", "port": 5678 },
"justMyCode": false,
"pathMappings": [
{ "localRoot": "${workspaceFolder}", "remoteRoot": "/home/bb/hermes-agent" }
]
}remote-pdbpip install remote-pdbfrom remote_pdb import set_trace
set_trace(host="127.0.0.1", port=4444) # blocks until connectionnc 127.0.0.1 4444
# You get a (Pdb) prompt exactly as if debugging locally.remote-pdbdebugpydebugpy-p no:xdistrun_agent.pybreakpoint()hermestui_gatewayhermes --tui# tui_gateway/server.py near the top of serve()
import debugpy
debugpy.listen(("127.0.0.1", 5678))
debugpy.wait_for_client()hermes --tuicontinueremote-pdbfrom remote_pdb import set_trace
set_trace(host="127.0.0.1", port=4444) # in the RPC handler you want to trapnc 127.0.0.1 4444_SlashWorkerremote-pdbset_trace()execgateway/run.pyremote-pdbdebugpy--wait-for-client-p no:xdist-n 0breakpoint()PYTHONBREAKPOINT=0breakpoint()echo $PYTHONBREAKPOINTdebugpy.listenwait_for_client()ptrace_scope=1echo 0 > /proc/sys/kernel/yama/ptrace_scopedebugpypdbdebugpythreading.settrace()pdbawaitawaitinteractasyncio.run_coroutine_threadsafe!stmtasyncio.ensure_futurescripts/run_tests.shHOME=<tmpdir>pytestbreakpoint()set_trace()pip install debugpypython -c "import debugpy; print(debugpy.__version__)"ss -tlnp | grep 5678PYTHONBREAKPOINT=0wherewbreakpoint()set_trace()rg -n 'breakpoint\(\)|set_trace\(|debugpy\.listen' --type py# add above the KeyError site
breakpoint()
# then in pdb:
(Pdb) pp d
(Pdb) pp list(d.keys())
(Pdb) w # how did we get herescripts/run_tests.sh tests/the_test.py --pdb -p no:xdist
# But if it only fails WITH other tests:
source .venv/bin/activate
python -m pytest tests/ -x --pdb -p no:xdist
# Now it pdb-traps at the exact failing test after state accumulated.# Add at handler entry
import remote_pdb; remote_pdb.set_trace(host="127.0.0.1", port=4444)nc 127.0.0.1 4444w!import asyncio; asyncio.all_tasks()PYTHONFAULTHANDLER=1 python -m pdb -c continue path/to/entrypoint.py
# On crash, pdb lands at the frame of the exception with full locals