Skip to main content

Local-first database for Python — documents, cache, queue, FTS, vectors over one SQLite file. The Python port of monlite; reads/writes the same .db as the TypeScript packages.

Project description

monlite (Python)

The local-first database for Python — documents, cache, durable queue, cron, and full-text search over one SQLite file, with a pure-standard-library core. No dependencies required.

monlite is the Python port of monlite. It reads and writes the same .db file as the TypeScript @monlite/* packages — same documents, change feed, _kv/sorted-set tables, _jobs queue, _schedules, and FTS5 index — so Python ingests or embeds while Node serves (or any split you like). Every table layout is byte-compatible and covered by an interop suite that round-trips a file between the two runtimes.

pip install monlite

Quick start

from monlite import create_db, kv

db = create_db("app.db")
users = db.collection("users")

users.create({"name": "Ali", "age": 30, "tags": ["admin"]})
users.create_many([{"name": "Sara", "age": 25}, {"name": "Omar", "age": 40}])

adults = users.find_many(where={"age": {"gte": 18}}, order_by={"age": "asc"})
ali = users.find_first(where={"name": "Ali"})
users.update({"_id": ali["_id"]}, {"$inc": {"age": 1}, "$push": {"tags": "vip"}})
count = users.count(where={"role": "admin"})

cache = kv(db)
cache.set("session:42", {"user": "ali"}, ttl=60_000)

Query operators

Mongo/Prisma-style, mirroring the TypeScript API (snake_case method names):

where={"age": {"gte": 18, "lt": 65}}
where={"role": {"in": ["admin", "editor"]}, "name": {"not": "root"}}
where={"name": {"contains": "ali", "mode": "insensitive"}}
where={"sku": {"startsWith": "AB", "endsWith": "-1"}}
where={"email": {"regex": r"@example\.com$"}}
where={"tags": {"has": "admin"}}                       # array contains
where={"items": {"elemMatch": {"qty": {"gte": 5}}}}     # any array element matches
where={"OR": [{"role": "admin"}, {"age": {"gte": 40}}]}

Update operators: $set, $unset, $inc, $push, $pull, $addToSet. Plus aggregate(_count=True, _sum=["amt"], _avg=["amt"], _min=[...], _max=[...]) and group_by("category", _count=True, _sum=["amt"]).

Transactions

with db.transaction():           # nestable via SAVEPOINTs; rolls back on exception
    orders.create({...})
    inventory.update({"_id": sku}, {"$inc": {"stock": -1}})

Change feed — see what Node wrote (and vice-versa)

Opt in with changefeed=True; writes append to _monlite_changes in the exact format the TS side reads (upsert/delete ops, the <padded-ms>:<nodeId>:<padded-seq> version string):

db = create_db("shared.db", changefeed=True)
last = 0
while True:
    for ch in db.changes(since=last):     # ordered; includes Node's writes
        handle(ch["coll"], ch["doc_id"], ch["op"])
        last = ch["seq"]
    time.sleep(0.2)

kv — cache, locks, pub/sub, sorted sets (Redis's local role)

c = kv(db)
c.set("k", {"v": 1}, ttl=60_000); c.incr("hits")
c.set_nx("lock:job", 1, ttl=5_000)          # atomic set-if-absent
with c.with_lock("report", ttl_ms=10_000):  # ergonomic distributed lock
    build_report()

c.subscribe("jobs", lambda m: print("got", m))
c.publish("jobs", {"id": 7})                 # same-process now; c.poll() drains other writers

c.zadd("board", 100, "ada"); c.zincrby("board", 5, "bo")
c.zrange("board", 0, -1, rev=True)           # leaderboard; zrank/zscore/zrange_by_score too

Durable queue

from monlite import create_queue

q = create_queue(db)
q.add("email", {"to": "a@b.c"}, priority=5, max_attempts=3)   # retries + backoff + dedupe (job_id)
q.process("email", lambda payload: send(payload))             # claim+run loop (multi-process safe)

A Node @monlite/queue worker and this one share _jobs — enqueue in Python, process in Node, or the reverse.

Cron

from monlite import create_cron, next_cron_run

cron = create_cron(db)
cron.schedule("digest", "0 9 * * *", send_digest, tz="Europe/Istanbul", jitter=30_000)
cron.tick()   # fire due schedules (call on your own loop; multi-process safe via atomic claim)
next_cron_run("*/15 * * * *")  # -> datetime

Full-text search (FTS5)

from monlite import fts

idx = fts(db, "posts", fields=["title", "body"])  # builds the FTS5 index + keeps it current
posts = db.collection("posts")
posts.create({"title": "SQLite is great", "body": "embedded and fast"})
idx.search("sqlite", limit=10)                    # ranked documents (interop-compatible with Node)

Cross-language interop

Each runtime is a complete, independent library; the shared-file interop is a bonus you opt into. The interop test suite round-trips documents, the change feed, sorted sets, and the queue between Node and Python over one file. See the file format spec for the conventions both sides follow.

Optional extras

The whole stack above is pure standard-library sqlite3. Native extras mirror the TypeScript packages and install separately:

pip install "monlite[vector]"    # semantic search via sqlite-vec
pip install "monlite[postgres]"  # sync to PostgreSQL
pip install "monlite[mongo]"     # sync to MongoDB

Status

Documents (with transactions, aggregation, and the change feed), kv (cache, locks, pub/sub, sorted sets), the durable queue, cron, and FTS5 are implemented and covered by tests — including a cross-runtime interop suite. The [vector] extra and sync adapters are next. The file format is the contract every part shares.

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

monlite-0.1.0.tar.gz (44.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

monlite-0.1.0-py3-none-any.whl (33.0 kB view details)

Uploaded Python 3

File details

Details for the file monlite-0.1.0.tar.gz.

File metadata

  • Download URL: monlite-0.1.0.tar.gz
  • Upload date:
  • Size: 44.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.6

File hashes

Hashes for monlite-0.1.0.tar.gz
Algorithm Hash digest
SHA256 e1e5fa47180ea80c2d465afe6933a4330eea163a25b919aabc5f7be1f3e91b94
MD5 cbc40801c0bc486d7f69da58f1cd101b
BLAKE2b-256 49e81d25309c7d9264d58cc6375e4d7bfa13632cb4a1838f966554aeeb63ced5

See more details on using hashes here.

File details

Details for the file monlite-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: monlite-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 33.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.6

File hashes

Hashes for monlite-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f3f9b81938d6e406bb32abbe48be690af1c6eb890d1202d9d3e25f092562f36d
MD5 7744893e8c0b3dce48e1004d9ac1aadf
BLAKE2b-256 0c904772e975a963394a030800f669d6ac432632b6999115493efae6b2103777

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page