Skip to main content

Redis-like caching with zero infrastructure -- an embedded, SQLite-backed cache.

Project description

lytecache

Redis-like caching with zero infrastructure. lytecache gives you the familiar Redis API surface -- set/get, TTLs, atomic counters, eviction -- backed by a local SQLite file instead of a server. No daemon to run, no port to open, no client library to configure. Just pip install and go.

Install

pip install lytecache

Quickstart

from lytecache import LyteCache

cache = LyteCache()                    # no path, no setup -- just works
cache.set("user:42", {"name": "Ada"}, ttl=300)
cache.get("user:42")                   # {"name": "Ada"}
cache.incr("hits")                     # 1
cache.get("missing", "default")        # "default"

That's it. The first call to LyteCache() creates the database file (including any missing parent directories) and applies the schema automatically. There is no init(), no migration step, and no server to start.

Where is my data?

By default, LyteCache() stores its file at:

<platform cache dir>/lytecache/<project-id>.db
  • Linux: $XDG_CACHE_HOME/lytecache/<project-id>.db, or ~/.cache/lytecache/<project-id>.db
  • macOS: ~/Library/Caches/lytecache/<project-id>.db
  • Windows: %LOCALAPPDATA%\lytecache\<project-id>.db

<project-id> is a short hash of your current working directory, so every project on your machine automatically gets its own cache file -- two different apps never collide, and nothing is left behind in your repo.

You can inspect or override this:

LyteCache.default_path()     # -> Path, the resolved default location
cache.path                   # -> Path, this instance's actual file
cache.stats()["path"]        # the file is never a mystery

To pin the location explicitly (containers, tests, CI), either pass a path or set an environment variable:

cache = LyteCache("/data/cache.db")   # explicit escape hatch
export LYTECACHE_PATH=/data/cache.db  # takes priority over the default

API

Method Description
set(key, value, ttl=None) Store a value, optionally with a TTL in seconds.
get(key, default=None, cls=None) Read a value; returns default on miss or expiry. Never raises on miss. cls reconstructs a dataclass/plain object from a stored JSON value.
delete(*keys) Delete keys; returns the number actually deleted.
exists(key) Whether a (non-expired) key is present.
add(key, value, ttl=None) Set only if absent (atomic SET NX).
replace(key, value, ttl=None) Set only if present (atomic SET XX).
get_set(key, value) Atomically swap in a new value, returning the old one.
set_many(mapping, ttl=None) / get_many(keys) Bulk set/get in a single transaction.
expire(key, ttl) / persist(key) Set or remove a TTL on an existing key.
ttl(key) Seconds remaining (float), -1 if no expiry, None if missing.
touch(key, ttl) Refresh a key's TTL (sliding expiration).
incr(key, amount=1) / decr(key, amount=1) Atomic integer counters.
incr_float(key, amount) Atomic float counter.
keys(pattern="*") Lazily iterate matching keys (glob syntax).
flush() Clear the current namespace.
stats() Hits, misses, hit rate, key count, size, evictions, path.
vacuum() / close() Reclaim disk space / shut down cleanly.
memoize(ttl=None) Decorator that caches a function's return value.
lock(name, timeout=30, blocking=True, poll=0.05) Process-safe context-manager lock.

LyteCache also works as a context manager:

with LyteCache() as cache:
    cache.set("k", "v")

Serialization

str/int/float/bytes/bool round-trip exactly. Anything else -- dict, list, dataclasses, plain objects -- is JSON-encoded, and reads back as a plain dict/list by default:

from dataclasses import dataclass

@dataclass
class Address:
    city: str
    zip_code: str

@dataclass
class Person:
    name: str
    age: int
    address: Address

cache.set("p:1", Person("Ada", 30, Address("London", "E1")))

cache.get("p:1")               # {"name": "Ada", "age": 30, "address": {...}} -- plain dict
cache.get("p:1", cls=Person)   # Person(name="Ada", age=30, address=Address(...)) -- typed

tuple values are JSON-encoded too and come back as list -- there's no tuple type in JSON.

Values that can't be represented as JSON raise SerializationError by default (serializer="auto"). If you genuinely need to cache an arbitrary Python object, opt into pickling explicitly:

