A lightweight, async-ready ORM and key-value store for SQLite
Project description
Obele
Obele is a lightweight SQLite ORM and key-value store for modern Python applications. It keeps a small, Django-like API for models and queries while supporting both synchronous code and native asyncio workflows.
Highlights
- Sync and async APIs for models, queries, raw SQL, search, and KV operations.
Every method has an
a-prefixed async twin that runs on a worker thread, so transactions and scoped bindings behave identically in both worlds. - Thread-safe SQLite connection management: per-thread connections under WAL
mode, transactions with savepoint nesting, scoped database bindings, and
query logging. Plain
":memory:"databases are shared across threads. - Declarative models with field validation, foreign keys, reverse relations,
expressions (
F("views") + 1), lazy slicing, prefetching, mixins, signals, pagination, and FTS5 search indexes. - Persistent dict-like
KVStorewith namespaces, TTL, atomic operations, range/prefix/scan queries, memoization, serializers, and async methods.
Requirements
- Python 3.13 or newer.
- SQLite with FTS5 enabled for full-text search features.
Obele has no required runtime dependencies outside the Python standard library.
Installation
pip install obele
For local development:
git clone https://github.com/ichinga-samuel/obele.git
cd obele
python -m pip install -e ".[dev]"
pytest -q
Quickstart
import asyncio
from obele import Database, Model, TextField, IntegerField
Database.configure("app.sqlite3")
class User(Model):
table_name = "users"
name = TextField()
age = IntegerField(nullable=True)
async def main():
await User.acreate_table()
await User.acreate(name="Alice", age=30)
await User.acreate(name="Bob", age=25)
users = await User.filter(age__gte=18).order_by("name").aall()
for user in users:
print(user.name, user.age)
await Database.aclose_all()
asyncio.run(main())
The same API is available synchronously by omitting the a prefix:
User.create_table()
User.create(name="Ada", age=36)
adults = User.filter(age__gte=18).all()
Direct SQL
Use Database helpers when you need lower-level access:
cursor = Database.execute(
"INSERT INTO users (name, age) VALUES (?, ?)",
["Grace", 40],
)
print(cursor.lastrowid)
row = Database.fetchone("SELECT name FROM users WHERE id = ?", [cursor.lastrowid])
Async helpers return fully materialized results - no cursors to manage:
result = await Database.aexecute(
"INSERT INTO users (name, age) VALUES (?, ?)",
["Linus", 33],
)
print(result.lastrowid)
row = await Database.afetchone("SELECT name FROM users WHERE age > ?", [30])
Database.aexecute() and Database.aexecutemany() return an ExecResult
containing rows, rowcount, and lastrowid; there is no async cursor to
close. The former obele.asqlite and raw async-connection exports are no
longer part of the public API.
Transactions And Scoped Databases
with Database.transaction(mode="IMMEDIATE"):
User.create(name="One")
User.create(name="Two")
async with Database.transaction():
await User.acreate(name="Async One")
await User.acreate(name="Async Two")
Transaction modes are DEFERRED, IMMEDIATE (the default), and EXCLUSIVE.
Nested transactions use savepoints.
Use scoped bindings for tests, tenants, or short-lived alternate databases:
with Database.using("tenant.sqlite3"):
User.create_table()
User.create(name="Tenant User")
Key-Value Store
from obele import KVStore
settings = KVStore("settings", key_type=str, namespace="app")
settings["theme"] = "dark"
settings.set("session", {"user_id": 1}, ttl=3600)
print(settings["theme"])
print(settings.prefix("sess"))
Async KV methods are prefixed with a:
await settings.aset("feature:search", True)
enabled = await settings.aget("feature:search")
Documentation
- User Guide
- Architecture and Execution Model
- Migration CLI
- API Reference
- Queries
- Fields
- Key-Value Store
To preview the docs locally:
mkdocs serve
Testing
The test suite is organized by public behavior and uses the local checkout by
default via pyproject.toml.
pytest -q
Current suite coverage includes ORM CRUD, queries, async behavior, direct database access, FTS search, pagination, signals, field types, and KVStore features.
License
Obele is released under the MIT License. See LICENSE.
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 obele-1.0.1.tar.gz.
File metadata
- Download URL: obele-1.0.1.tar.gz
- Upload date:
- Size: 88.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
365efa501cd1c053e348df35a53f39d53c28e814ff56b9062430fc106af9a134
|
|
| MD5 |
9d70636b362e0f2965432d1a647a87f1
|
|
| BLAKE2b-256 |
d816d64a8a53eb08b4487d568eff073eb093c81905610445ef895b51810fa49b
|
File details
Details for the file obele-1.0.1-py3-none-any.whl.
File metadata
- Download URL: obele-1.0.1-py3-none-any.whl
- Upload date:
- Size: 65.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3e07185c554cbf7f49a6649e34799e193a0b5a86efda988036a48b9b652f5605
|
|
| MD5 |
bfefad91cee529fba547767e03f081ec
|
|
| BLAKE2b-256 |
084a05fd7eaafa80067f7907792e00761c0f6ca7b558c2d1de9b65aa3cfe2dc6
|