PicoCache: Persistent Memoization
A Persistent, datastore‑backed lru_cache for Python.
PicoCache gives you the ergonomics of functools.lru_cache while keeping your
cached values safe across process restarts and even across machines.
PicoCache ships with a zero‑dependency SQLiteCache that relies only on the
standard‑library sqlite3 module. Additional back‑ends can be enabled via
extras:
- SQLAlchemyCache – persists to any SQL database supported by SQLAlchemy.
- RedisCache – stores values in Redis, ideal for distributed deployments.
- DjangoCache – plugs straight into Django’s configured cache backend (Memcached, Redis, database, etc.).
Why PicoCache?
- Familiar API – decorators feel identical to
functools.lru_cache. - Durable – survive restarts, scale horizontally.
- Introspectable –
cache_info()andcache_clear()just like the standard library. - Zero boilerplate – pass a connection URL and start decorating.
Installation
# core (built‑in SQLiteCache, no external deps)
pip install picocache
# optional extras
pip install picocache[redis] # RedisCache
pip install picocache[sqlalchemy] # SQLAlchemyCache
pip install picocache[django] # DjangoCache
# or any combination, e.g.
pip install "picocache[redis,sqlalchemy]"
Quick‑start
1. Built‑in SQLiteCache (no external deps)
from picocache import SQLiteCache
cache = SQLiteCache() # defaults to ./picocache.db
@cache
def fib(n: int) -> int:
return n if n < 2 else fib(n - 1) + fib(n - 2)
2. SQLAlchemy back‑end
from picocache import SQLAlchemyCache
# Create the decorator bound to an SQLite file
sql_cache = SQLAlchemyCache("sqlite:///cache.db")
@sql_cache(maxsize=256) # feels just like functools.lru_cache
def fib(n: int) -> int:
return n if n < 2 else fib(n - 1) + fib(n - 2)
3. Redis
from picocache import RedisCache
redis_cache = RedisCache("redis://localhost:6379/0")
@redis_cache(maxsize=128, typed=True)
def slow_add(a: int, b: int) -> int:
print("Executing body…")
return a + b
On the second call with the same arguments, slow_add() returns instantly and
“Executing body…” is not printed – the result came from Redis.
4. Django
from picocache import DjangoCache
django_cache = DjangoCache() # uses settings.CACHES["default"]
@django_cache(maxsize=None) # unlimited size, rely on Django’s TTL
def expensive_fn(x):
...
Thread Safety
PicoCache is fully thread-safe. Multiple threads can safely call decorated functions concurrently without any additional synchronization.
Key Thread Safety Features:
- Atomic Operations: All cache operations (lookup, store, evict) are atomic and protected by locks.
- No Duplicate Computation: If multiple threads request the same uncached value simultaneously, only one thread computes it while others wait for the result.
- Safe Statistics: Hit/miss counters are updated atomically.
- Concurrent Access: Different threads can safely access different cached values in parallel.
Backend-Specific Notes:
- SQLiteCache: Uses a global lock for all operations. Thread-safe but may become a bottleneck under high concurrency.
- RedisCache: Leverages Redis's atomic operations and connection pooling for excellent concurrent performance.
- SQLAlchemyCache: Uses SQLAlchemy's connection pooling for thread-safe database access.
- DjangoCache: Inherits thread safety from Django's cache framework. When
maxsizeis specified, adds an additionalfunctools.lru_cachelayer which is also thread-safe.
Example with Threading:
from picocache import SQLiteCache
import threading
cache = SQLiteCache()
@cache(maxsize=128)
def expensive_computation(x):
import time
time.sleep(1) # Simulate expensive work
return x * x
# Multiple threads can safely call the cached function
threads = []
for i in range(10):
t = threading.Thread(target=lambda: expensive_computation(5))
threads.append(t)
t.start()
for t in threads:
t.join()
# Only one thread will perform the computation; others will wait and reuse the result
print(expensive_computation.cache_info()) # Shows hits=9, misses=1
API
Each decorator is constructed with connection details (if any) and called with
the same signature as functools.lru_cache:
SQLAlchemyCache(url_or_engine, *, key_serializer=None, value_serializer=None, ...)
RedisCache(url_or_params, *, key_serializer=None, value_serializer=None, ...)
DjangoCache(*, key_serializer=None, value_serializer=None, ...)
__call__(maxsize=None, typed=False)
Returns a decorator that memoises the target function.
| Param | Type | Default | Meaning |
|---|---|---|---|
maxsize |
int/None |
None |
Per‑function entry limit (None → no limit). |
typed |
bool |
False |
Treat arguments with different types as distinct (same as stdlib). |
The wrapped function gains:
.cache_info()→namedtuple(hits, misses, currsize, maxsize).cache_clear()or.clear()→ empties the persistent store for that function.
Running the tests
uv sync --all-extras
just test
- SQL tests run against an in‑memory SQLite DB (no external services).
- Redis tests are skipped automatically unless a Redis server is available on
localhost:6379.
License
MIT – see LICENSE for details.
Metadata
Release files for picocache 1.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| picocache-1.4.0.tar.gz | 14.3 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| picocache-1.4.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 30.1 kB
Release files / picocache-1.4.0.tar.gz
| Download URL | picocache-1.4.0.tar.gz |
|---|---|
| Size | 14.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d7d14c0065311046e16a7b2dd283816a78e7d22d4b5dbf398ddb5ca7d3b27011
|
|
BLAKE2b-256 checksum How to use checksums |
b61ceda90e7ce3fd53a53f85ffe324f51a14ac95eaef0c3c2b277ad503ab11e4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.13.0 {"installer":{"name":"uv","version":"0.13.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / picocache-1.4.0-py3-none-any.whl
| Download URL | picocache-1.4.0-py3-none-any.whl |
|---|---|
| Size | 15.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0846b94fcfd14cb3faa1738acf23b2a538c21ba446be3bcc8c11ec0e6fa0192d
|
|
BLAKE2b-256 checksum How to use checksums |
ee2dbc9516f43030be1b1c5949c79c068357166bbcbb86a408adce9fde4dc54b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.13.0 {"installer":{"name":"uv","version":"0.13.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Security for picocache 1.4.0
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.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.