Skip to main content

🗃️ ABL — Array-Based Lightweight Database

Motor de base de datos tabular embebido en C con API Python ultrarrápida.

PyPI Python License Downloads Build

Persistencia estructurada sin servidores. Un solo archivo .abl, una clave secreta, y listo.


🚀 Instalación en 10 segundos

pip install abl-db

Requisito: Python 3.7+ y un compilador C (GCC/Clang/MSVC). La extensión C se compila automáticamente durante la instalación.


✨ ¿Por qué ABL?

Característica ABL SQLite PostgreSQL JSON
Sin servidor ✅ ✅ ❌ ✅
Esquemas tabulares ✅ ✅ ✅ ❌
Ofuscación integrada ✅ ❌ ❌ ❌
Motor en C nativo ✅ ✅ ✅ ❌
Peso ligero ✅ ✅ ❌ ✅
Zero-config ✅ ✅ ❌ ✅

ABL llena el hueco entre JSON (sin estructura) y SQLite (overkill para proyectos pequeños). Ideal para scripts, herramientas CLI, microservicios y prototipos que necesitan persistencia tabular con un mínimo de fricción.


🎯 Uso en 30 segundos

from abl import ABLDatabase

# 1. Abre o crea la base de datos (un solo archivo .abl)
with ABLDatabase("datos.abl", secret="mi_clave") as db:

    # 2. Define una tabla con columnas
    productos = db.table("productos")
    productos.cols(["id", "nombre", "precio", "stock"])

    # 3. Inserta filas como diccionarios
    productos.set("001", {
        "id": "001",
        "nombre": "Laptop Pro",
        "precio": "1299.99",
        "stock": "42"
    })

    # 4. Recupera filas como diccionarios
    fila = productos.get("001")
    print(fila)
    # {'id': '001', 'nombre': 'Laptop Pro', 'precio': '1299.99', 'stock': '42'}

📖 Documentación completa

📦 Instalación

Desde PyPI (recomendado)

pip install abl-db

Desde el código fuente

git clone https://github.com/tu-usuario/abl-db.git
cd abl-db
pip install -e .

Verifica la instalación

from abl import ABLDatabase
print("✅ ABL instalado correctamente")

🏗️ Conceptos clave

ABL organiza los datos en tablas con filas identificadas por un row_id único. Cada fila es un diccionario donde las claves son las columnas definidas.

Base de datos (.abl)
├── Tabla: productos
│   ├── Esquema: ["id", "nombre", "precio", "stock"]
│   ├── Fila "001": {"id":"001", "nombre":"Laptop", ...}
│   └── Fila "002": {"id":"002", "nombre":"Monitor", ...}
│
└── Tabla: clientes
    ├── Esquema: ["id", "empresa", "email"]
    ├── Fila "C-01": {"id":"C-01", "empresa":"TechCorp", ...}
    └── Fila "C-02": {"id":"C-02", "empresa":"DevStudio", ...}

💡 Nota: El esquema de cada tabla se guarda automáticamente en la base de datos, así que no necesitas redefinirlo cada vez que abras el archivo.


🔧 API Reference

ABLDatabase(filename, secret)

Abre o crea una base de datos ABL.

Parámetro Tipo Default Descripción
filename str "database.abl" Ruta del archivo .abl
secret str "default_secret" Clave de ofuscación XOR (máx. 64 caracteres)
db = ABLDatabase("mi_db.abl", secret="supersecreto123")

Métodos:

Método Retorno Descripción
table(name) ABLTable Obtiene o crea una tabla por nombre
keys() List[str] Lista todas las claves indexadas (filas + esquemas)
close() None Cierra la BD y persiste los índices en disco

Context manager:

with ABLDatabase("datos.abl", secret="clave") as db:
    # ... operaciones ...
# Se cierra automáticamente al salir del bloque

ABLTable

Representa una tabla dentro de la base de datos.

tabla = db.table("nombre_tabla")

Métodos:

Método Parámetros Descripción
cols(columns) List[str] Define las columnas del esquema. Obligatorio antes de insertar.
set(row_id, data) str, Dict[str, Any] Inserta o actualiza una fila
get(row_id, buffer_size=4096) str, int Recupera una fila como diccionario
# Definir esquema
tabla.cols(["id", "nombre", "email"])

# Insertar / actualizar
tabla.set("USR-001", {
    "id": "USR-001",
    "nombre": "Ana López",
    "email": "ana@ejemplo.com"
})

# Recuperar
fila = tabla.get("USR-001")
# {'id': 'USR-001', 'nombre': 'Ana López', 'email': 'ana@ejemplo.com'}

Excepciones

Excepción Herencia Cuándo ocurre
ABLError Exception Error genérico del motor (fallo de E/S, sin memoria, etc.)
KeyNotFoundError ABLError, KeyError La fila o clave solicitada no existe
from abl import ABLDatabase, KeyNotFoundError

db = ABLDatabase("datos.abl", secret="clave")
tabla = db.table("usuarios")
tabla.cols(["id", "nombre"])

try:
    fila = tabla.get("ID_INEXISTENTE")
