El módulo lzma en Python ofrece compresión y descompresión con LZMA y LZMA2. Admite archivos modernos .xz, el formato antiguo .lzma y streams RAW configurados con filtros explícitos. Incluye funciones de una sola operación, objetos incrementales y una interfaz de archivo parecida a bz2.
LZMA suele producir archivos menores que gzip o bzip2, pero necesita más CPU y puede consumir mucha memoria. El preset 9 puede llevar al compresor a usar cientos de megabytes. El preset predeterminado 6 es un punto de partida más seguro y la configuración final debe basarse en pruebas con datos representativos.
Cuándo conviene lzma
Usa lzma cuando necesites compatibilidad XZ, cuando el tamaño final importe más que la velocidad máxima o cuando generes datasets de archivo que no se abran continuamente. Para otra alternativa eficaz en texto, consulta bz2 en Python.
Comprime datos en memoria
import lzma
datos = ("registro de auditoría\n" * 2000).encode("utf-8")
comprimido = lzma.compress(datos, preset=6)
restaurado = lzma.decompress(comprimido)
assert restaurado == datos
print(len(datos), len(comprimido))lzma.compress() crea XZ por defecto. Es cómodo para cargas pequeñas, pero entrada y salida permanecen en memoria. Para grandes volúmenes, utiliza archivos o procesamiento incremental.
Escribe un archivo XZ de texto
import lzma
with lzma.open("eventos.log.xz", "wt", encoding="utf-8", preset=6) as archivo:
archivo.write("inicio del proceso\n")
archivo.write("proceso completado\n")Los modos siguen la convención de los archivos normales: rt, wt, xt y at para texto, con equivalentes binarios. Usa creación exclusiva cuando una sobrescritura accidental deba fallar.
Lee sin cargar todo
import lzma
with lzma.open("eventos.log.xz", "rt", encoding="utf-8") as archivo:
for linea in archivo:
procesar(linea.rstrip())La iteración entrega una línea cada vez y combina bien con la guía para leer archivos gigantes con Python. El descompresor mantiene un diccionario interno, por lo que conviene observar la memoria en servicios prolongados.
Usa LZMAFile para binario
LZMAFile acepta una ruta o un archivo ya abierto e implementa gran parte de io.BufferedIOBase. Admite with, iteración, lectura, escritura y búsqueda cuando el recurso lo permite.
from lzma import LZMAFile
with LZMAFile("datos.bin.xz", "wb", preset=5) as destino:
destino.write(b"cabecera\x00")
destino.write(b"datos" * 10_000)Una misma instancia no es segura para lectores o escritores concurrentes. Usa un lock o instancias separadas.
Selecciona el formato
FORMAT_XZ: contenedor moderno con checks y filtros.FORMAT_ALONE: formato legado.lzmacon menos funciones.FORMAT_RAW: sin contenedor; ambos lados necesitan la cadena exacta.FORMAT_AUTO: detecta XZ o LZMA al descomprimir.
Elige XZ para proyectos nuevos. RAW solo es razonable cuando un protocolo ya define todos los parámetros.
Checks de integridad
XZ puede usar CRC32, CRC64 o SHA-256. CRC64 es el valor predeterminado. Comprueba lzma.is_check_supported() antes de pedir una opción que la liblzma instalada podría no tener.
import lzma
check = lzma.CHECK_SHA256
if not lzma.is_check_supported(check):
check = lzma.CHECK_CRC64
resultado = lzma.compress(b"contenido", check=check)Los checks detectan corrupción accidental. No autentican al remitente y no reemplazan una firma o un hash autenticado.
Compresión incremental
import lzma
compresor = lzma.LZMACompressor(preset=6)
partes = []
for bloque in generar_bloques():
parte = compresor.compress(bloque)
if parte:
partes.append(parte)
partes.append(compresor.flush())
comprimido = b"".join(partes)El objeto puede acumular entrada, por lo que una llamada puede devolver bytes vacíos. flush() termina el stream e impide seguir usando el compresor.
Limita la memoria del descompresor
LZMADecompressor acepta memlimit. Si el stream necesita más memoria, lanza LZMAError. Es una protección importante para entradas externas.
import lzma
dec = lzma.LZMADecompressor(memlimit=128 * 1024 * 1024)
salida = bytearray()
limite_salida = 50 * 1024 * 1024
for bloque in recibir_bloques():
pendiente = bloque
while pendiente or not dec.needs_input:
parte = dec.decompress(pendiente, max_length=64 * 1024)
pendiente = b""
salida.extend(parte)
if len(salida) > limite_salida:
raise ValueError("la salida excedió el límite")
if dec.eof:
breakmemlimit controla el espacio de trabajo, no el tamaño final. Combínalo con max_length y un contador total.
Streams concatenados
lzma.decompress() y LZMAFile procesan miembros concatenados. Una sola instancia de LZMADecompressor no. Al llegar a eof, inspecciona unused_data y crea otro descompresor si se permiten más miembros.
Cadenas de filtros
Una cadena admite hasta cuatro filtros y debe terminar con LZMA1 o LZMA2. Delta puede mejorar datos numéricos con cambios regulares; BCJ está pensado para código de máquina.
import lzma
filtros = [
{"id": lzma.FILTER_DELTA, "dist": 4},
{"id": lzma.FILTER_LZMA2, "preset": 6},
]
comprimido = lzma.compress(datos, format=lzma.FORMAT_RAW, filters=filtros)Los filtros personalizados reducen la interoperabilidad. Registra la configuración exacta y prueba todas las implementaciones que deban leerla.
Evita presets extremos
PRESET_EXTREME puede combinarse con niveles de 0 a 9, pero suele aumentar mucho el tiempo por una mejora modesta. No uses 9 extremo dentro de una petición web sin benchmark, límites de memoria y una cola de trabajos controlada.
Seguridad operativa
- Limita el tamaño comprimido.
- Usa
memlimity un límite de salida. - Comprueba
eofpara detectar truncamiento. - No confíes en extensiones ni nombres suministrados.
- Almacena en un directorio aislado.
- Limita el tiempo de CPU.
- No compartas un descompresor entre hilos.
Si el XZ está dentro de un archivo con rutas, aplica las validaciones del artículo sobre archivos ZIP con Python.
Mide datos reales
Compara tiempo, memoria y tamaño para texto, binarios, multimedia ya comprimida y archivos vacíos. Incluye entradas corruptas, truncadas, múltiples miembros y checks distintos. Prueba presets 3, 6 y 9.
Buenas prácticas
- Prefiere
FORMAT_XZ. - Mantén preset 6 hasta medir.
- Especifica codificación en texto.
- Usa context managers.
- Procesa grandes datos por bloques.
- Finaliza con
flush(). - Combina límites de memoria y salida.
- Maneja
LZMAErrorsin exponer información sensible.
Conclusión
lzma en Python proporciona compresión XZ eficiente, archivos de texto y binarios, checks y filtros avanzados. Su ventaja principal es el tamaño; sus costes son CPU, memoria y complejidad operativa.
Consulta la documentación oficial de lzma y el proyecto XZ Utils. Para configurar presets y límites, revisa configparser en Python, y para observar memoria usa tracemalloc en Python.







