O interpretador Python precisa gravar e carregar algumas estruturas internas com rapidez, especialmente objetos de código usados em arquivos .pyc. O módulo marshal no Python expõe esse formato binário de baixo nível para tipos simples e objetos internos. Ele pode ser útil em ferramentas especializadas, mas não foi projetado como formato geral de persistência, intercâmbio ou entrada externa.
Neste guia, você aprenderá a usar dump(), load(), dumps() e loads(), controlar versões e bloquear code objects. O conteúdo complementa nossos artigos sobre pickle, pickletools, py_compile, bytecode com dis e comparação de arquivos.
Para que marshal existe
Marshal foi criado para necessidades internas do Python. Seu principal uso é apoiar a leitura e escrita de estruturas do interpretador, não fornecer um protocolo estável para aplicações.
Ao contrário de JSON, ele não prioriza interoperabilidade. Ao contrário de pickle, não tenta serializar instâncias arbitrárias de classes. A documentação deixa claro que detalhes do formato podem mudar entre versões.
Serializar em memória
marshal.dumps() converte um valor suportado em bytes.
import marshal
objeto = {
"nome": "Ana",
"pontos": [10, 20, 30],
}
dados = marshal.dumps(objeto)
print(type(dados), len(dados))O resultado é binário e não deve ser editado manualmente.
Restaurar a partir de bytes
marshal.loads() lê um valor dos bytes.
restaurado = marshal.loads(dados)
print(restaurado)Use somente bytes produzidos por um ambiente confiável e compatível. A documentação oficial de marshal adverte explicitamente contra dados não confiáveis.
Gravar em arquivo
dump() escreve um valor em um objeto de arquivo binário.
with open("dados.marshal", "wb") as arquivo:
marshal.dump(objeto, arquivo)Se o valor contém um tipo não suportado, a função gera ValueError. Parte de dados inválidos pode já ter sido escrita no arquivo, portanto não reutilize o destino depois de uma falha.
Gravação atômica
Para evitar arquivos parciais, grave em um temporário e substitua o destino somente após sucesso.
from pathlib import Path
caminho = Path("dados.marshal")
temporario = caminho.with_suffix(".tmp")
with temporario.open("wb") as arquivo:
marshal.dump(objeto, arquivo)
temporario.replace(caminho)Essa técnica reduz corrupção por erro de serialização, mas não garante durabilidade contra perda de energia sem flush e sincronização adequados.
Ler de um arquivo
load() lê um único valor a partir da posição atual.
with open("dados.marshal", "rb") as arquivo:
objeto = marshal.load(arquivo)Bytes posteriores ao primeiro valor permanecem no arquivo. Isso permite concatenar vários valores, mas a aplicação precisa saber quantos existem ou usar framing próprio.
Vários valores em sequência
with open("sequencia.marshal", "wb") as arquivo:
marshal.dump({"id": 1}, arquivo)
marshal.dump({"id": 2}, arquivo)
with open("sequencia.marshal", "rb") as arquivo:
primeiro = marshal.load(arquivo)
segundo = marshal.load(arquivo)Sem contador, tamanho ou marcador externo, não existe uma forma robusta de distinguir final válido de truncamento apenas pelo protocolo da aplicação.
Tipos suportados
Marshal trabalha com vários tipos simples usados internamente, incluindo:
None, booleanos e números;- strings e objetos de bytes;
- tuplas, listas e dicionários;
- sets e frozensets;
- algumas estruturas adicionais conforme a versão do formato;
- objetos de código quando permitidos.
Instâncias arbitrárias de classes não são suportadas como no pickle.
Recursão em contêineres
Versões modernas do formato conseguem representar determinados contêineres recursivos.
lista = []
lista.append(lista)
dados = marshal.dumps(lista)
restaurada = marshal.loads(dados)
assert restaurada[0] is restauradaEstruturas extremamente profundas podem atingir limites e causar falhas. Não use profundidade sem controle.
Versão do formato
O módulo expõe marshal.version, que indica a versão atual do formato usada por padrão.
print(marshal.version)
dados = marshal.dumps(objeto, marshal.version)O parâmetro de versão controla o formato, não a compatibilidade completa entre interpretadores. Um valor suportado hoje pode envolver tipos que outra versão não entende.
Não confundir formato com versão do Python
O número do formato marshal não é igual ao número de versão do Python. Além disso, objetos de código podem mudar mesmo quando o formato marshal é reconhecido.
Registre implementação, versão completa do Python, plataforma e versão do formato junto ao artefato quando a compatibilidade importa.
Objetos de código
Code objects representam bytecode, constantes, nomes e metadados de uma função ou módulo compilado.
codigo = compile("resultado = 2 + 2", "<exemplo>", "exec")
dados = marshal.dumps(codigo)
restaurado = marshal.loads(dados)Executar o objeto restaurado continua sendo execução de código.
ambiente = {}
exec(restaurado, ambiente)
print(ambiente["resultado"])Nunca execute code objects vindos de uma origem desconhecida.
Bloquear code objects
Versões recentes oferecem o argumento nomeado allow_code.
dados = marshal.dumps(
objeto,
allow_code=False,
)
restaurado = marshal.loads(
dados,
allow_code=False,
)Quando falso, serializar ou carregar objetos de código é rejeitado. Esse controle reduz uma categoria de dados, mas não transforma bytes hostis em entrada segura.
Compatibilidade de code objects
A documentação alerta que o formato dos objetos de código não é compatível entre versões do Python. Carregar um code object de versão incorreta possui comportamento indefinido.
Não armazene code objects como cache duradouro. Use o mecanismo oficial de .pyc, que inclui tags e cabeçalhos de validação.
marshal e arquivos pyc
Embora arquivos .pyc usem marshal para a parte do code object, eles também possuem cabeçalho gerenciado por importlib. Não tente construir um pyc apenas gravando marshal.dumps(codigo).
Use py_compile ou compileall para gerar caches válidos.
marshal versus pickle
Pickle suporta classes personalizadas e protocolos de reconstrução, mas pode executar callables durante a carga. Marshal suporta um conjunto menor e continua sendo inseguro para dados não confiáveis.
Nenhum dos dois é adequado para uploads externos. Para dados, prefira JSON, banco de dados ou um formato com esquema.
marshal versus JSON
JSON possui interoperabilidade, texto legível e um modelo restrito. Marshal é específico do Python e otimizado para detalhes internos.
Mesmo quando marshal produz arquivos menores, a falta de estabilidade costuma ser um custo maior em persistência de aplicação.
Detecção de truncamento
Um arquivo incompleto pode gerar EOFError, ValueError ou outro erro de leitura.
try:
with open("dados.marshal", "rb") as arquivo:
objeto = marshal.load(arquivo)
except (EOFError, ValueError, TypeError) as erro:
print("Arquivo inválido:", erro)Não tente continuar usando um valor parcialmente lido.
Integridade externa
Quando dados internos precisam ser armazenados, mantenha hash ou HMAC separado.
import hashlib
assinatura = hashlib.sha256(dados).hexdigest()Um hash simples detecta corrupção acidental, mas não adulteração por um invasor que pode recalculá-lo. Para autenticidade, use HMAC com uma chave secreta.
Limites de recursos
Dados malformados podem tentar criar estruturas grandes ou profundas. Antes de ler, limite o tamanho do arquivo e processe entradas potencialmente hostis em subprocesso com memória e tempo controlados.
from pathlib import Path
caminho = Path("dados.marshal")
if caminho.stat().st_size > 10_000_000:
raise ValueError("arquivo grande demais")Essa verificação não substitui isolamento.
Eventos de auditoria
Operações de marshal geram eventos de auditoria do Python. Ambientes embutidos e políticas de segurança podem observar carregamentos e gravações.
Não dependa apenas desses eventos para impedir entrada perigosa. A validação de origem deve acontecer antes da chamada.
Uso em testes e ferramentas
Marshal pode ser útil em testes do próprio interpretador, estudos de formato e ferramentas que trabalham com artefatos efêmeros da mesma versão.
Documente explicitamente que o arquivo é descartável e deve ser regenerado ao trocar de runtime.
Teste de round-trip
def test_round_trip():
original = {
"ids": [1, 2, 3],
"ativo": True,
}
restaurado = marshal.loads(marshal.dumps(original))
assert restaurado == originalTeste tipos suportados, arquivo vazio, truncamento, versão escolhida e allow_code=False.
Erros frequentes
- Usar marshal como banco de dados duradouro.
- Carregar bytes recebidos de usuários.
- Presumir compatibilidade entre versões.
- Executar code objects restaurados.
- Ignorar arquivo parcial após
ValueError. - Confundir dados marshal com pyc completo.
- Não limitar tamanho e profundidade.
- Tratar
allow_code=Falsecomo sandbox.
Boas práticas
- Use marshal somente para necessidades internas e efêmeras.
- Mantenha versão de Python e formato registradas.
- Bloqueie code objects quando não forem necessários.
- Grave por arquivo temporário e substituição.
- Valide tamanho antes de carregar.
- Use integridade autenticada para dados internos.
- Regere artefatos ao trocar de runtime.
- Prefira formatos estáveis para dados de aplicação.
Conclusão
O módulo marshal no Python oferece serialização binária rápida para um conjunto de tipos e estruturas internas do interpretador. Ele explica parte do funcionamento dos caches de bytecode e pode apoiar ferramentas especializadas.
Seu contrato é deliberadamente limitado: o formato não é estável para persistência geral, code objects não são portáveis e dados desconhecidos não são seguros. Use marshal apenas em ambientes controlados, com artefatos descartáveis, limites de recursos e compatibilidade de runtime bem definida.





