2025, Nov 23 03:02
Почему в GDB ломается readline в Python и как это исправить
Python под GDB теряет редактирование и историю readline. Поясняем причину (sys.stdout/sys.stderr) и показываем, как вернуть работу или обойтись без input().
Запуск Python под GDB удобен, когда нужно отлаживать стык нативного кода и Python, но есть тонкая ловушка: построчное редактирование и история в readline перестают работать. Привычные сочетания вроде Ctrl+P не срабатывают, а readline вообще не видит истории. Если вы запускаете скрипт командой gdb -x py_script.py и полагаетесь на input(), вероятно, уже сталкивались с этим.
Как воспроизвести проблему
Поведение легко воспроизвести. Минимальный скрипт ниже читает ввод в цикле и выводит текущую длину истории readline. Под GDB счётчик не растёт, а привычное редактирование ввода фактически отключено.
import readline
while True:
line_buf = input("_in: ")
print(readline.get_current_history_length())
print('out:', line_buf)
Запущенный внутри сеанса GDB, readline показывает длину истории 0 вне зависимости от того, сколько строк вы ввели.
Что происходит на самом деле
Это связано с тем, как GDB при старте интегрируется с вводом-выводом Python. GDB заменяет стандартные потоки вывода Python собственными постраничными потоками. Официальное руководство говорит об этом напрямую:
При запуске GDB переопределяет sys.stdout и sys.stderr Python, чтобы печатать через собственные постраничные потоки вывода GDB. Программа на Python, которая пишет в один из этих потоков, может иметь вывод, прерванный пользователем (см. Screen Size). В такой ситуации возбуждается исключение Python KeyboardInterrupt.
Одного этого перенаправления достаточно, чтобы в такой конфигурации сломать редактирование и историю readline. Попытки обращаться напрямую к gdb.sys.stdin, gdb.sys.stdout или gdb.sys.stderr проблему не решают.
Решение
Верните sys.stdout и sys.stderr к встроенным исходным потокам до импорта readline. С этими двумя присваиваниями редактирование ввода и история в GDB работают как обычно.
import sys
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__
import readline
while True:
entry = input("_in: ")
print(readline.get_current_history_length())
print('out:', entry)
После этого история readline увеличивается при каждом вводе, а привычные клавиши навигации начинают работать.
Альтернативный путь: вовсе не использовать input()
Во многих сценариях с GDB нет острой необходимости использовать input(). Типичный приём — реализовать собственную команду GDB на Python и передавать аргументы прямо из приглашения GDB. Ниже компактный пример:
import gdb
class PromptCmd(gdb.Command):
def __init__(self):
super().__init__("ask", gdb.COMMAND_USER)
def invoke(self, arg, from_tty):
print("You typed:", arg)
PromptCmd()
Такой подход полностью устраняет интерактивное редактирование строк в Python и органично вписывается в привычные рабочие процессы с GDB.
Почему это важно
Когда вы совмещаете Python и GDB, разбираться, почему ломается поведение input(), бывает утомительно — особенно когда readline тихо теряет историю и возможности редактирования. Понимание того, что GDB переопределяет sys.stdout и sys.stderr, а восстановление sys.__stdout__ и sys.__stderr__ устраняет симптомы, экономит время и избавляет от бесполезных приключений с подсистемой ввода.
Итоги
Если при запуске Python внутри GDB у вас исчезают редактирование и история readline, до импорта readline верните оригинальные стандартные потоки присваиваниями sys.stdout = sys.__stdout__ и sys.stderr = sys.__stderr__. Если это подходит под ваш сценарий, рассмотрите вариант обработки взаимодействия через gdb.Command вместо input(). В любом случае понимание того, как GDB влияет на I/O Python, помогает сохранить предсказуемое интерактивное поведение.