El intérprete Python necesita una forma rápida de guardar y cargar algunas estructuras internas, especialmente objetos de código usados en archivos .pyc. El módulo marshal en Python expone este formato binario de bajo nivel para valores simples y datos del intérprete. Puede ayudar en herramientas especializadas, pero no fue diseñado como formato general de persistencia, intercambio o entrada externa.
Esta guía explica dump(), load(), dumps(), loads(), versiones de formato y bloqueo de code objects. Complementa nuestros artículos sobre pickle, pickletools, py_compile, bytecode con dis y comparación de archivos.
Para qué existe marshal
Marshal fue creado para necesidades internas de Python. Su propósito principal es ayudar al intérprete a leer y escribir estructuras internas, no proporcionar un protocolo estable para aplicaciones.
A diferencia de JSON, no prioriza interoperabilidad. A diferencia de pickle, no intenta serializar instancias arbitrarias. La documentación advierte que los detalles pueden cambiar entre versiones.
Serializar en memoria
marshal.dumps() convierte un valor soportado en bytes.
import marshal
objeto = {
"nombre": "Ana",
"puntos": [10, 20, 30],
}
datos = marshal.dumps(objeto)
print(type(datos), len(datos))El resultado es binario y no debería editarse manualmente.
Restaurar desde bytes
marshal.loads() lee un valor.
restaurado = marshal.loads(datos)
print(restaurado)Utiliza solamente bytes producidos por un entorno confiable y compatible. La documentación oficial de marshal advierte contra datos no confiables.
Escribir en archivo
dump() escribe en un archivo binario.
with open("datos.marshal", "wb") as archivo:
marshal.dump(objeto, archivo)Si existe un tipo no soportado, genera ValueError. Parte de datos inválidos puede haber sido escrita, por lo que no debes reutilizar el destino tras el fallo.
Escritura atómica
Escribe en un temporal y reemplaza el destino solo después del éxito.
from pathlib import Path
ruta = Path("datos.marshal")
temporal = ruta.with_suffix(".tmp")
with temporal.open("wb") as archivo:
marshal.dump(objeto, archivo)
temporal.replace(ruta)Esto reduce corrupción por errores de serialización, aunque la durabilidad ante pérdida de energía puede requerir flush y sincronización.
Leer desde archivo
load() lee un valor desde la posición actual.
with open("datos.marshal", "rb") as archivo:
objeto = marshal.load(archivo)Los bytes posteriores permanecen sin leer. Esto permite concatenar valores, pero la aplicación necesita contador o framing.
Varios valores consecutivos
with open("secuencia.marshal", "wb") as archivo:
marshal.dump({"id": 1}, archivo)
marshal.dump({"id": 2}, archivo)
with open("secuencia.marshal", "rb") as archivo:
primero = marshal.load(archivo)
segundo = marshal.load(archivo)Sin framing, puede ser difícil distinguir final válido de truncamiento.
Tipos soportados
Marshal maneja varios tipos simples, incluidos:
None, booleanos y números;- strings y bytes;
- tuplas, listas y diccionarios;
- sets y frozensets;
- estructuras adicionales según la versión;
- objetos de código cuando están permitidos.
Las instancias arbitrarias de clases no se soportan como en pickle.
Contenedores recursivos
Las versiones modernas pueden representar determinados contenedores recursivos.
lista = []
lista.append(lista)
datos = marshal.dumps(lista)
restaurada = marshal.loads(datos)
assert restaurada[0] is restauradaLas estructuras extremadamente profundas todavía pueden alcanzar límites. No proceses anidamiento sin control.
Versión del formato
El módulo expone marshal.version, versión predeterminada actual.
print(marshal.version)
datos = marshal.dumps(objeto, marshal.version)El argumento no garantiza compatibilidad completa entre intérpretes. Un formato reconocido puede contener un tipo cuya representación cambió.
Formato no equivale a versión de Python
El número de formato marshal es independiente de la release de Python. Los objetos de código pueden cambiar aunque el contenedor se reconozca.
Registra implementación, versión completa, plataforma y formato cuando importa la compatibilidad.
Objetos de código
Los code objects contienen bytecode, constantes, nombres y metadatos.
codigo = compile("resultado = 2 + 2", "<ejemplo>", "exec")
datos = marshal.dumps(codigo)
restaurado = marshal.loads(datos)Ejecutar el objeto restaurado sigue siendo ejecución de código.
ambiente = {}
exec(restaurado, ambiente)
print(ambiente["resultado"])Nunca ejecutes code objects de origen desconocido.
Bloquear code objects
Las versiones recientes ofrecen allow_code.
datos = marshal.dumps(
objeto,
allow_code=False,
)
restaurado = marshal.loads(
datos,
allow_code=False,
)Cuando es falso, se rechazan objetos de código. Esto elimina una categoría, pero no vuelve seguros bytes hostiles.
Compatibilidad de code objects
La documentación advierte que su formato no es compatible entre versiones. Cargar uno incorrecto tiene comportamiento indefinido.
No los guardes como caché duradero. Usa el mecanismo oficial de .pyc con tags y encabezados.
marshal y pyc
Aunque los .pyc usan marshal para el code object, también incluyen un encabezado gestionado por importlib. marshal.dumps(codigo) por sí solo no crea un pyc válido.
Usa py_compile o compileall.
marshal frente a pickle
Pickle soporta clases personalizadas y protocolos de reconstrucción, pero puede invocar callables. Marshal soporta menos tipos y sigue siendo inseguro para datos no confiables.
Ninguno es adecuado para uploads externos. Prefiere JSON, base de datos o formato con esquema.
marshal frente a JSON
JSON ofrece interoperabilidad, texto legible y modelo restringido. Marshal es específico de Python e interno.
Aunque pueda ser más pequeño, la falta de estabilidad suele costar más en persistencia.
Detectar truncamiento
Un archivo incompleto puede generar EOFError, ValueError u otro error.
try:
with open("datos.marshal", "rb") as archivo:
objeto = marshal.load(archivo)
except (EOFError, ValueError, TypeError) as error:
print("Archivo inválido:", error)No utilices un valor parcialmente leído.
Integridad externa
Los datos internos pueden acompañarse de hash o HMAC.
import hashlib
digest = hashlib.sha256(datos).hexdigest()Un hash detecta corrupción accidental, pero no un atacante que puede reemplazar ambos. Usa HMAC con clave secreta para autenticidad.
Límites de recursos
Datos malformados pueden intentar crear estructuras grandes o profundas. Limita tamaño antes de cargar y procesa entradas hostiles en subprocess con memoria y tiempo restringidos.
from pathlib import Path
ruta = Path("datos.marshal")
if ruta.stat().st_size > 10_000_000:
raise ValueError("archivo demasiado grande")La verificación de tamaño no sustituye aislamiento.
Eventos de auditoría
Las operaciones generan eventos de auditoría de Python. Los runtimes embebidos pueden observar cargas y escrituras.
No dependas solo de hooks para aceptar entrada peligrosa. La validación de origen debe ocurrir antes.
Uso en tests y herramientas
Marshal puede servir en tests del intérprete, estudios de formato y herramientas con artefactos efímeros de la misma versión.
Documenta que el archivo es descartable y se regenera al cambiar de runtime.
Test de round-trip
def test_round_trip():
original = {
"ids": [1, 2, 3],
"activo": True,
}
restaurado = marshal.loads(marshal.dumps(original))
assert restaurado == originalPrueba tipos soportados, vacío, truncamiento, versión y allow_code=False.
Errores frecuentes
- Usar marshal como base duradera.
- Cargar bytes de usuarios.
- Suponer compatibilidad entre versiones.
- Ejecutar code objects restaurados.
- Conservar archivo parcial tras
ValueError. - Confundir datos marshal con pyc completo.
- No limitar tamaño y profundidad.
- Tratar
allow_code=Falsecomo sandbox.
Buenas prácticas
- Usa marshal solo para necesidades internas y efímeras.
- Registra versiones de Python y formato.
- Desactiva code objects si no son necesarios.
- Escribe mediante temporal y reemplazo.
- Comprueba tamaño antes de cargar.
- Autentica artefactos internos cuando corresponda.
- Regenera datos al cambiar de runtime.
- Elige formatos estables para datos de aplicación.
Conclusión
El módulo marshal en Python ofrece serialización binaria rápida para un conjunto de tipos y estructuras internas. Ayuda a comprender parte de los cachés de bytecode y puede apoyar herramientas especializadas.
Su contrato es estrecho: el formato no es estable para persistencia general, los code objects no son portables y los datos desconocidos no son seguros. Úsalo solo en entornos controlados, con artefactos descartables, límites y compatibilidad claramente definida.






