El módulo mailbox manipula buzones de correo almacenados en disco. Soporta Maildir, mbox, MH, Babyl y MMDF y presenta una interfaz parecida a un diccionario, donde las claves identifican mensajes. Es útil para migraciones, copias de seguridad, filtros locales, índices, herramientas forenses e integraciones con clientes de correo.
El módulo no descarga mensajes por IMAP ni envía correo por SMTP. Trabaja con formatos locales. Combínalo con el paquete email para interpretar cabeceras, MIME, adjuntos y cuerpos.
Elegir el formato
Maildir almacena cada mensaje en un archivo separado dentro de tmp, new y cur. Este diseño tolera mejor procesos independientes y normalmente no requiere un lock global. mbox y MMDF guardan muchos mensajes en un solo archivo y exigen locking cuidadoso.
Para un sistema nuevo con escritores concurrentes, Maildir suele ser más seguro. Para interoperar con archivos existentes, conserva el formato y sigue sus reglas.
Abrir un Maildir
import mailbox
buzon = mailbox.Maildir("correo", create=True)
print(len(buzon))
for mensaje in buzon:
print(mensaje.get("Subject"))La iteración por defecto devuelve representaciones de mensajes, no claves. Cada acceso genera un objeto nuevo. Modificarlo en memoria no actualiza el buzón hasta asignarlo otra vez a la clave.
Iterar por claves
for clave in buzon.iterkeys():
mensaje = buzon.get_message(clave)
print(clave, mensaje.get("From"))Las claves solo tienen sentido para esa instancia y formato. Operaciones como MH.pack() pueden invalidarlas. No son identificadores globales permanentes.
Añadir un mensaje
from email.message import EmailMessage
mensaje = EmailMessage()
mensaje["From"] = "alice@example.com"
mensaje["To"] = "bob@example.com"
mensaje["Subject"] = "Informe"
mensaje.set_content("Contenido")
clave = buzon.add(mensaje)add() acepta mensajes mailbox, objetos del paquete email, strings, bytes y archivos binarios. El contenido se copia; el buzón no conserva una referencia viva.
Reemplazar un mensaje
mensaje = buzon.get_message(clave)
mensaje.replace_header("Subject", "Informe revisado")
buzon[clave] = mensajeMetadatos específicos, como flags, pueden conservarse o convertirse según la subclase. Revisa las reglas del formato.
Eliminar mensajes
buzon.remove(clave)
# del buzon[clave]
# buzon.discard(clave)remove() y del generan KeyError si falta la clave. discard() ignora el caso, útil cuando otro proceso puede eliminar mensajes.
Locking en formatos de archivo único
Adquiere el lock antes de leer y modificar mbox, MMDF y otros formatos que lo requieren.
buzon = mailbox.mbox("archivo.mbox")
buzon.lock()
try:
for clave, mensaje in buzon.iteritems():
if mensaje.get("Subject") == "Borrar":
buzon.discard(clave)
buzon.flush()
finally:
buzon.unlock()
buzon.close()Sin locking, dos procesos pueden perder cambios o corromper todo el archivo. Un conflicto puede generar ExternalClashError.
Concurrencia en Maildir
Maildir evita el lock global porque cada mensaje es un archivo. Aun así, múltiples threads que escriben en el mismo buzón pueden producir colisiones de nombres si la aplicación no las coordina.
Usa un lock de aplicación para writers locales y prueba en el sistema de archivos real, especialmente si es almacenamiento de red.
Flags de Maildir
Python 3.13 añadió métodos rápidos para consultar y cambiar flags sin abrir el mensaje completo.
flags = buzon.get_flags(clave)
buzon.add_flag(clave, "S")
buzon.remove_flag(clave, "F")
buzon.set_flags(clave, "RS")Un objeto MaildirMessage ya cargado no se sincroniza automáticamente con cambios hechos a nivel del buzón. Recárgalo.
Información y carpetas Maildir
get_info() y set_info(), también desde Python 3.13, acceden a la sección info del nombre del archivo.
print(buzon.list_folders())
archivados = buzon.add_folder("Archivados.2026")
subbuzon = buzon.get_folder("Archivados.2026")El estilo Courier utiliza puntos para representar niveles lógicos.
Limpiar entregas temporales
Maildir.clean() elimina archivos antiguos de tmp según la convención del formato. Úsalo con cuidado en almacenamiento inestable.
Leer bytes, texto o archivo
datos = buzon.get_bytes(clave)
texto = buzon.get_string(clave)
with buzon.get_file(clave) as archivo:
primera = archivo.readline()get_bytes() es mejor para procesamiento fiel. get_string() produce una representación limpia de siete bits. Usa un parser binario con política explícita.
Factory personalizada
from email import policy
from email.parser import BytesParser
def fabrica(archivo):
return BytesParser(policy=policy.default).parse(archivo)
buzon = mailbox.Maildir("correo", factory=fabrica)Una factory puede devolver objetos modernos del paquete email o solo metadatos para reducir memoria.
mbox y la línea From
mbox separa mensajes con líneas que comienzan por From . Las apariciones en el cuerpo se escapan al guardar. Existen variantes incompatibles, por lo que las migraciones deben probarse.
Los métodos de mbox aceptan from_ para incluir o quitar la línea Unix From.
MH y secuencias
MH guarda un mensaje por archivo y admite secuencias nombradas.
buzon = mailbox.MH("mh")
secuencias = buzon.get_sequences()
secuencias["importantes"] = ["1", "3"]
buzon.set_sequences(secuencias)pack() renumera mensajes y deja inválidas las claves anteriores.
Migrar entre formatos
origen = mailbox.mbox("origen.mbox")
destino = mailbox.Maildir("destino", create=True)
origen.lock()
try:
for mensaje in origen:
destino.add(mensaje)
finally:
origen.unlock()
origen.close()
destino.close()Antes de migrar, copia el origen, cuenta mensajes, registra hashes y define cómo convertir flags. Valida adjuntos y abre el resultado con otro cliente.
Modificar durante la iteración
Los mensajes añadidos después de crear el iterador no aparecen. Los eliminados antes de ser alcanzados se omiten. Procesos concurrentes aún pueden invalidar claves.
Contenido no confiable
El correo puede incluir cabeceras malformadas, MIME profundo, adjuntos enormes, nombres peligrosos y HTML activo. No ejecutes adjuntos ni renderices HTML sin sanitizar. Limita tamaño, partes, profundidad y descompresión.
Backups y flush
Antes de cambios destructivos en mbox, crea una copia y verifica espacio. flush() escribe cambios pendientes, pero no sustituye un plan de recuperación.
Maildir aísla mejor las operaciones, aunque migraciones grandes necesitan checkpoints y capacidad de reanudación.
Exportar metadatos
import json
import mailbox
buzon = mailbox.Maildir("correo")
registros = []
for clave in buzon.iterkeys():
msg = buzon.get_message(clave)
registros.append({
"key": str(clave),
"from": msg.get("From"),
"to": msg.get("To"),
"subject": msg.get("Subject"),
"date": msg.get("Date"),
"flags": msg.get_flags(),
})
with open("indice.json", "w", encoding="utf-8") as f:
json.dump(registros, f, ensure_ascii=False, indent=2)Las cabeceras no son confiables, pueden repetirse y contener fechas inválidas.
Errores frecuentes
- Modificar mbox sin lock.
- Esperar que editar Message actualice el buzón.
- Usar claves como IDs permanentes.
- Cerrar el buzón mientras se usa un archivo devuelto.
- Confundir almacenamiento local con IMAP.
- Ignorar variantes mbox.
- Procesar adjuntos sin límites.
Buenas prácticas
- Prefiere Maildir para escritura concurrente.
- Bloquea formatos que lo requieren.
- Llama
flush()yclose(). - Haz backup antes de cambios masivos.
- Parsea bytes con política explícita.
- Valida conteos, flags y hashes tras migrar.
- Limita todo contenido no confiable.
Guías relacionadas
Continúa con quopri en Python, mimetypes en Python, fileinput en Python, filecmp en Python y ExitStack en Python.
Consulta la documentación oficial de mailbox y la documentación del paquete email.
Conclusión
mailbox ofrece una interfaz uniforme sobre formatos locales muy distintos. Su uso fiable depende de entender locking, copia de mensajes, duración de claves, diferencias de formato y riesgos del contenido. Para nuevos almacenes concurrentes, Maildir suele ser la base más segura.







