Fast binary serializer for Python with optional C acceleration.
Project description
🥒 compickle
Serialización binaria para Python con motor en C — rápido, compacto y sin dependencias.
¿Qué es compickle?
compickle es un serializador binario escrito principalmente en C y expuesto como extensión nativa de Python. Está diseñado para ser muy simple de usar y más rápido que el módulo estándar pickle en la mayoría de cargas de trabajo, gracias a:
- Un buffer de salida dinámico que crece en potencias de 2.
- Una tabla de deduplicación con hashing FNV-1a O(1) amortizado (capacidad de hasta 8 192 entradas).
- Compilación nativa con
-O3 -march=native -mtune=native. - Un fallback puro en Python que se activa automáticamente si la extensión C no está disponible.
⚙️ Instalación
pip install .
O para desarrollo (compilación en el directorio actual):
python setup.py build_ext --inplace
El compilador usa
-O3 -march=native -mtune=nativeautomáticamente, generando código optimizado para tu arquitectura específica.
🚀 Uso rápido
import compickle
# Serializar cualquier objeto a archivo
datos = {
"nombre": "Rex",
"edad": 5,
"activo": True,
"coordenadas": (4.0, 2.0),
"etiquetas": {"perro", "mascota"},
}
compickle.dump(datos, "datos.cpkl")
# Deserializar desde archivo
copia = compickle.load("datos.cpkl")
# Verificar qué motor está activo
print(compickle.backend()) # → 'c' o 'python'
📖 API completa
compickle.dump(obj, path)
Serializa obj y escribe el resultado binario en path.
compickle.dump(mi_objeto, "salida.cpkl")
- Usa el motor C si está disponible; Python puro en caso contrario.
- Resetea automáticamente la tabla de deduplicación antes de cada serialización.
compickle.load(path)
Lee el archivo binario en path y reconstruye el objeto original.
obj = compickle.load("salida.cpkl")
compickle.dedup_reset()
Resetea manualmente la tabla interna de deduplicación (tanto la del motor C como la del fallback Python).
compickle.dedup_reset()
Útil si reutilizas el proceso para serializar muchos objetos distintos en sesiones largas.
compickle.backend()
Devuelve el motor activo.
compickle.backend() # → 'c' (extensión compilada)
# → 'python' (fallback puro Python)
🧩 Tipos soportados
| Tipo Python | Deduplicado | Notas |
|---|---|---|
None |
— | 1 byte: 0x00 |
bool |
— | 1 byte: 0x80 / 0x81 |
int |
— | Enteros 0–59 en 1 byte; arbitrario big-endian |
float |
— | IEEE 754 doble precisión (8 bytes) |
complex |
— | Dos float64 big-endian (16 bytes) |
str |
✅ | UTF-8 + FNV-1a hash |
bytes |
✅ | Deduplicado por contenido |
bytearray |
✅ | Deduplicado por contenido |
list |
— | Elementos serializados recursivamente |
tuple |
— | Elementos serializados recursivamente |
set |
— | Ordenado por repr() para reproducibilidad |
frozenset |
— | Ordenado por repr() para reproducibilidad |
dict |
— | Claves y valores serializados recursivamente |
function |
✅ | Nombre + código fuente (via inspect.getsource) |
closure |
✅ | Fuente + variables capturadas del closure |
class (tipo) |
✅ | Nombre + módulo + código fuente |
Instancia (__dict__) |
✅ | Clase + fuente + estado del __dict__ |
Instancia (__slots__) |
✅ | Recorre MRO completo para capturar todos los slots |
Objeto con __reduce__ |
✅ | Prioridad máxima; soporta estado opcional |
🔬 Cómo funciona internamente
Motor C (compickle.c)
El corazón de compickle es una extensión CPython que implementa:
Buffer dinámico (Buf)
buf_grow → realloc en potencias de 2 (inicio: 8 192 bytes)
buf_u8 → escribe 1 byte
buf_raw → escribe N bytes con memcpy
Tabla de deduplicación (FNV-1a)
Capacidad: 8 192 entradas (DEDUP_CAP)
Buckets: 16 384 (HASH_BUCKETS)
Colisiones: lista enlazada (campo .next)
Cuando un bloque de bytes se ha visto antes, se emite una referencia compacta (2–5 bytes) en lugar de repetir los datos. Los strings largos, bloques de código fuente y datos binarios se benefician enormemente de esto.
Codificación de longitudes (variable)
n ≤ 0x3F → 1 byte
n ≤ 0x3FFF → 2 bytes (bit 0x40 marcador)
n > 0x3FFF → 5 bytes (0xFF + uint32 big-endian)
Prioridad de serialización de objetos
1. __reduce__ explícito en la clase → tag 0x1F
2. __dict__ disponible → tag 0x1C
3. __slots__ sin __dict__ → tag 0x1E (recorre MRO completo)
Fallback Python (compickle.py)
Si la extensión C no compila, compickle.py implementa exactamente el mismo protocolo binario en Python puro, con las mismas tags y el mismo esquema de deduplicación. El API público es idéntico; solo cambia la velocidad.
Carga diferida (__init__.py)
El módulo público usa lazy loading: la extensión C solo se importa en la primera llamada a dump, load, dedup_reset o backend(). Esto evita errores en import si la extensión no está compilada todavía.
compickle.backend() # ← importa la extensión C aquí, la primera vez
🏗️ Estructura del proyecto
compickle/
├── compickle.c # Motor principal en C (extensión CPython)
├── compickle.py # Implementación de referencia en Python puro
├── __init__.py # API pública con lazy loading
└── setup.py # Build con -O3 -march=native -mtune=native
🧪 Ejemplo avanzado: clases, slots y reduce
import compickle
# ── Clase con __dict__ ──────────────────────────────────────────────
class Punto:
def __init__(self, x, y):
self.x = x
self.y = y
p = Punto(3.0, 7.5)
compickle.dump(p, "punto.cpkl")
p2 = compickle.load("punto.cpkl")
print(p2.x, p2.y) # → 3.0 7.5
# ── Clase con __slots__ ─────────────────────────────────────────────
class Vector:
__slots__ = ("x", "y", "z")
def __init__(self, x, y, z):
self.x, self.y, self.z = x, y, z
v = Vector(1, 2, 3)
compickle.dump(v, "vector.cpkl")
v2 = compickle.load("vector.cpkl")
print(v2.x, v2.y, v2.z) # → 1 2 3
# ── Objeto con __reduce__ ───────────────────────────────────────────
class Color:
def __init__(self, r, g, b):
self.r, self.g, self.b = r, g, b
def __reduce__(self):
return (Color, (self.r, self.g, self.b))
c = Color(255, 128, 0)
compickle.dump(c, "color.cpkl")
c2 = compickle.load("color.cpkl")
print(c2.r, c2.g, c2.b) # → 255 128 0
📦 Formato binario (resumen de tags)
0x00 None
0x02 int (arbitrario)
0x03 float (8 bytes)
0x04 complex (16 bytes)
0x05 str (UTF-8, deduplicado)
0x06 bytes (deduplicado)
0x07 bytearray (deduplicado)
0x08 list
0x09 tuple
0x0A set
0x0B frozenset
0x0C dict
0x0D function (nombre + fuente)
0x12 class (nombre + módulo + fuente)
0x1C instancia con __dict__
0x1E instancia con __slots__
0x1F instancia vía __reduce__
0x80/0x81 False / True
0xC0–0xFB int pequeño (0–59, 1 byte)
0xFB bloque nuevo para dedup
0xFC–0xFE referencia dedup (4 / 2 / 1 byte de índice)
📄 Licencia
MIT — úsalo como quieras.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file compickle-1.0.1.tar.gz.
File metadata
- Download URL: compickle-1.0.1.tar.gz
- Upload date:
- Size: 18.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: python-requests/2.33.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
394ca7d341c57a22f2d3d3006bc7badbaa9cd095ccd2d39e1d198010276fe3d4
|
|
| MD5 |
502011731b93d51e513183f95d2e06a5
|
|
| BLAKE2b-256 |
12d372c1f84ec66b34aab0b520372c7babde2ffa828f13a10cbfbb65b592189f
|
File details
Details for the file compickle-1.0.1-cp313-cp313-android_24_arm64_v8a.whl.
File metadata
- Download URL: compickle-1.0.1-cp313-cp313-android_24_arm64_v8a.whl
- Upload date:
- Size: 49.0 kB
- Tags: Android API level 24+ ARM64 v8a, CPython 3.13
- Uploaded using Trusted Publishing? No
- Uploaded via: python-requests/2.33.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ad1586d551ab534b5bf53d63fa3c68b5bc0082f460e2c414f97d25dfd21391ae
|
|
| MD5 |
3354239c1e591387faf7aee0f23a10b0
|
|
| BLAKE2b-256 |
1b67b4a9d1287fc694e224f31467b2cb86a04c444da6c5c4c1084c3977ef6a3c
|