except KeyNotFoundError:
    print("❌ Esa fila no existe")

💡 Ejemplos prácticos

📝 Ejemplo 1: Inventario de productos

from abl import ABLDatabase

with ABLDatabase("inventario.abl", secret="inv2024") as db:
    productos = db.table("productos")
    productos.cols(["sku", "nombre", "categoria", "precio", "stock"])

    # Añadir productos
    productos.set("LAP-001", {
        "sku": "LAP-001",
        "nombre": "MacBook Air M3",
        "categoria": "Portátiles",
        "precio": "1199.00",
        "stock": "25"
    })

    productos.set("MON-002", {
        "sku": "MON-002",
        "nombre": "Dell UltraSharp 27\"",
        "categoria": "Monitores",
        "precio": "499.99",
        "stock": "12"
    })

    # Consultar
    laptop = productos.get("LAP-001")
    print(f"📦 {laptop['nombre']} — Stock: {laptop['stock']} unidades")

    # Listar todas las claves
    print("\n📋 Claves registradas:")
    for k in db.keys():
        print(f"   • {k}")

Salida:

📦 MacBook Air M3 — Stock: 25 unidades

📋 Claves registradas:
   • __schema__productos
   • productos:LAP-001
   • productos:MON-002

👥 Ejemplo 2: Gestión de empleados y clientes (múltiples tablas)

from abl import ABLDatabase

with ABLDatabase("empresa.abl", secret="empresaSecreta") as db:

    # Tabla de empleados
    empleados = db.table("empleados")
    empleados.cols(["dni", "nombre", "departamento", "salario", "activo"])

    empleados.set("72839102F", {
        "dni": "72839102F",
        "nombre": "María García",
        "departamento": "Ingeniería",
        "salario": "45000",
        "activo": "true"
    })

    empleados.set("83920184G", {
        "dni": "83920184G",
        "nombre": "Carlos Ruiz",
        "departamento": "Ventas",
        "salario": "38000",
        "activo": "true"
    })

    # Tabla de clientes
    clientes = db.table("clientes")
    clientes.cols(["id", "empresa", "contacto", "telefono", "pais"])

    clientes.set("C-9921", {
        "id": "C-9921",
        "empresa": "TechCorp SL",
        "contacto": "Juan Pérez",
        "telefono": "+34 600 123 456",
        "pais": "España"
    })

    # Consultas cruzadas
    emp = empleados.get("72839102F")
    cli = clientes.get("C-9921")

    print(f"👤 {emp['nombre']} ({emp['departamento']})")
    print(f"🏢 Cliente: {cli['empresa']} — {cli['pais']}")

🔐 Ejemplo 3: Configuración segura con ofuscación

from abl import ABLDatabase

# Los datos se ofuscan automáticamente con la clave secreta
with ABLDatabase("config.abl", secret="MiClaveSuperSecreta2024!") as db:
    config = db.table("settings")
    config.cols(["clave", "valor"])

    config.set("api_key", {"clave": "api_key", "valor": "sk-live-abc123xyz"})
    config.set("db_password", {"clave": "db_password", "valor": "p@ssw0rd!"})

    # Los datos están ofuscados en disco, pero legibles en memoria
    api = config.get("api_key")
    print(f"🔑 API Key: {api['valor']}")

⚠️ Nota de seguridad: La ofuscación XOR protege contra lectura casual del archivo, pero no es cifrado criptográfico robusto. Para datos altamente sensibles, considera cifrar el archivo a nivel de sistema operativo.


🔄 Ejemplo 4: Actualización de filas existentes

from abl import ABLDatabase

with ABLDatabase("stock.abl", secret="stock") as db:
    items = db.table("items")
    items.cols(["sku", "nombre", "cantidad"])

    # Insertar
    items.set("ITEM-01", {"sku": "ITEM-01", "nombre": "Teclado", "cantidad": "10"})

    # Actualizar (sobrescribe la fila con el mismo row_id)
    items.set("ITEM-01", {"sku": "ITEM-01", "nombre": "Teclado Mecánico", "cantidad": "5"})

    actualizado = items.get("ITEM-01")
    print(actualizado)
    # {'sku': 'ITEM-01', 'nombre': 'Teclado Mecánico', 'cantidad': '5'}

📋 Límites y especificaciones técnicas

Límite Valor Notas
Índices iniciales 1,000 Auto-escalable ×2 cuando se alcanza el límite
Longitud máx. de clave (row_id) 64 caracteres Incluye prefijo tabla:
Columnas por tabla 32 Definidas con cols()
Longitud máx. de celda 256 caracteres Por columna individual
Tamaño máx. de fila 4,096 bytes (default) Auto-escalable al doble si es necesario
Longitud máx. de clave secreta 64 caracteres Usada para la ofuscación XOR

🏗️ Arquitectura interna

