Redis-like commands backed by SQLite. No server to run.
Project description
babyredis
🧱 Redis-like commands, SQLite underneath, no server to run.
[!NOTE] In progress. Open source contributions welcome.
babyredis is a redis-py-shaped client
backed by a single SQLite file (WAL mode). Think
redka, but pure Python, in-process, and
pip-installable with zero dependencies — sqlite3 ships with Python.
from babyredis import Redis
r = Redis("cache.db") # or Redis(":memory:") for ephemeral
r.set("session:42", "ciara", ex=3600)
r.get("session:42") # b'ciara'
r.incr("page:views") # 1
r.ttl("session:42") # 3600
Why?
- No server. No daemon to install, configure, monitor, or pay for. Your cache is a file next to your app — perfect for single-server deployments, CLIs, scripts, and side projects.
- Survives restarts. Unlike a dict (or fakeredis), data persists and can be shared across processes on the same machine.
- Familiar API. Method signatures mirror redis-py (
setwithex/px/nx/xx/keepttl/get,ttlreturning -2/-1, bytes responses with adecode_responsesflag), so swapping a small app off Redis — or onto it later — is mostly an import change.
Honest trade-offs: local SQLite reads are fast (no TCP round-trip), but Redis will beat this on write throughput and tail latency, and there's no cross-machine networking. If you need pub/sub, clustering, or six-figure ops/sec, run real Redis. This is for everyone who doesn't.
Supported commands (v0.3)
Strings: set, get, getset, getdel, setex, psetex, setnx,
append, strlen, mset, mget, incr/incrby, decr/decrby,
incrbyfloat.
Hashes: hset (with mapping=), hget, hgetall, hdel, hexists,
hkeys, hvals, hlen, hmget, hsetnx, hincrby, hincrbyfloat,
hstrlen, hscan/hscan_iter.
Sets: sadd, srem, smembers, sismember, smismember, scard,
spop, srandmember, smove, sinter, sunion, sdiff,
sinterstore/sunionstore/sdiffstore, sscan/sscan_iter.
Sorted sets: zadd (nx/xx/gt/lt/ch/incr), zincrby,
zscore, zmscore, zrem, zcard, zcount, zrange/zrevrange,
zrangebyscore/zrevrangebyscore (with (-exclusive and ±inf bounds),
zrank/zrevrank, zpopmin/zpopmax,
zremrangebyrank/zremrangebyscore,
zunionstore/zinterstore (with weights and SUM/MIN/MAX aggregation;
plain sets participate at score 1.0), zscan/zscan_iter.
Lists: lpush, rpush, lpop/rpop (with count), llen,
lrange, lindex, lset, lrem, ltrim, linsert, lmove,
rpoplpush.
Keys & server: delete, exists, expire, pexpire, expireat,
persist, ttl, pttl, keys (Redis glob patterns),
scan/scan_iter, rename, renamenx, randomkey, type, dbsize,
flushdb, ping.
Pipelines: pipeline() queues commands and runs them in a single
SQLite transaction on execute() — unlike real Redis MULTI/EXEC, the
batch is fully ACID: an error rolls back the whole batch.
with r.pipeline() as pipe:
pipe.set("a", 1).incr("hits").rpush("log", "x")
results = pipe.execute() # [True, 1, 1]
Semantics follow Redis: operations against a key of the wrong type raise
ResponseError (WRONGTYPE), removing a collection's last element removes
the key, and TTLs apply per key across all types. Expired keys are
invisible immediately and physically purged lazily, with a periodic sweep
on writes.
Async
babyredis.aio mirrors redis.asyncio: same constructor, every command
awaitable, *_iter helpers are async generators. Commands run in a
worker thread so the event loop never blocks on SQLite I/O.
from babyredis.aio import Redis
async with Redis("cache.db") as r:
await r.set("k", "v", ex=60)
await r.get("k")
async for key in r.scan_iter(match="user:*"):
...
Performance
Single-operation latency beats Redis-over-loopback ~6-10x (no TCP round
trip) and fakeredis ~5x; a plain dict beats everything by ~40x; Redis
wins back parity with pipelining and wins outright on concurrent write
throughput. Full table, methodology, and honest caveats in
docs/performance.md. Reproduce with
python benchmarks/bench.py.
Concurrency
File-backed databases use one SQLite connection per thread: reads take no
Python lock (WAL allows concurrent readers) and writes serialize through
SQLite's own locking. Cross-process access to the same file is safe —
writes are atomic via immediate transactions. :memory: databases use a
single lock-guarded connection (SQLite memory databases can't be shared
across connections).
Compatibility validation
tests/test_redka_compat.py ports the edge-case suite from
redka's Go tests (score tie-breaking,
list range/trim boundary permutations, store-variant overwrite semantics,
atomic mset rollback, and more). Where redka deviates from Redis —
silent wrong-type reads, keeping emptied keys, refusing cross-type
rename, no negative zrange indices — babyredis follows Redis, and
each divergence is noted in the test.
On top of that, tests/test_oracle.py runs property-based tests with
fakeredis as the oracle: a
Hypothesis state machine fires random command sequences at both clients
and asserts identical results (or identical failures) after every step.
Testing
babyredis doubles as a Redis stand-in for tests. Opt into the bundled fixtures:
# conftest.py
pytest_plugins = ["babyredis.testing"]
def test_something(babyredis_client):
babyredis_client.set("k", "v")
Install
pip install babyredis
Roadmap
- Type hints +
py.typed
Development
pip install -e .
pip install pytest hypothesis fakeredis
pytest tests/
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 babyredis-0.5.0.tar.gz.
File metadata
- Download URL: babyredis-0.5.0.tar.gz
- Upload date:
- Size: 17.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2007899a162335f38a75739dd0ccfe63f379a039751564f9a2108cc1ff80edb6
|
|
| MD5 |
216ef46243f0eb360c4a6bc62184722b
|
|
| BLAKE2b-256 |
99ce8a5d89b472fd75285f256094412bbf788307c9bbe90eed964725e16f10b0
|
Provenance
The following attestation bundles were made for babyredis-0.5.0.tar.gz:
Publisher:
release.yml on ciaracade/babyredis
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
babyredis-0.5.0.tar.gz -
Subject digest:
2007899a162335f38a75739dd0ccfe63f379a039751564f9a2108cc1ff80edb6 - Sigstore transparency entry: 1934718637
- Sigstore integration time:
-
Permalink:
ciaracade/babyredis@8e1a18396cdd571b191dfb3d01a1d05f051c7b1d -
Branch / Tag:
refs/tags/v0.5.0 - Owner: https://github.com/ciaracade
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@8e1a18396cdd571b191dfb3d01a1d05f051c7b1d -
Trigger Event:
push
-
Statement type:
File details
Details for the file babyredis-0.5.0-py3-none-any.whl.
File metadata
- Download URL: babyredis-0.5.0-py3-none-any.whl
- Upload date:
- Size: 19.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
92ba607c2b38ddbb02b0f2a1e27349482c73ebb21aeda2f3f0bcc598c950cc3f
|
|
| MD5 |
ccbfdcd74f805af1c2995264a05085cc
|
|
| BLAKE2b-256 |
499c4c2ea2b53532fa6d3e63120a12a8f0283d9519d44934054eee47100bb23c
|
Provenance
The following attestation bundles were made for babyredis-0.5.0-py3-none-any.whl:
Publisher:
release.yml on ciaracade/babyredis
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
babyredis-0.5.0-py3-none-any.whl -
Subject digest:
92ba607c2b38ddbb02b0f2a1e27349482c73ebb21aeda2f3f0bcc598c950cc3f - Sigstore transparency entry: 1934718691
- Sigstore integration time:
-
Permalink:
ciaracade/babyredis@8e1a18396cdd571b191dfb3d01a1d05f051c7b1d -
Branch / Tag:
refs/tags/v0.5.0 - Owner: https://github.com/ciaracade
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@8e1a18396cdd571b191dfb3d01a1d05f051c7b1d -
Trigger Event:
push
-
Statement type: