🏠 Главная › user guide › software development python debugpy
{/ Эта страница автоматически создается на основе файла SKILL.md навыка с помощью сайта site/scripts/generate-skill-docs.py. Редактируйте исходный код SKILL.md, а не эту страницу. /}
Ниже приведено полное определение навыка, которое Гермес загружает при активации этого навыка. Это то, что агент видит в качестве инструкций, когда навык активен.
Отладчик Python (pdb + debugpy)
Обзор
Три инструмента, выбираемых по ситуации:
Инструмент
Когда
breakpoint() + pdb
Локальный, интерактивный, самый простой. Добавьте breakpoint() в исходный код, запустите его в обычном режиме, получите REPL в этой строке.
python -m pdb
Запустите существующий скрипт в pdb без редактирования исходного кода. Полезно для быстрого тыкания.
отладка
Удаленный/безголовый/"присоединиться к уже запущенному процессу". Talks DAP, скриптируемый с терминала, работает для долгоживущих процессов (шлюз, демон, дочерние элементы PTY).
Начните с breakpoint(). Это самый дешевый вариант.
Когда использовать
Тест не пройден, и обратная трассировка не показывает, почему значение неверно.
Вам нужно выполнить функцию и наблюдать за изменением коллекции.
Длительный процесс (шлюз Hermes, tui_gateway) работает неправильно, и вы не можете его перезапустить.
Посмертное исследование: в рабочем коде генерируется исключение, и вы хотите проверить местных жителей на месте сбоя.
Подпроцесс/дочерний процесс (Python _SlashWorker, рабочий мост PTY) является фактическим местом ошибки.
Не используйте для: вещей, которые print() / logging.debug решают менее чем за минуту, или вещи, которые pytest -vv --tb=long --showlocals уже обнаруживает.
Краткий справочник по pdb
Внутри любого приглашения pdb ((Pdb)):
Команда
Действие
h / h cmd
помощь
н
следующая строка (перешагнуть)
с
шагнуть в
р
возврат из текущей функции
с
продолжить
unt N
продолжать до строки N
j N
перейти на строку N (только та же функция)
л/лл
список источников вокруг текущей строки/полная функция
ш
где (трассировка стека)
у / д
перемещаться вверх/вниз по стопке
а
вывести аргументы текущей функции
p выражение / pp выражение
печать / красивое выражение
показать выражение
автоматическая печать выражения на каждой остановке
b файл:строка
установить точку останова
б функция
перерыв при входе в функцию
b файл:строка, условие
условная точка останова
кл Н
очистить точку останова N
tbreak файл:строка
одноразовая точка останова
!stmt
выполнить произвольный Python (задания включены)
взаимодействовать
перейдите в полную версию Python REPL в текущей области (Ctrl+D для выхода)
к
бросить
Команда interact является самой мощной — вы можете импортировать что угодно, проверять сложные объекты и даже вызывать методы, изменяющие состояние. По умолчанию локальные файлы доступны только для чтения; используйте !x = 42 из приглашения (Pdb) для изменения.
Рецепт 1: Локальная точка останова
Самый простой. Отредактируйте файл:
defcompute(x,y):result=some_helper(x)breakpoint()# <-- drops into pdb herereturnresult+y
Запустите код в обычном режиме. Вы попадаете на строку breakpoint() с полным доступом к локальным пользователям.
Не забудьте удалить breakpoint() перед фиксацией. Используйте git diff или grep перед фиксацией:
rg-n'breakpoint\(\)'--typepy
Рецепт 2: Запускаем скрипт под pdb (без редактирования исходного кода)
python-mpdbpath/to/script.pyarg1arg2
# Lands at first line of script(Pdb)bpath/to/script.py:42
(Pdb)c
Рецепт 3: Отладка теста pytest
Программа запуска тестов Hermes и pytest поддерживают это:
# Drop to pdb on failure (or on any raised exception):
scripts/run_tests.shtests/path/to/test_file.py::test_name--pdb
# Drop to pdb at the START of the test:
scripts/run_tests.shtests/path/to/test_file.py::test_name--trace
# Show locals in tracebacks without pdb:
scripts/run_tests.shtests/path/to/test_file.py--showlocals--tb=long
Примечание: scripts/run_tests.sh по умолчанию использует xdist (-n 4), а pdb НЕ работает под xdist. Добавьте -p no:xdist или запустите одиночный тест с -n 0:
scripts/run_tests.shtests/foo_test.py::test_bar--pdb-pno:xdist
# or
source.venv/bin/activate
python-mpytesttests/foo_test.py::test_bar--pdb
Это обходит гарантии hermetic-env — хорошо для отладки, но перед отправкой необходимо повторно запустить под оболочкой для подтверждения.
Шаблон A: Source-edit — процесс ожидает отладчика при запуске
Добавьте в верхней части точки входа (или внутри функции, которую вы хотите отладить):
importdebugpydebugpy.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
Запустите процесс; он блокируется в wait_for_client().
Шаблон B: без редактирования исходного кода — запуск с -m debugpy
Требуется, чтобы PID и отладочная программа были предварительно установлены в целевой среде:
python-mdebugpy--listen127.0.0.1:5678--pid<pid>
# debugpy injects itself into the process. Then attach a client as below.
Некоторые конфигурации ядра/безопасности блокируют внедрение на основе ptrace (/proc/sys/kernel/yama/ptrace_scope). Исправьте с помощью:
echo0|sudotee/proc/sys/kernel/yama/ptrace_scope
Подключение клиента из терминала
Самый простой клиент DAP на стороне терминала — это VS Code CLI или небольшой скрипт. Изнутри Hermes у вас есть два практических варианта:
Вариант 1: собственный CLI REPL debugpy — не официальная функция, а небольшой клиентский скрипт DAP:
# /tmp/dap_client.pyimportsocket,json,itertools,time,sysHOST,PORT="127.0.0.1",5678s=socket.create_connection((HOST,PORT))seq=itertools.count(1)defsend(msg):msg["seq"]=next(seq)body=json.dumps(msg).encode()s.sendall(f"Content-Length: {len(body)}\r\n\r\n".encode()+body)defrecv():header=b""whileb"\r\n\r\n"notinheader:header+=s.recv(1)length=int(header.decode().split("Content-Length:")[1].split("\r\n")[0].strip())body=b""whilelen(body)<length:body+=s.recv(length-len(body))returnjson.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.
Это хорошо для разовой автоматизации, но болезненно для интерактивного UX.
Вариант 2: Прикрепить из VS Code/Cursor/Zed — если у пользователя он открыт, он может добавить 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"}]}
Вариант 3. Откажитесь от DAP и используйте remote-pdb — обычно это именно то, что вам нужно от терминального агента:
pipinstallremote-pdb
В вашем коде:
fromremote_pdbimportset_traceset_trace(host="127.0.0.1",port=4444)# blocks until connection
Затем из терминала:
nc127.0.0.14444# You get a (Pdb) prompt exactly as if debugging locally.
remote-pdb — самый чистый и удобный для агентов выбор, когда протокол DAP debugpy является излишним. Используйте debugpy только тогда, когда вам действительно нужна интеграция с IDE.
Отладка процессов, специфичных для Hermes
Тесты
См. рецепт 3. Всегда добавляйте -p no:xdist или запускайте отдельные тесты без xdist.
run_agent.py / CLI — одноразовый
Самый простой: добавьте breakpoint() рядом с подозрительной строкой, а затем запустите hermes в обычном режиме. Управление возвращается к вашему терминалу в точке паузы.
Подпроцесс tui_gateway (порожденный hermes --tui)
Шлюз работает как дочерний элемент Node TUI. Опции:
А. Исходный код — отредактируйте шлюз:
# tui_gateway/server.py near the top of serve()importdebugpydebugpy.listen(("127.0.0.1",5678))debugpy.wait_for_client()
Запустите hermes --tui. TUI будет зависшим (его серверная часть ожидает). Прикрепить клиента; выполнение возобновляется, когда вы «продолжаете».
Б. Используйте remote-pdb для определенного обработчика:
fromremote_pdbimportset_traceset_trace(host="127.0.0.1",port=4444)# in the RPC handler you want to trap
Запустите соответствующую команду косой черты из TUI, затем nc 127.0.0.1 4444 в другом терминале.
Подпроцесс _SlashWorker
Тот же шаблон — «remote-pdb» с «set_trace()» внутри пути «exec» воркера. Рабочий объект сохраняется при выполнении команд косой черты, поэтому первый триггер блокируется до тех пор, пока вы не подключитесь; последующие команды косой черты выполняются нормально, если вы не перевооружитесь.
Шлюз (gateway/run.py)
Долговечный. Используйте «remote-pdb» в обработчике или «debugpy» с «--wait-for-client», если вы все равно перезапускаете шлюз.
Распространенные ошибки
pdb под pytest-xdist ничего не делает. Вы не увидите подсказку, тест просто зависает. Всегда используйте -p no:xdist или -n 0.
breakpoint() в контекстах CI/без TTY зависает процесс. Безопасно локально; никогда не совершайте этого. Добавьте grep перед фиксацией в качестве страховки.
PYTHONBREAKPOINT=0 отключает все вызовы breakpoint(). Проверьте окружение, если ваша точка останова не сработала:
bash
echo $PYTHONBREAKPOINT
debugpy.listen блокируется, только если вы также вызываете wait_for_client(). Без него выполнение продолжается, и ваша первая точка останова может сработать до подключения клиента.
Присоединение к PID не удается на усиленных ядрах.ptrace_scope=1 (по умолчанию в Ubuntu) разрешает только ptrace одного и того же пользователя для дочерних процессов. Обходной путь: echo 0 > /proc/sys/kernel/yama/ptrace_scope (требуется root) или запустить с самого начала под debugpy.
Threads.pdb отлаживает только текущий поток. Для многопоточного кода используйте debugpy (DAP с поддержкой потоков) или установите threading.settrace() для каждого потока.
asyncio.pdb работает в сопрограммах, но await внутри pdb требует Python 3.13+ или await из режима interact в более старых версиях. Для версий 3.11/3.12 используйте трюки asyncio.run_coroutine_threadsafe или ожидание на основе !stmt через asyncio.ensure_future.
scripts/run_tests.sh удаляет учетные данные и устанавливает HOME=<tmpdir>. Если ваша ошибка зависит от конфигурации пользователя или реальных ключей API, она не будет воспроизводиться под оболочкой. Сначала выполните отладку с помощью необработанного pytest для воспроизведения, а затем повторно подтвердите под оболочкой.
Разветвление/многопроцессорность. pdb не следует за разветвлениями. Каждому дочернему элементу нужна своя точка останова() или set_trace(). Для субагентов Hermes выполняйте отладку по одному процессу за раз.
[ ] Для удаленной отладки убедитесь, что порт действительно прослушивается: ss -tlnp | команда 5678
[ ] Первая точка останова действительно достигает (если это не так, скорее всего, у вас PYTHONBREAKPOINT=0, вы находитесь под xdist или выполнение завершено до присоединения)
[ ] where/w показывает ожидаемый стек вызовов
[ ] Очистка после отладки: в зафиксированном коде нет случайных breakpoint()/set_trace().
bash
rg -n 'breakpoint\(\)|set_trace\(|debugpy\.listen' --type py
Одноразовые рецепты
"Почему в этом диктовке отсутствует ключ?"
# add above the KeyError sitebreakpoint()# then in pdb:(Pdb)ppd(Pdb)pplist(d.keys())(Pdb)w# how did we get here
"Этот тест проходит изолированно, но не проходит в пакете."
scripts/run_tests.shtests/the_test.py--pdb-pno:xdist
# But if it only fails WITH other tests:
source.venv/bin/activate
python-mpytesttests/-x--pdb-pno:xdist
# Now it pdb-traps at the exact failing test after state accumulated.
"Мой асинхронный обработчик блокируется."
# Add at handler entryimportremote_pdb;remote_pdb.set_trace(host="127.0.0.1",port=4444)
Запустите обработчик. nc 127.0.0.1 4444, затем w, чтобы увидеть приостановленный кадр, !import asyncio; asyncio.all_tasks(), чтобы узнать, что еще ожидается.
"Вскрытие при сбое в дочернем процессе/подпроцессе Ink."
PYTHONFAULTHANDLER=1python-mpdb-ccontinuepath/to/entrypoint.py
# On crash, pdb lands at the frame of the exception with full locals