Python readline: History and Autocomplete

Published on: August 25, 2026
Reading time: 5 minutes
A developer typing code on a laptop with a Python book beside in an office.

The readline module adds line editing, command history, and tab completion to interactive terminal programs. It can affect the traditional Python prompt and ordinary input() calls, allowing users to navigate with arrow keys, reuse previous entries, and complete words without retyping them.

Despite its name, the module may be backed by GNU Readline or the compatible libedit implementation. That difference affects configuration files, key bindings, history formats, and small API details. Modern Python also introduces another distinction: the new REPL added in Python 3.13 does not use readline by default.

Availability

readline is optional and normally available on Unix. It is not supported on Android, iOS, or WASI. A distributor may build CPython without it, so portable software should handle ImportError.

try:
    import readline
except ImportError:
    readline = None

Windows may use third-party alternatives, but those are not the standard module. Do not make terminal convenience features a hard dependency when the main application can still run without them.

Detect the backend

Since Python 3.13, readline.backend reports either readline or editline.

import readline

print(readline.backend)

macOS commonly uses libedit. GNU Readline normally reads ~/.inputrc, while libedit commonly reads ~/.editrc. Their command syntax is different, and their history files may not be interchangeable.

Enable Tab completion

The simplest setup uses rlcompleter, which completes Python names in the current namespace.

import readline
import rlcompleter

if readline.backend == "editline":
    readline.parse_and_bind("bind ^I rl_complete")
else:
    readline.parse_and_bind("tab: complete")

The guide to Python rlcompleter covers identifier completion in more detail. Custom command applications usually need their own completer.

Write a custom completer

A completer receives text and state. It returns one match for each state value and eventually returns None.

import readline

COMMANDS = ["configure", "help", "list", "open", "quit", "run"]

def complete(text, state):
    matches = [item for item in COMMANDS if item.startswith(text)]
    if state < len(matches):
        return matches[state]
    return None

readline.set_completer(complete)
readline.set_completer_delims(" \t\n")
readline.parse_and_bind("tab: complete")

Keep completion fast. A function that performs a network request, scans a huge directory, or queries a database on every Tab press creates a poor interface.

Completion delimiters

set_completer_delims() controls which characters split the current word. File paths, dotted names, and colon-separated values may require removing characters from the default set.

delimiters = readline.get_completer_delims()
readline.set_completer_delims(delimiters.replace("/", ""))

get_begidx() and get_endidx() expose the active completion range. GNU Readline and libedit can return different indexes for similar editing states, so cross-platform tests are important.

In-memory history

The global history list can be inspected and modified.

import readline

readline.add_history("status")
readline.add_history("list projects")

count = readline.get_current_history_length()
for index in range(1, count + 1):
    print(index, readline.get_history_item(index))

get_history_item() uses one-based indexes. remove_history_item() and replace_history_item() use zero-based positions. Mixing those conventions is a common bug.

Do not store secrets

Tokens, passwords, private keys, recovery codes, and personal data must not be written to command history. Use getpass for passwords and disable automatic history around sensitive prompts.

import getpass
import readline

readline.set_auto_history(False)
try:
    secret = getpass.getpass("Token: ")
finally:
    readline.set_auto_history(True)

The setting is process-global. Restore it after errors and never log the captured value.

Load and save a history file

A persistent file makes commands available across sessions.

import atexit
import os
import readline

history_path = os.path.expanduser("~/.my_app_history")

try:
    readline.read_history_file(history_path)
except FileNotFoundError:
    pass

readline.set_history_length(1000)
atexit.register(readline.write_history_file, history_path)

Python 3.14 adds auditing events to history and initialization-file access. Security hooks may record or reject these operations.

Protect the history file

History often reveals paths, hostnames, arguments, and operational details. Create it in a private directory and use restrictive permissions where the platform supports them.

from pathlib import Path
import os

path = Path.home() / ".my_app_history"
if not path.exists():
    fd = os.open(path, os.O_CREAT | os.O_WRONLY, 0o600)
    os.close(fd)

Unix mode bits are not a universal security model. Windows access control requires different assumptions.

Concurrent sessions

write_history_file() overwrites the destination. Two open sessions can lose each other’s commands. When available, append_history_file() appends only new entries.

import atexit
import readline

starting_count = readline.get_current_history_length()

def save_incremental(path):
    current = readline.get_current_history_length()
    new_items = max(0, current - starting_count)
    readline.set_history_length(1000)
    if new_items:
        readline.append_history_file(new_items, path)

atexit.register(save_incremental, history_path)

Appending still does not guarantee perfect coordination between simultaneous writers. Use file locking or per-session history when consistency matters.

