readline no Python: histórico e autocomplete

Publicado em: 25/08/2026
Tempo de leitura: 6 minutos
Close-up view of a computer screen displaying code in a software development environment.

O módulo readline adiciona edição de linha, histórico de comandos e autocompletar a aplicações interativas executadas no terminal. Ele influencia o prompt tradicional do Python e também chamadas de input(), permitindo navegar com as setas, reutilizar entradas anteriores e completar palavras com Tab.

Apesar do nome, o módulo pode usar GNU Readline ou a implementação compatível libedit. Essa diferença afeta arquivos de configuração, atalhos, histórico e alguns detalhes da API. Em Python moderno, também é importante saber que o novo REPL introduzido no Python 3.13 não depende de readline por padrão.

Disponibilidade

readline é um módulo opcional e normalmente está disponível em sistemas Unix. Ele não é suportado em Android, iOS ou WASI. Algumas distribuições podem compilar o Python sem a biblioteca, portanto uma aplicação portátil deve tratar ImportError.

try:
    import readline
except ImportError:
    readline = None

No Windows, bibliotecas de terceiros podem oferecer comportamento semelhante, mas não fazem parte do módulo padrão. Não force a importação quando a aplicação precisa funcionar em vários sistemas.

Descobrir o backend

Desde o Python 3.13, readline.backend informa se a implementação é readline ou editline.

import readline

print(readline.backend)

No macOS, libedit é comum. O arquivo de configuração costuma ser ~/.editrc, enquanto GNU Readline usa normalmente ~/.inputrc. Não copie comandos de configuração entre os dois formatos sem verificar o backend.

Ativar autocomplete com Tab

O caso mais simples usa rlcompleter, que completa nomes disponíveis no namespace Python.

import readline
import rlcompleter

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

O módulo rlcompleter no Python explica como completar identificadores e objetos. Para aplicações próprias, um completer customizado oferece mais controle.

Criar um completer customizado

A função recebe text e state. Ela deve retornar uma sugestão por chamada e terminar com None.

import readline

COMANDOS = ["abrir", "ajuda", "configurar", "executar", "sair"]

def completar(texto, estado):
    opcoes = [item for item in COMANDOS if item.startswith(texto)]
    if estado < len(opcoes):
        return opcoes[estado]
    return None

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

Evite acessar rede, banco de dados ou filesystem pesado a cada pressionamento de Tab. O completer é chamado repetidamente e precisa responder rápido.

Delimitadores de palavras

set_completer_delims() define quais caracteres separam a palavra atual. Para comandos com caminhos, pontos, barras ou dois-pontos, o padrão pode cortar o texto cedo demais.

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

Os índices get_begidx() e get_endidx() mostram a região do buffer sendo completada. GNU Readline e libedit podem devolver valores diferentes em cenários semelhantes, então teste nos sistemas suportados.

Histórico em memória

O histórico global pode ser consultado e alterado programaticamente.

import readline

readline.add_history("status")
readline.add_history("listar projetos")

quantidade = readline.get_current_history_length()
for indice in range(1, quantidade + 1):
    print(indice, readline.get_history_item(indice))

get_history_item() usa índice começando em 1. Já remove_history_item() e replace_history_item() usam posição começando em zero. Essa diferença é uma fonte comum de erros.

Não grave segredos no histórico

Aplicações que solicitam tokens, senhas, chaves privadas ou dados pessoais não devem permitir que essas entradas sejam gravadas. Use getpass para senhas e desative o histórico automático ao redor de prompts sensíveis.

import getpass
import readline

readline.set_auto_history(False)
segredo = getpass.getpass("Token: ")
readline.set_auto_history(True)

O estado é global ao processo. Restaure-o mesmo em caso de erro e não registre o valor em logs. Para aplicações complexas, encapsule a mudança em um context manager.

Ler e salvar histórico

Um arquivo de histórico torna comandos disponíveis em sessões futuras.

import atexit
import os
import readline

historico = os.path.expanduser("~/.meu_app_history")

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

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

No Python 3.14, leitura e escrita desses arquivos geram eventos de auditoria. Ambientes com hooks de segurança podem registrar ou bloquear a operação.

Permissões do arquivo

O histórico pode conter caminhos, nomes de servidores, argumentos e outros dados sensíveis. Crie o arquivo com permissões restritas e evite diretórios compartilhados.

from pathlib import Path
import os

caminho = Path.home() / ".meu_app_history"
if not caminho.exists():
    descritor = os.open(caminho, os.O_CREAT | os.O_WRONLY, 0o600)
    os.close(descritor)

Em sistemas não Unix, a política de permissões é diferente. Não trate 0o600 como garantia universal.

Sessões concorrentes

write_history_file() sobrescreve o arquivo. Duas sessões abertas podem perder comandos uma da outra. Quando disponível, append_history_file() acrescenta apenas as entradas novas.

import atexit
import readline

inicio = readline.get_current_history_length()

def salvar_incremental(caminho):
    atual = readline.get_current_history_length()
    novos = max(0, atual - inicio)
    readline.set_history_length(1000)
    if novos:
        readline.append_history_file(novos, caminho)