┌─────────────────────────────────────────────┐
│              ABLDatabase (Python)           │
│         ┌─────────────────────┐             │
│         │   ABLTable (Python) │             │
│         │  ┌───────────────┐  │             │
│         │  │ abl.c (Motor C)│  │             │
│         │  │ ┌───────────┐ │  │             │
│         │  │ │ archivo   │ │  │             │
│         │  │ │  .abl     │ │  │             │
│         │  │ │ [Datos    │ │  │             │
│         │  │ │  XOR]     │ │  │             │
│         │  │ │ [Índices] │ │  │             │
│         │  │ └───────────┘ │  │             │
│         │  └───────────────┘  │             │
│         └─────────────────────┘             │
└─────────────────────────────────────────────┘

Formato del archivo .abl (binario):

┌──────────────────────────────────────────────────────────────┐
│  [Bloques de datos ofuscados]                                │
│  Cada bloque = fila CSV cifrada con XOR (clave secreta)      │
├──────────────────────────────────────────────────────────────┤
│  [Tabla de índices en memoria]                               │
│  Array de: { key[64 bytes], offset(uint64), length(uint32) } │
├──────────────────────────────────────────────────────────────┤
│  count   (uint32)  → Número total de registros               │
│  offset  (uint64)  → Posición del inicio de la tabla índices │
└──────────────────────────────────────────────────────────────┘
  • Persistencia: Los índices se guardan automáticamente al cerrar la base de datos (close() o salir del with).
  • Recuperación: Al abrir un archivo .abl existente, los índices se cargan desde el final del archivo.
  • Ofuscación: Todos los datos se cifran/descifran con XOR usando la clave secreta. Sin la clave correcta, los datos son ilegibles.

🛠️ Troubleshooting

Error: No se pudo cargar el motor C

ABLSError: No se pudo cargar el motor C ('_abl_engine.so')

Solución: La extensión C no está compilada. Instala desde el código fuente:

git clone https://github.com/tu-usuario/abl-db.git
cd abl-db
python setup.py build_ext --inplace
pip install -e .

Asegúrate de tener GCC instalado:


Error: Fila no encontrada (KeyNotFoundError)

KeyNotFoundError: Fila 'XYZ' no encontrada.

Causas comunes:

  1. El row_id no existe en la tabla.
  2. Intentas acceder a una fila de otra tabla (los IDs incluyen el prefijo tabla: internamente).
  3. La base de datos se abrió con una clave secreta diferente a la usada para escribir los datos.

Los datos aparecen como basura / caracteres extraños

Causa: La base de datos se abrió con una clave secreta diferente a la que se usó para escribir.

Solución: Asegúrate de usar exactamente la misma clave:

# Escritura
with ABLDatabase("datos.abl", secret="clave_correcta") as db:
    ...

# Lectura (debe ser la MISMA clave)
with ABLDatabase("datos.abl", secret="clave_correcta") as db:
    ...

📁 Estructura del paquete

abl/
├── __init__.py       # Exporta ABLDatabase, ABLError, KeyNotFoundError
├── core.py           # API Python + bindings ctypes al motor C
├── exceptions.py     # Jerarquía de excepciones
├── abl.c             # Motor principal en C (ofuscación, E/S, índices)
├── abl.h             # Headers y estructuras del motor
└── _abl_engine.so    # Extensión compilada (generada automáticamente)

setup.py              # Configuración de setuptools + Extension C
README.md             # Este archivo
LICENSE               # Licencia MIT

🤝 Contribuir

¡Las contribuciones son bienvenidas! Sigue estos pasos:

  1. Fork el repositorio
  2. Crea una rama: git checkout -b feature/nueva-funcionalidad
  3. Commitea tus cambios: git commit -am 'Añade nueva funcionalidad'
  4. Push: git push origin feature/nueva-funcionalidad
  5. Abre un Pull Request

Reportar bugs

Abre un issue en GitHub con:

  • Versión de Python (python --version)
  • Sistema operativo
  • Comando o código que reproduce el error
  • Traceback completo del error

ABL — Persistencia tabular sin fronteras. 🚀

⭐ Star en GitHub · 📦 PyPI · 🐛 Reportar bug

Metadata

Release files for abl-db 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for abl-db 0.1.1
File Size Uploaded
abl_db-0.1.1.tar.gz 14.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for abl-db 0.1.1
File Interpreter ABI Platform
abl_db-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 23.9 kB

Release files / abl_db-0.1.1.tar.gz

Download URL abl_db-0.1.1.tar.gz
Size 14.2 kB
Tags Source
SHA-256 checksum
How to use checksums
9d6dd17250df16516981251d8731a291115df4b2a7ba79689d9caa8dc93f54d8
BLAKE2b-256 checksum
How to use checksums
0ccd83f5ee80026baf178c7ff934a6b54bbd5cb04ce9b001e70e67bc8c24c377
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.4

Release files / abl_db-0.1.1-py3-none-any.whl

Download URL abl_db-0.1.1-py3-none-any.whl
Size 9.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
44296bd817635191635c73e0e4b59842ad5002f86ad7a8d3a187b689620a49ff
BLAKE2b-256 checksum
How to use checksums
6f7d6449b8bc3bb05ce77e09ca78433982e57ce34d2994d7eaf9153e0fb76d98
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.4

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page