Skip to main content

babyredis

🧱 Redis-like commands, SQLite underneath, no server to run.

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 dependenciessqlite3 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 (set with ex/px/nx/xx/keepttl/get, ttl returning -2/-1, bytes responses with a decode_responses flag), 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/

Release files for babyredis 0.5.0

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

Source distribution (sdist)

Source distribution for babyredis 0.5.0
File Size Uploaded
babyredis-0.5.0.tar.gz 17.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for babyredis 0.5.0
File Interpreter ABI Platform
babyredis-0.5.0-py3-none-any.whl Python 3 none any Details

Total release size: 36.7 kB

Release files / babyredis-0.5.0.tar.gz

Download URL babyredis-0.5.0.tar.gz
Size 17.4 kB
Tags Source
SHA-256 checksum
How to use checksums
2007899a162335f38a75739dd0ccfe63f379a039751564f9a2108cc1ff80edb6
BLAKE2b-256 checksum
How to use checksums
99ce8a5d89b472fd75285f256094412bbf788307c9bbe90eed964725e16f10b0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 24, 2026.

Transparency log

Release files / babyredis-0.5.0-py3-none-any.whl

Download URL babyredis-0.5.0-py3-none-any.whl
Size 19.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
92ba607c2b38ddbb02b0f2a1e27349482c73ebb21aeda2f3f0bcc598c950cc3f
BLAKE2b-256 checksum
How to use checksums
499c4c2ea2b53532fa6d3e63120a12a8f0283d9519d44934054eee47100bb23c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 24, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.5.0 This release

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