tomllib, disponível na biblioteca padrão desde Python 3.11, lê documentos TOML 1.0 e retorna dicionários, listas e tipos de data correspondentes. Ele é útil para inspecionar configurações e pyproject.toml sem uma dependência adicional.
Ler e validar os campos
from pathlib import Path
import tomllib
def carregar_config(caminho: Path) -> dict[str, object]:
if caminho.stat().st_size > 1_000_000:
raise ValueError("arquivo de configuração muito grande")
with caminho.open("rb") as arquivo:
dados = tomllib.load(arquivo)
app = dados.get("app")
if not isinstance(app, dict) or not isinstance(app.get("porta"), int):
raise ValueError("app.porta é obrigatório")
return dados
Parsing bem-sucedido confirma a sintaxe, não o contrato da aplicação. Valide tabelas, tipos, limites e valores desconhecidos. Trate tomllib.TOMLDecodeError para produzir uma mensagem útil sem expor conteúdo sensível.
Para números que exigem precisão decimal:
from decimal import Decimal
import tomllib
dados = tomllib.loads("taxa = 0.1", parse_float=Decimal)
O artigo sobre pyproject.toml explica as tabelas usadas por projetos Python. tomllib não escreve nem preserva comentários; não tente editar um arquivo apenas serializando o dicionário com outro formato.
A documentação oficial de tomllib, consultada em 22 de julho de 2026, alerta para limitar o tamanho de entrada não confiável e descreve a conversão de tipos. Separe parsing, validação e aplicação da configuração para manter erros claros.
Escolher load() ou loads()
tomllib.load() lê um arquivo binário; tomllib.loads() recebe uma string:
with open("pyproject.toml", "rb") as arquivo:
projeto = tomllib.load(arquivo)
trecho = tomllib.loads("""
[servidor]
host = "127.0.0.1"
porta = 8080
""")
TOML usa UTF-8. Abra com "rb" para load(), como a API exige. Se outra fonte já decodificou o documento, use loads(). Rejeite entrada grande antes do parsing quando arquivo ou requisição não for confiável, pois estruturas profundas consomem CPU e memória.
Sintaxe inválida gera tomllib.TOMLDecodeError. A mensagem ajuda o desenvolvedor, mas pode revelar conteúdo sensível. Traduza-a na fronteira:
def ler_toml(caminho: Path) -> dict[str, object]:
try:
with caminho.open("rb") as arquivo:
return tomllib.load(arquivo)
except tomllib.TOMLDecodeError as exc:
raise ValueError(f"TOML inválido em {caminho.name}") from exc
Não converta toda exceção em “TOML inválido”. FileNotFoundError, PermissionError e falhas de I/O descrevem problemas operacionais diferentes.
Conhecer os tipos convertidos
Strings viram str, inteiros viram int, booleanos viram bool, arrays viram listas e tabelas viram dicionários. Datas e horas locais viram date, time ou datetime ingênuo; datas com offset viram datetime consciente. Não presuma que todo valor temporal é string.
from datetime import datetime
dados = tomllib.loads("""
lancamento = 2026-08-15T15:00:00Z
manutencao = 2026-08-16
""")
lancamento = dados["lancamento"]
if not isinstance(lancamento, datetime) or lancamento.tzinfo is None:
raise ValueError("lancamento precisa de offset")
Floats normalmente viram float. Use parse_float para semântica decimal:
from decimal import Decimal
precos = tomllib.loads(
'mensalidade = 19.90',
parse_float=Decimal,
)
assert precos["mensalidade"] == Decimal("19.90")
O callable recebe o token TOML original e não pode retornar dicionário ou lista. Ele afeta floats, não inteiros.
Validar schema depois do parsing
TOML válido pode ser configuração inválida. Confira tabelas obrigatórias, tipos exatos, faixas, opções incompatíveis e chaves desconhecidas. Como bool é subclasse de int, isinstance(True, int) é verdadeiro; às vezes type(valor) is int é necessário.
from dataclasses import dataclass
@dataclass(frozen=True)
class ConfigServidor:
host: str
porta: int
debug: bool
def validar_servidor(dados: dict[str, object]) -> ConfigServidor:
tabela = dados.get("servidor")
if not isinstance(tabela, dict):
raise ValueError("tabela [servidor] obrigatória")
desconhecidas = set(tabela) - {"host", "porta", "debug"}
if desconhecidas:
raise ValueError(f"campos desconhecidos: {sorted(desconhecidas)}")
host = tabela.get("host")
porta = tabela.get("porta")
debug = tabela.get("debug", False)
if not isinstance(host, str) or not host:
raise ValueError("host precisa ser string não vazia")
if type(porta) is not int or not 1 <= porta <= 65535:
raise ValueError("porta deve ficar entre 1 e 65535")
if type(debug) is not bool:
raise ValueError("debug precisa ser booleano")
return ConfigServidor(host, porta, debug)
Converter o dicionário em objeto imutável concentra a validação e impede que código distante dependa de chaves não verificadas. Bibliotecas de schema ajudam em contratos maiores, mas tomllib deliberadamente não oferece essa etapa.
Ler pyproject.toml defensivamente
A tabela [project] é padronizada pela especificação de empacotamento; configurações próprias ficam em [tool.<nome>]. O arquivo pode omitir [project] quando outro mecanismo fornece metadados, então ferramentas de inspeção devem relatar a ausência.
Chaves com pontos têm significado próprio salvo quando estão entre aspas. Examine a estrutura resultante, não deduza pelo texto. Arrays de tabelas viram listas de dicionários; valide cada item.
Configuração pode controlar imports, arquivos, destinos de rede ou comandos. Parsing não torna esses valores seguros. Aplique contenção de caminhos, políticas de URL e allowlists quando o valor ganhar poder. Nunca execute expressão Python vinda de TOML.
Combinar fontes explicitamente
Aplicações combinam padrões, TOML, ambiente e linha de comando. Documente a precedência e valide o resultado final. Um dict.update() raso pode substituir uma tabela inteira; merge recursivo genérico pode criar combinações não previstas. Prefira construir campo a campo o objeto tipado.
Não registre o dicionário inteiro: credenciais e endpoints privados podem estar presentes. Registre a fonte carregada e apenas modos não secretos. Diferencie arquivo opcional ausente de arquivo obrigatório ilegível.
Respeitar o limite de somente leitura
tomllib não serializa TOML nem preserva comentários, espaços, aspas ou apresentação. Seus dicionários servem para consumir valores, não para edição round-trip. Use um writer para gerar arquivo novo e um editor que preserve estilo para modificar arquivo humano. Não regrave pyproject.toml a partir do dicionário esperando conservar o original.
Teste entrada mínima, configuração completa, sintaxe quebrada, tabelas ausentes, tipos errados, campos desconhecidos, valores-limite, Unicode, variantes temporais e rejeição por tamanho. Mantenha cada fixture focada.
O fluxo confiável é limitar e ler, interpretar TOML, validar o contrato, converter em valores tipados e só então aplicar. Essa separação produz erros precisos e impede confundir sintaxe aceita com autorização ou validade de negócio.
Planejar a evolução da configuração
Configuração é uma interface e precisa de compatibilidade. Ao renomear uma chave, decida se aceita a grafia antiga temporariamente, emite aviso de descontinuação ou rejeita com instrução de migração. Não escolha silenciosamente entre duas chaves conflitantes. Se versões implantadas compartilham arquivo, documente a versão mínima exigida por opção.
Um campo config_version ajuda em mudanças estruturais grandes, mas não substitui validação. Interprete a versão primeiro, encaminhe ao validador correspondente e converta estruturas antigas para um único objeto de domínio atual. Teste todas as versões aceitas e a mensagem das não suportadas.
Padrões pertencem ao código quando fazem parte do comportamento; exemplos pertencem a arquivos de amostra documentados. Copiar uma amostra para produção não pode habilitar placeholders de segredos com aparência válida. Exija que credenciais venham do mecanismo persistente aprovado, sem fallback inseguro.
Se a configuração recarrega durante a execução, interprete e valide um candidato completo antes de substituir o estado ativo. Uma aplicação parcial pode deixar componentes discordando. Troque um objeto imutável atomicamente quando possível, preserve o último valor válido após falha e emita diagnóstico sanitizado.
Eventos de monitoramento de arquivo podem chegar repetidos ou enquanto o editor substitui o arquivo. Faça debounce e repita somente erros transitórios compreendidos. Não trate sintaxe quebrada como motivo para zerar configuração.
Conferir segurança e manutenção
Defina permissões para que apenas a conta de implantação escreva e a aplicação leia. tomllib não impede alteração maliciosa do arquivo. Quando o risco exigir, verifique origem, propriedade e integridade antes de aplicar.
Ao remover uma opção, procure seu uso em amostras, documentação e arquivos implantados. Mensagens devem citar a chave e a ação necessária sem imprimir o valor. Para listas e tabelas extensas, inclua o índice do item inválido.
Em revisão de código, confirme limite de tamanho, modo binário, tratamento específico de erros, validação de campos desconhecidos, faixa de números e comportamento temporal. Confira ainda se valores influentes passam por controles no ponto de uso.
Registre essas decisões junto ao contrato da aplicação para que futuras alterações mantenham o mesmo nível de validação.