{/* Trang này được tạo tự động từ SKILL.md của kỹ năng bởi website/scripts/generate-skill-docs.py. Chỉnh sửa nguồn SKILL.md, không phải trang này. */}
Gỡ lỗi Python
Gỡ lỗi Python: pdb REPL + gỡ lỗi từ xa (DAP).
Siêu dữ liệu kỹ năng
| Nguồn | Đi kèm (được cài đặt theo mặc định) |
| Đường dẫn |
skills/software-development/Python-debugpy ` | | Phiên bản |
1.0.0 ` | | Tác giả | Đại lý Hermes | | Giấy phép | MIT | | Nền tảng | Linux, macOS | | Thẻ |
debugging
,
,
`pdb
,
`debugpy
,
`breakpoints
,
`dap
,
`post-mortem
` |
| Kỹ năng liên quan | [XPROTECTX36XPROTECTX](/docs/user-guide/skills/bundled/software-development/software-development-systematic-debugging), [XPROTECTX37XPROTECTX](/docs/user-guide/skills/bundled/software-development/software-development-node-inspect-debugger), [XPROTECTX38XPROTECTX](/docs/user-guide/skills/bundled/software-development/software-development-debugging-Hermes-TUI-commands) |
## Tham khảo: đầy đủ SKILL.md
:::info
Sau đây là định nghĩa kỹ năng đầy đủ mà Hermes tải khi kỹ năng này được kích hoạt. Đây là những gì tác nhân coi là hướng dẫn khi kỹ năng được kích hoạt.
:::
# Trình gỡ lỗi Python (pdb + debugpy)
## Tổng quan
Ba công cụ, được chọn theo tình huống:
| Công cụ | Khi nào |
|---|---|
| **
`breakpoint()
+ pdb** | Địa phương, tương tác, đơn giản nhất. Thêm
`breakpoint()
` vào nguồn, chạy bình thường, lấy REPL ở dòng đó. |
| **
`Python -m pdb
** | Khởi chạy tập lệnh hiện có trong pdb mà không cần chỉnh sửa nguồn. Hữu ích cho việc chọc nhanh. |
| **
`debugpy
** | Từ xa/không đầu/"đính kèm vào quy trình đã chạy." Talks DAP, có thể viết tập lệnh từ terminal, hoạt động cho các quy trình tồn tại lâu dài (cổng, daemon, con PTY). |
**Bắt đầu với
`breakpoint()
.** Đó là giải pháp rẻ nhất nhưng hiệu quả.
## Khi nào nên sử dụng
- Thử nghiệm thất bại và truy nguyên không tiết lộ lý do tại sao giá trị sai
- Bạn cần duyệt qua một chức năng và xem bộ sưu tập biến đổi
- Một tiến trình chạy lâu (Hermes Gateway, TUI_gateway) hoạt động sai và bạn không thể khởi động lại nó
- Khám nghiệm tử thi: một ngoại lệ được kích hoạt trong mã sản phẩm và bạn muốn kiểm tra người dân địa phương tại địa điểm xảy ra sự cố
- Một tiến trình con/con (Python
`_SlashWorker
, nhân viên cầu nối PTY) là trang web lỗi thực tế`**Không sử dụng cho:** những thứ
`print()
` /
`logging.debug
` giải quyết trong vòng chưa đầy một phút hoặc những thứ
`pytest -vv --tb=long --showlocals
` đã tiết lộ.
## pdb Tham khảo nhanh
Bên trong bất kỳ dấu nhắc pdb nào (
(Pdb)
):
| Lệnh | Hành động |
|---|---|
|
`h
` /
`h cmd
` | giúp đỡ |
|
`n
` | dòng tiếp theo (bước qua) |
|
`s
` | bước vào |
|
`r
` | trở về từ hàm hiện tại |
|
`c
` | tiếp tục |
|
`unt N
` | tiếp tục cho đến dòng N |
|
`j N
` | nhảy tới dòng N (chỉ cùng chức năng) |
|
`l
` /
`ll
` | danh sách nguồn xung quanh dòng hiện tại/đầy đủ chức năng |
|
`w
` | ở đâu (dấu vết ngăn xếp) |
|
`u
` /
`d
` | di chuyển lên/xuống trong ngăn xếp |
|
`a
` | in các đối số của hàm hiện tại |
|
`p expr
` /
`pp expr
` | biểu thức in / in đẹp |
|
`display expr
` | tự động in expr mỗi lần dừng |
|
`b file:line
` | đặt điểm dừng |
|
`b func
` | phá vỡ mục nhập chức năng |
|
`b file:line, cond
` | điểm dừng có điều kiện |
|
`cl N
` | xóa điểm dừng N |
|
`tbreak file:line
` | điểm dừng một lần |
|
!stmt
` | thực thi Python tùy ý (bao gồm các bài tập) |
|
`interact
` | thả vào REPL Python đầy đủ trong phạm vi hiện tại (Ctrl+D để thoát) |
|
`q
` | bỏ |
Lệnh
`interact
` là lệnh mạnh nhất — bạn có thể nhập bất kỳ thứ gì, kiểm tra các đối tượng phức tạp, thậm chí gọi các phương thức thay đổi trạng thái. Theo mặc định, các địa chỉ cục bộ ở chế độ chỉ đọc; sử dụng
!x = 42
` từ lời nhắc
(Pdb)
` để thay đổi.
## Công thức 1: Điểm dừng cục bộ
Dễ nhất. Chỉnh sửa tập tin:
``` python
def compute(x, y):
result = some_helper(x)
breakpoint() # <-- drops into pdb here
return result + y
`
``Chạy mã bình thường. Bạn hạ cánh tại tuyến
`breakpoint()
` với toàn quyền tiếp cận với người dân địa phương.
**Đừng quên xóa
`breakpoint()
` trước khi xác nhận.** Sử dụng
`git diff
` hoặc grep xác nhận trước:
`
`bash
rg -n 'breakpoint\(\)' --type py
`
## Công thức 2: Khởi chạy tập lệnh dưới pdb (không chỉnh sửa nguồn)
`bash
Python -m pdb path/to/script.py arg1 arg2
# Lands at first line of script
(Pdb) b path/to/script.py:42
(Pdb) c
`
## Công thức 3: Debug test pytest
Người chạy thử nghiệm Hermes và pytest đều hỗ trợ điều này:
``` bash
# 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=long
`
``Lưu ý:
`scripts/run_tests.sh
` sử dụng xdist (
-n 4
) theo mặc định và pdb KHÔNG hoạt động trong xdist. Thêm
-p no:xdist
` hoặc chạy thử nghiệm đơn lẻ với
-n 0
:
``` bash
scripts/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 --pdb
`
`Điều này bỏ qua các đảm bảo hermetic-env - tốt cho việc gỡ lỗi, nhưng chạy lại dưới trình bao bọc để xác nhận trước khi đẩy.
## Công thức 4: Khám nghiệm tử thi bất kỳ trường hợp ngoại lệ nào
``` python
import pdb, sys
try:
run_the_thing()
except Exception:
pdb.post_mortem(sys.exc_info()[2])
`
``Hoặc gói toàn bộ tập lệnh:
`bash
Python -m pdb -c Continue script.py
# When it crashes, pdb catches it and you're in the frame of the exception
`
``Hoặc đặt móc toàn cầu trong thay thế/jupyter:
``` python
import sys
def excepthook(etype, value, tb):
import pdb; pdb.post_mortem(tb)
sys.excepthook = excepthook
`
## Công thức 5: Remote debug bằng debugpy (gắn vào tiến trình đang chạy)
Đối với các quy trình tồn tại lâu dài: Cổng Hermes, thu_gateway, daemon, một quy trình đã hoạt động sai và không thể khởi động lại sạch sẽ.
### Thiết lập
`bash
source /home/bb/Hermes-agent/.venv/bin/activate
pip install debugpy
`
### Mẫu A: Chỉnh sửa nguồn - quá trình chờ trình gỡ lỗi khi khởi chạy
Thêm gần đầu điểm vào (hoặc bên trong hàm bạn muốn gỡ lỗi):
`Python
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
`
``Bắt đầu quá trình; nó chặn
`wait_for_CLIent()
.
### Mẫu B: Không chỉnh sửa nguồn — khởi chạy với
`
-m debugpy
`
``` bash
Python -m debugpy --listen 127.0.0.1:5678 --wait-for-CLIent your_script.py arg1
`
``Tương đương với mục nhập mô-đun:
`bash
Python -m debugpy --listen 127.0.0.1:5678 --wait-for-CLIent -m your.module
`
### Mẫu C: Đính kèm vào một tiến trình đang chạy
Cần cài đặt sẵn PID và debugpy trong môi trường của mục tiêu:
`bash
Python -m debugpy --listen 127.0.0.1:5678 --pid <pid>
# debugpy injects itself into the process. Then attach a CLIent as below.
`
``Một số hạt nhân/cấu hình bảo mật chặn việc tiêm dựa trên ptrace (
/proc/sys/kernel/yama/ptrace_scope
). Khắc phục bằng:
`
``` bash
echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
`
### Kết nối máy khách từ terminal
Máy khách DAP phía terminal dễ dàng nhất là VS Code CLI hoặc một tập lệnh nhỏ. Từ bên trong Hermes, bạn có hai lựa chọn thiết thực:
**Tùy chọn 1: CLI REPL riêng của
`debugpy
`
** — không phải là một tính năng chính thức mà là một tập lệnh máy khách DAP nhỏ:
``` python
# /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.
`
``Điều này tốt cho tự động hóa một lần nhưng gây khó khăn cho UX tương tác.
**Tùy chọn 2: Đính kèm từ Mã VS / Con trỏ / Zed** — nếu người dùng có một mã mở, họ có thể thêm
`launch.JSON
:
``` 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" }
]
}
`
``**Tùy chọn 3: Bỏ DAP, sử dụng
`remote-pdb
`
** — thường là những gì bạn thực sự muốn từ một đại lý đầu cuối:
``` bash
pip install remote-pdb
`
``Trong mã của bạn:
`
`Python
from remote_pdb import set_trace
set_trace(host="127.0.0.1", port=4444) # blocks until connection
`
``Sau đó từ terminal:
`
`bash
nc 127.0.0.1 4444
# You get a (Pdb) prompt exactly as if debugging locally.
`
```remote-pdb
` là sự lựa chọn thân thiện với tác nhân sạch nhất khi giao thức DAP của
`debugpy
` quá mức cần thiết. Chỉ sử dụng
`debugpy
` khi bạn thực sự cần tích hợp IDE.
## Gỡ lỗi các quy trình dành riêng cho Hermes
### Kiểm tra
Xem Công thức 3. Luôn thêm
-p no:xdist
` hoặc chạy thử nghiệm đơn lẻ mà không cần xdist.
###
`run_agent.py
` / CLI — một lần
Dễ nhất: thêm
`breakpoint()
` gần dòng nghi ngờ, sau đó chạy
`Hermes
` bình thường. Điều khiển quay trở lại terminal của bạn tại điểm tạm dừng.
### Quy trình con
`TUI_gateway
` (được sinh ra bởi
`Hermes --TUI
)
Cổng chạy như một phần tử con của Node TUI. Tùy chọn:
**A. Chỉnh sửa nguồn cổng:**
`
``` python
# TUI_gateway/server.py near the top of serve()
import debugpy
debugpy.listen(("127.0.0.1", 5678))
debugpy.wait_for_CLIent()
`
Bắt đầu
`Hermes --TUI
. TUI sẽ bị đóng băng (phần phụ trợ của nó đang chờ). Đính kèm một khách hàng; quá trình thực thi sẽ tiếp tục khi bạn
`Continue
.
**B. Sử dụng
`remote-pdb
` ở trình xử lý cụ thể:**
`
``` python
from remote_pdb import set_trace
set_trace(host="127.0.0.1", port=4444) # in the RPC handler you want to trap
`
Kích hoạt lệnh gạch chéo phù hợp từ TUI, sau đó
`nc 127.0.0.1 4444
` trong một terminal khác.
### Quy trình con
`_SlashWorker
Mẫu tương tự -
`remote-pdb
` với
`set_trace()
` bên trong đường dẫn
`exec
` của nhân viên. Worker liên tục thực hiện các lệnh gạch chéo, do đó, trình kích hoạt đầu tiên sẽ chặn cho đến khi bạn kết nối; các lệnh gạch chéo tiếp theo sẽ diễn ra bình thường trừ khi bạn tái trang bị.
### Cổng (
`gateway/run.py
)
Sống lâu. Sử dụng
`remote-pdb
` ở trình xử lý hoặc
`debugpy
` với
`
--wait-for-CLIent
` nếu bạn vẫn khởi động lại cổng.
## Những cạm bẫy thường gặp
1. **pdb trong pytest-xdist âm thầm không làm gì cả.** Bạn sẽ không thấy lời nhắc, quá trình kiểm tra chỉ bị treo. Luôn sử dụng
-p no:xdist
` hoặc
-n 0
.
2. **
`breakpoint()
` trong ngữ cảnh CI / không phải TTY sẽ treo quy trình.** An toàn cục bộ; không bao giờ cam kết nó. Thêm grep cam kết trước làm mạng lưới an toàn.
3. **
`PythonBREAKPOINT=0
** vô hiệu hóa tất cả các cuộc gọi
`breakpoint()
. Kiểm tra env nếu điểm dừng của bạn không đạt:
``` bash
echo $PythonBREAKPOINT
4. **
`debugpy.listen
` chỉ chặn nếu bạn cũng gọi
`wait_for_CLIent()
.** Nếu không có nó, quá trình thực thi vẫn tiếp tục và điểm dừng đầu tiên của bạn có thể kích hoạt trước khi máy khách được đính kèm.
5. **Không thể đính kèm PID trên các hạt nhân cứng.**
`ptrace_scope=1
` (mặc định của Ubuntu) chỉ cho phép truy cập các tiến trình con của cùng một người dùng. Cách giải quyết:
`echo 0 > /proc/sys/kernel/yama/ptrace_scope
` (cần root) hoặc khởi chạy dưới
`debugpy
` ngay từ đầu.
6. **Chủ đề.**
`pdb
` chỉ gỡ lỗi chuỗi hiện tại. Đối với mã đa luồng, hãy sử dụng
`debugpy
` (DAP nhận biết luồng) hoặc đặt
`threading.settrace()
` cho mỗi luồng.7. **asyncio.**
`pdb
` hoạt động trong coroutine nhưng
`await
` bên trong pdb yêu cầu Python 3.13+ hoặc
`await
` từ chế độ
`interact
` trên các phiên bản cũ hơn. Đối với phiên bản 3.11/3.12, hãy sử dụng các thủ thuật
`asyncio.run_coroutine_threadsafe
` hoặc chờ dựa trên
!stmt
` thông qua
`asyncio.ensure_future
.
8. **
`scripts/run_tests.sh
` loại bỏ thông tin xác thực và đặt
`HOME=<tmpdir
.** Nếu lỗi của bạn phụ thuộc vào cấu hình người dùng hoặc khóa API thực, lỗi đó sẽ không tái tạo trong trình bao bọc. Trước tiên hãy gỡ lỗi bằng
`pytest
` thô để repro, sau đó xác nhận lại trong trình bao bọc.
9. **Rẽ nhánh/đa xử lý.** pdb không tuân theo các nhánh. Mỗi đứa trẻ cần có
`breakpoint()
` hoặc
`set_trace()
` riêng. Đối với các đại lý phụ của Hermes, hãy gỡ lỗi từng quy trình một.
## Danh sách kiểm tra xác minh
- [ ] Sau
`pip install debugpy
, xác nhận:
`Python -c "import debugpy; print(debugpy.__version__)"
`
- [] Để gỡ lỗi từ xa, hãy xác nhận cổng thực sự đang lắng nghe:
`ss -tlnp | grep 5678
- [ ] Điểm dừng đầu tiên thực sự đạt đến (nếu không, bạn có thể có
`PythonBREAKPOINT=0
, bạn đang ở chế độ xdist hoặc quá trình thực thi đã hoàn tất trước khi đính kèm)
- [ ]
`where
` /
`w
` hiển thị ngăn xếp cuộc gọi dự kiến
- [ ] Dọn dẹp sau gỡ lỗi: không có
`breakpoint()
` /
`set_trace()
` đi lạc trong mã đã cam kết
``` bash
rg -n 'breakpoint\(\)|set_trace\(|debugpy\.listen' --type py
## Bí quyết một lần`**"Tại sao lệnh này lại thiếu chìa khóa?"**
`
`Python
# add above the KeyError site
breakpoint()
# then in pdb:
(Pdb) pp d
(Pdb) pp list(d.keys())
(Pdb) w # how did we get here
`
``**"Thử nghiệm này thành công một cách độc lập nhưng thất bại trong bộ phần mềm."**
`
``` bash
scripts/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.
`
``**"Sự bế tắc của trình xử lý không đồng bộ của tôi."**
`
``` python
# Add at handler entry
import remote_pdb; remote_pdb.set_trace(host="127.0.0.1", port=4444)
`
Kích hoạt trình xử lý.
`nc 127.0.0.1 4444
, sau đó là
`w
` để xem khung treo,
!import asyncio; asyncio.all_tasks()
` để xem những gì khác đang chờ xử lý.
**"Xác định sự cố trong quy trình con/quy trình con Ink."**
`
``` bash
PythonFAULTHANDLER=1 Python -m pdb -c Continue path/to/entrypoint.py
# On crash, pdb lands at the frame of the exception with full locals
`
`