Limit growth

set_history_length() controls the number of lines saved. A negative value means unlimited history and may create an ever-growing file.

Choose a practical line limit and, for tools with large commands, consider a byte-size limit or rotation policy.

Modify the current line buffer

get_line_buffer() returns the current input. insert_text() inserts at the cursor, and redisplay() refreshes the screen.

import readline


def add_default_text():
    if not readline.get_line_buffer():
        readline.insert_text("list ")
        readline.redisplay()

readline.set_startup_hook(add_default_text)

A startup hook runs before the first prompt is printed. A pre-input hook, when supported, runs after the prompt and before character input begins.

Avoid unsafe automatic insertion

Text inserted into a prompt may look ready for execution. Do not construct commands from untrusted remote content, filenames, or pasted data without validation. The user should retain control before pressing Enter.

Configuration files

read_init_file() loads a configuration file, while parse_and_bind() applies one line directly.

if readline.backend == "readline":
    readline.parse_and_bind("set editing-mode vi")
else:
    readline.parse_and_bind("bind -v")

Do not automatically load a configuration file from an untrusted location. Readline configuration can define macros and change important key behavior.

Use with input()

After importing and configuring the module, regular input() calls gain editing and history behavior.

while True:
    try:
        command = input("app> ").strip()
    except EOFError:
        break
    except KeyboardInterrupt:
        print()
        continue

    if command == "quit":
        break
    execute(command)

Handle EOF and Ctrl+C without exposing a stack trace. The guide to Python signal explains interruption and cooperative shutdown.

The new Python REPL

The current documentation notes that the new REPL introduced in Python 3.13 does not use readline. Set PYTHON_BASIC_REPL to use the basic compatible REPL.

This limitation applies to the interpreter’s new interactive prompt. Applications that call input() can still configure readline normally.

Testing

Test GNU Readline and libedit, missing-module behavior, absent and read-only history files, concurrent sessions, Unicode input, zero and multiple completion matches, Ctrl+C, EOF, and non-interactive terminals.

Pseudo-terminal tests can emulate a real TTY. A later guide in this batch covers the Unix pty module.

Common mistakes

Common failures include assuming GNU Readline on macOS, sharing GNU and libedit configuration, storing secrets, leaving unlimited history, overwriting concurrent sessions, running slow operations in the completer, confusing history index bases, and requiring the module on unsupported platforms.

Conclusion

readline turns basic prompts into productive terminal interfaces with editing, history, and completion. Reliable use requires backend detection, secure history handling, bounded storage, and graceful fallback.

Keep completers fast, protect history files, and test both GNU Readline and libedit. Consult the official readline documentation and the GNU Readline manual.

Share:

Facebook
WhatsApp
Twitter
LinkedIn

Article content

    Related articles

    Peaceful river scene with mossy boulders and flowing water captured in long exposure.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python io: Master Streams and Buffers

    Learn Python io for text and binary streams, buffering, encodings, StringIO, BytesIO, raw I/O, and file-like interfaces.

    Ler mais

    Tempo de leitura: 6 minutos
    24/08/2026
    Business professional analyzing financial data on multiple computer monitors at his workspace.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python select: Monitor Multiple I/O

    Learn Python select to monitor sockets and pipes, handle partial I/O, backpressure, poll, epoll, signals, and platform differences.

    Ler mais

    Tempo de leitura: 6 minutos
    24/08/2026
    Striking image of a red-bellied python showcasing its vibrant scales in dramatic lighting.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python signal: Graceful Process Shutdown

    Learn Python signal to handle SIGTERM and SIGINT, stop services, use timers and wakeup file descriptors, and avoid handler deadlocks.

    Ler mais

    Tempo de leitura: 6 minutos
    24/08/2026
    A developer typing code on a laptop with a Python book beside in an office.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python errno: Understand System Errors

    Learn Python errno to interpret system codes, handle OSError, files, networks, retries, and native calls portably.

    Ler mais

    Tempo de leitura: 4 minutos
    24/08/2026
    Side view of young cheerful ethnic female with book speaking on cellphone in public library while looking away
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python ctypes: Call C Libraries

    Learn Python ctypes to load C libraries, define types and pointers, manage memory, callbacks, ABI, and native errors safely.

    Ler mais

    Tempo de leitura: 6 minutos
    24/08/2026
    A developer typing code on a laptop with a Python book beside in an office.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python expat: Low-Level XML Parsing

    Learn Python expat for low-level XML parsing, handlers, namespaces, diagnostics, and protections against amplification and denial of service.

    Ler mais

    Tempo de leitura: 4 minutos
    23/08/2026