๐Ÿฆ€ Rust Core ๐Ÿ Python API v0.2.0 ๐Ÿ“ฆ PyPI

Cache in Python,
at Rust speed

In-memory, thread-safe cache for FastAPI, Django, and any Python backend.
TTL with lazy expiration, a memoize decorator, and built-in metrics โ€” no Redis, no broker, no database.

$ pip install rust-py-cache
python
>>> from rust_py_cache import Cache
>>> cache = Cache()
>>> cache.set("user:1", {"name": "Roberto"}, ttl=60)
>>> cache.get("user:1")
{'name': 'Roberto'}
>>> cache.stats()
{'hits': 1, 'misses': 0, 'sets': 1, ...}
>>> โ–ˆ
31Tests passing
lazy TTLExpiration
2Integrations
0Runtime deps

Everything a cache needs

Rust handles storage, locking, and expiration. Python stays simple.

โšก

In-Memory Speed

Reads and writes hit a sharded DashMap in process โ€” no network hop, no disk. A "mini Redis" living inside your app.

โณ

TTL & Lazy Expiration

set(key, value, ttl=60) in seconds. Expired keys are dropped on access, or swept in bulk with cleanup_expired().

๐Ÿ—ƒ๏ธ

Any Python Object

Values are pickle-serialized, so dicts, lists, dataclasses, sets โ€” anything picklable โ€” round-trip unchanged.

๐Ÿงต

Thread-Safe

Per-shard locks plus AtomicU64 counters mean you share one Cache across threads with no global lock on the hot path.

๐ŸŽ€

Memoize Decorator

@cache.cached(ttl=60) caches a function's return keyed by its arguments โ€” and even a None result is cached, not recomputed.

๐Ÿ“Š

Built-in Metrics

stats() returns hits, misses, sets, deletes, expired, and size โ€” observability for free.

๐Ÿ”‘

Dict-like API

set / get / delete / exists / keys / clear / len(cache) โ€” exactly the methods you'd reach for.

๐ŸŒ

FastAPI & Django

Drop a Cache in front of a slow query or report. Per-process, no broker, no Redis instance to run.

๐Ÿฆ€

Rust Core via PyO3

Storage, TTL, and counters compile to a native .so extension against the stable ABI. No Rust needed to use it.

Works with your stack

Call the core API, memoize a function, or wire it into your framework.

Python
from rust_py_cache import Cache

cache = Cache()

# Values can be any picklable Python object.
cache.set("user:1", {"name": "Roberto"})
cache.get("user:1")               # {'name': 'Roberto'}
cache.get("missing", default=0)   # 0 โ€” no KeyError

cache.exists("user:1")            # True (honors TTL)
cache.delete("user:1")            # True if it existed
cache.keys()                       # ['...']
len(cache)                         # number of entries

cache.stats()  # {'hits', 'misses', 'sets', 'deletes', 'expired', 'size'}
Python
from rust_py_cache import Cache

cache = Cache()

# ttl in seconds (int or float); None = never expires.
cache.set("token", "abc", ttl=30)
cache.set("forever", 42)             # no expiration
cache.set("bad", 1, ttl=0)         # ValueError: ttl must be > 0

# Lazy expiration: an expired key is dropped on access...
cache.get("token")   # None once 30s have passed

# ...or you can sweep every expired entry at once:
removed = cache.cleanup_expired()   # -> count removed
Python
from rust_py_cache import Cache

cache = Cache()

# Memoize by arguments โ€” runs once per distinct call.
@cache.cached(ttl=60)
def fib(n):
    return n if n < 2 else fib(n-1) + fib(n-2)

fib(30)   # computed once, then served from cache

# Custom key: a fixed string, or a callable of the args.
@cache.cached(ttl=300, key=lambda uid: f"user:{uid}")
def load_user(uid):
    return db.query(uid)
Python
from fastapi import FastAPI
from rust_py_cache import Cache

app = FastAPI()
cache = Cache()

@app.get("/users/{user_id}")
def get_user(user_id: int):
    key = f"user:{user_id}"
    cached = cache.get(key)
    if cached is not None:
        return {"source": "cache", "data": cached}

    user = load_from_db(user_id)      # slow path
    cache.set(key, user, ttl=30)
    return {"source": "db", "data": user}
Python
# views.py
from django.http import JsonResponse
from rust_py_cache import Cache

cache = Cache()

def product_detail(request, product_id):
    key = f"product:{product_id}"
    data = cache.get(key)
    if data is None:
        product = Product.objects.get(pk=product_id)
        data = {"id": product.id, "name": product.name}
        cache.set(key, data, ttl=60)
    return JsonResponse(data)

# Invalidate when the row changes:
@receiver(post_save, sender=Product)
def drop_cache(sender, instance, **kwargs):
    cache.delete(f"product:{instance.id}")

Observable by default

Every operation is counted with lock-free atomics. Inspect the cache at any time.

hits
reads served
ยท
misses
absent / expired
ยท
sets
writes
ยท
deletes
removals
ยท
expired
TTL evictions
ยท
size
live entries

Counters

  • hits read found
  • misses read empty
  • sets write
  • deletes remove
  • expired TTL swept

Good to know

  • AtomicU64 lock-free
  • O(1) snapshot
  • thread-safe shared
cache.stats()
{
  "hits": 128,
  "misses": 7,
  "sets": 135,
  "deletes": 3,
  "expired": 12,
  "size": 120
}
cache.cleanup_expired()
# sweep every expired entry, return how many left
12

How it works

Rust stores and expires. Python serializes and calls. You inspect.

Your App
FastAPI Django Script
โ†’
Python API
set() / get() @cache.cached() stats()
โ†’
๐Ÿฆ€ Rust Core
cache.rs entry.rs stats.rs ttl.rs
โ†’
Output
values in / out TTL + LRU eviction hit / miss counts

Storage is a concurrent DashMap<String, CacheEntry>; values are opaque Vec<u8> bytes that Python pickles. The native .so is compiled once via maturin + PyO3, against the stable ABI (abi3-py310) โ€” one wheel covers Python 3.10โ€“3.13+.
Your users just pip install โ€” no Rust toolchain, no Redis, no message broker required.

Ready in seconds

Install from PyPI. No Rust, no compilers, no configuration.

Install

pip install rust-py-cache

Upgrade

pip install -U rust-py-cache

From source

maturin develop

Verify

python -c "from rust_py_cache import Cache"
Requires Python 3.10+ ยท Linux ยท macOS ยท Windows ยท MIT License
Copied!