2025, Nov 15 23:00
Restore Python readline history and input() editing when running under GDB: reset sys.stdout and sys.stderr
Learn why readline history and input() break in Python under GDB and how to fix it by restoring sys.stdout and sys.stderr, plus an alternative via gdb.Command.
Running Python under GDB is convenient when you want to debug at the boundary between native code and Python, but it comes with a subtle trap: line editing and history in Python’s readline stop working. Typical shortcuts like Ctrl+P don’t respond, and readline reports no history at all. If you launch a script with gdb -x py_script.py and rely on input(), you’ve likely seen this firsthand.
Reproducing the issue
The behavior is easy to trigger. The following minimal script reads input in a loop and prints the current readline history length. Under GDB, the count never grows and the familiar input editing is effectively disabled.
import readline
while True:
line_buf = input("_in: ")
print(readline.get_current_history_length())
print('out:', line_buf)
Invoked inside a GDB session, readline shows a history length of 0 regardless of how many entries you type.
What is actually happening
The behavior ties back to how GDB integrates with Python’s I/O at startup. GDB replaces Python’s standard output streams with its own paging streams. The official manual is explicit about this:
At startup, GDB overrides Python’s sys.stdout and sys.stderr to print using GDB’s output-paging streams. A Python program which outputs to one of these streams may have its output interrupted by the user (see Screen Size). In this situation, a Python KeyboardInterrupt exception is thrown.
Redirected streams are enough to derail readline’s editing and history in this setup. Attempts to use gdb.sys.stdin, gdb.sys.stdout or gdb.sys.stderr directly do not resolve it.
The fix
Reset sys.stdout and sys.stderr to the built-in original streams before importing readline. With those two assignments in place, input editing and history behave as expected inside 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)
After this change, readline history increments on each input and the familiar navigation keys work.
An alternative path: avoid input() entirely
In many GDB-based workflows there may not be a strong reason to use input() at all. A common pattern is to implement a custom GDB command in Python and pass arguments directly from the GDB prompt. Here is a compact example:
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()
This approach avoids interactive line editing in Python entirely and slots cleanly into existing GDB workflows.
Why this matters
If you’re mixing Python with GDB, diagnosing broken input() behavior can be frustrating, especially when readline silently loses history and editing. Knowing that GDB overrides sys.stdout and sys.stderr, and that restoring sys.__stdout__ and sys.__stderr__ fixes the symptoms, saves time and prevents detours into unrelated input plumbing.
Takeaways
If readline editing and history break when running Python inside GDB, restore the original standard streams before importing readline by assigning sys.stdout = sys.__stdout__ and sys.stderr = sys.__stderr__. If your use case allows, consider handling interaction via a gdb.Command instead of input(). Either way, understanding how GDB touches Python’s I/O helps you keep interactive behavior predictable.