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

    Rack de servidores representando balanceamento de conexões com SO_REUSEPORT_LB no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    SO_REUSEPORT_LB: distribua conexões entre workers

    Aprenda SO_REUSEPORT_LB no Python para distribuir conexões entre múltiplos workers com segurança, testes e portabilidade.

    Ler mais

    Tempo de leitura: 6 minutos
    11/10/2026
    Código Python assíncrono em notebook para inspect.markcoroutinefunction
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    markcoroutinefunction: identifique wrappers async

    Aprenda inspect.markcoroutinefunction no Python para identificar wrappers assíncronos, integrar frameworks e evitar detecção incorreta de corrotinas.

    Ler mais

    Tempo de leitura: 6 minutos
    10/10/2026
    Código Python para percorrer pastas e arquivos com Path.walk
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    Path.walk: percorra diretórios com segurança

    Aprenda Path.walk no Python para percorrer diretórios, filtrar arquivos, tratar erros e controlar a travessia com segurança.

    Ler mais

    Tempo de leitura: 6 minutos
    10/10/2026
    Depuração de processo Python em terminal com código
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    pdb -p: depure processos Python em execução

    Aprenda a anexar o pdb a um processo Python em execução, inspecionar pilhas e diagnosticar travamentos com segurança.

    Ler mais

    Tempo de leitura: 6 minutos
    09/10/2026
    Código Python e representação de frações numéricas
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    fractions.from_number: converta números em frações

    Aprenda fractions.from_number no Python para converter números em frações exatas, controlar precisão e evitar arredondamentos inesperados.

    Ler mais

    Tempo de leitura: 5 minutos
    09/10/2026
    Desenvolvedor configurando servidor HTTPS e certificado TLS com Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    HTTPSServer: crie servidor HTTPS local no Python

    Aprenda HTTPSServer no Python para servir HTTPS localmente, configurar certificados, usar threads e entender limites de segurança.

    Ler mais

    Tempo de leitura: 5 minutos
    08/10/2026