cache = LyteCache(serializer="pickle")   # falls back to pickle when JSON can't represent a value

Security note: unpickling can execute arbitrary code. The default (serializer="auto") and serializer="json" never write or read pickled data, so this doesn't apply to them. Only serializer="pickle" (or serializer="auto" with allow_pickle=True) can read pickled values -- treat a cache file that might contain pickled data like application code, and never open one from an untrusted source.

When to use lytecache

Use it when:

  • You want caching, counters, or TTLs in a single-process (or single-machine, multi-process) application with no infrastructure to stand up.
  • Scripts, CLIs, notebooks, small web services, background jobs.
  • You want the cache file to survive restarts without running a separate daemon.

Don't use it when:

  • You need a cache shared live across multiple servers/hosts -- SQLite is a local file, not a network service. Use Redis/Memcached instead.
  • You have heavy concurrent write throughput from many processes -- SQLite's single-writer model will serialize writes and become a bottleneck.
  • You need pub/sub, streams, or other Redis data structures beyond a key-value store with counters. lytecache intentionally stays small.

Configuration reference

LyteCache(
    path=None,             # explicit file path; default: LyteCache.default_path()
    namespace="default",   # logical partition within the database file
    max_keys=None,         # evict when the namespace exceeds this many keys
    max_bytes=None,        # evict when the namespace exceeds this many bytes
    eviction="lru",        # "lru" | "ttl" | "random" | "noeviction"
    sweep_interval=60.0,   # seconds between background maintenance passes;
                           # None disables the thread and does maintenance
                           # opportunistically every ~100 operations instead
    serializer="auto",     # "auto" | "json" (strict, no pickle) | "pickle"
    strict=False,          # True: raise on internal read errors instead of
                           # degrading to a miss
    allow_pickle=False,    # "auto" mode only: allow reading pickled values
                           # written by a serializer="pickle" cache
)
  • Eviction policies: lru (default, evicts least-recently-used), ttl (evicts soonest-to-expire first), random, and noeviction (raises CacheFullError instead of evicting). lfu is a documented TODO.
  • Serialization: see the Serialization section above. serializer="auto" (the default) and serializer="json" never write or read pickled data; serializer="pickle" opts into pickling as a fallback for values JSON can't represent.
  • Concurrency: safe across threads (one connection per thread) and across processes (SQLite WAL mode). stats() counters (hits/misses/etc.) are per-process, not shared cluster-wide.

See SPEC.md for the on-disk schema and full semantics, and CHANGELOG.md for release notes.

License

Apache 2.0. See LICENSE.

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

lytecache-0.2.0.tar.gz (30.6 kB view details)

Uploaded Source

Built Distribution

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

lytecache-0.2.0-py3-none-any.whl (21.2 kB view details)

Uploaded Python 3

File details

Details for the file lytecache-0.2.0.tar.gz.

File metadata

  • Download URL: lytecache-0.2.0.tar.gz
  • Upload date:
  • Size: 30.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for lytecache-0.2.0.tar.gz
Algorithm Hash digest
SHA256 3566d6cd8621d9c4684a6277233981f02d005e1722d9ed248b46a62fd6cc0cce
MD5 22139d50f64195d4b801e589ff5f2c23
BLAKE2b-256 1d10459271edf0fe0bacb2d50c8f2ba89eda731abb074221db3f2b23178ab884

See more details on using hashes here.

Provenance

The following attestation bundles were made for lytecache-0.2.0.tar.gz:

Publisher: release.yml on lytecache/lytecache

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file lytecache-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: lytecache-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 21.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for lytecache-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 2ac15430d36056b2e12b46772858884c11cac88eec430af867f3a47950fefc77
MD5 ac04f112d418f3067d274996e5e3c793
BLAKE2b-256 a104cdc64bbad7822f785fdae8c9eaa9933965764b8c1f2c7893831231b17efd

See more details on using hashes here.

Provenance

The following attestation bundles were made for lytecache-0.2.0-py3-none-any.whl:

Publisher: release.yml on lytecache/lytecache

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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