atexit.register(salvar_incremental, historico)

Mesmo o append pode sofrer corrida quando várias instâncias escrevem ao mesmo tempo. Para consistência forte, use lock de arquivo ou histórico separado por sessão.

Limitar crescimento

set_history_length() controla quantas linhas são mantidas ao escrever. Valor negativo significa ilimitado e pode fazer o arquivo crescer indefinidamente.

Defina um limite compatível com o uso. Ferramentas com comandos extensos podem medir também o tamanho em bytes e rotacionar o arquivo.

Editar o buffer atual

get_line_buffer() retorna o texto digitado. insert_text() insere conteúdo no cursor e redisplay() atualiza a tela.

import readline


def preencher_padrao():
    if not readline.get_line_buffer():
        readline.insert_text("listar ")
        readline.redisplay()

readline.set_startup_hook(preencher_padrao)

O startup hook roda antes do prompt ser exibido. O pre-input hook, quando disponível, roda depois do prompt e antes da leitura dos caracteres.

Não insira dados não confiáveis

Texto inserido no buffer pode parecer uma instrução pronta para execução. Não preencha automaticamente comandos construídos com entrada externa, nomes de arquivos maliciosos ou conteúdo remoto sem escapar e validar.

O usuário deve continuar no controle antes de pressionar Enter.

Arquivo de configuração

read_init_file() carrega um arquivo de configuração. parse_and_bind() aplica uma linha diretamente.

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

Não carregue automaticamente um arquivo fornecido por usuário não confiável. A configuração pode alterar atalhos, macros e comportamento interativo.

Integração com input()

Depois de importar e configurar readline, chamadas comuns de input() passam a usar edição e histórico.

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

    if comando == "sair":
        break
    executar(comando)

Trate EOF e Ctrl+C sem imprimir stack trace para o usuário. O conjunto sobre signal no Python explica interrupções e encerramento cooperativo.

Novo REPL do Python

A documentação atual informa que o novo REPL introduzido no Python 3.13 não usa readline. Para voltar ao REPL básico compatível, defina a variável PYTHON_BASIC_REPL.

Isso não impede que aplicações próprias usem readline com input(); a observação vale especificamente para o novo prompt interativo do interpretador.

Testes

Teste GNU Readline e libedit, ausência do módulo, histórico inexistente, arquivo sem permissão, duas sessões concorrentes, limite de histórico, Tab com zero e várias sugestões, caracteres Unicode, interrupção por Ctrl+C e execução em terminal não interativo.

Automatizar testes de terminal pode exigir pseudo-terminal. O próximo conjunto sobre pty mostrará como simular uma sessão TTY em Unix.

Erros comuns

Os erros mais frequentes são presumir GNU Readline no macOS, usar a mesma configuração em libedit, salvar senhas no histórico, deixar o histórico ilimitado, sobrescrever sessões concorrentes, executar I/O lento no completer, esquecer que índices de histórico usam bases diferentes e depender do módulo em plataformas não suportadas.

Conclusão

readline transforma prompts simples em interfaces de terminal mais produtivas, com edição, histórico e autocomplete. Para usá-lo bem, trate backend, disponibilidade, segurança do histórico e concorrência.

Prefira completadores rápidos, limite o arquivo, não grave segredos e teste GNU Readline e libedit. Consulte a documentação oficial de readline e o manual GNU Readline.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Serene stream flowing through Bavarian mountains, capturing winter beauty and natural tranquility.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    io no Python: domine streams e buffers

    Aprenda io no Python para trabalhar com streams de texto e bytes, buffering, encoding, StringIO, BytesIO e I/O bruto com

    Ler mais

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

    select no Python: monitore vários I/Os

    Aprenda select no Python para monitorar sockets e pipes, tratar leituras parciais, backpressure, epoll, poll e sinais sem busy loop.

    Ler mais

    Tempo de leitura: 7 minutos
    24/08/2026
    View of multiple railway tracks with signals and buildings in an urban setting during daytime.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    signal no Python: encerre processos bem

    Aprenda signal no Python para tratar SIGTERM e SIGINT, encerrar serviços, usar timers, wakeup FD e coordenar shutdown sem deadlocks.

    Ler mais

    Tempo de leitura: 7 minutos
    24/08/2026
    Detailed view of programming code in a dark theme on a computer screen.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    errno no Python: entenda erros do sistema

    Aprenda errno no Python para interpretar códigos do sistema, tratar OSError, rede, arquivos, retries e chamadas nativas de forma portátil.

    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.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    ctypes no Python: use bibliotecas C

    Aprenda ctypes no Python para carregar bibliotecas C, definir tipos e ponteiros, gerenciar memória, callbacks, ABI e erros com segurança.

    Ler mais

    Tempo de leitura: 6 minutos
    24/08/2026
    A laptop screen showing a code editor with visible programming code in a dimly lit environment.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    expat no Python: parser XML de baixo nível

    Aprenda expat no Python para parsing XML de baixo nível, handlers, namespaces, erros e proteções contra amplificação e DoS.

    Ler mais

    Tempo de leitura: 5 minutos
    23/08/2026