Extremely simple to use mid-execution memoization library.
Project description
belljar 🫙
Mid-execution memoization for dynamic state.
Standard caching fails when your function relies on hidden or changing state (like a database cursor or an open file). belljar solves this by letting you build the cache key while your code runs — and keep it, as a value, for later.
uv add belljar
The Difference
| Concept | functools.lru_cache |
belljar |
|---|---|---|
| When is it checked? | Before the function runs. | Mid-execution, exactly when you tell it to. |
| What defines identity? | Static function arguments. | Whatever runtime state you fold in, in order. |
| Handling Mutable State | Fails. Returns stale/wrong data. | Succeeds. Hashes current state dynamically. |
| Where can you retrieve? | Only by calling again. | Anywhere — identities are storable strings. |
Usage
A Jar is a persistent store plus a cursor. include() advances the cursor by folding runtime state into the identity; get() and set() read and write the value at the current position (get() returns None when nothing is sealed there).
from belljar import Jar
import io
jar = Jar() # caches under ./.jar by default; pass any path to move it
# A simulated file. The object stays the same, but its internal cursor moves.
log_file = io.StringIO("chunk1 chunk2 chunk3")
def process_chunk(file_handle):
jar.seek() # start a fresh identity for this call
jar.include(process_chunk.__code__) # fold this function's code: edits invalidate
jar.include(file_handle.tell()) # fold the file's exact runtime cursor position
# If we've processed from this exact position before, skip the work
if (cached := jar.get()) is not None:
return cached
# Otherwise, do the heavy processing and seal the result
print("Doing heavy work...")
return jar.set(file_handle.read(6))
process_chunk(log_file) # Reads "chunk1", saves to disk. (Takes time)
process_chunk(log_file) # Reads "chunk2", saves to disk. (Takes time)
log_file.seek(0)
process_chunk(log_file) # Cursor is back at 0 → instantly returns cached "chunk1"
No decorators, no changed return types — a cache hit is a plain if.
Identities Are Values
jar.identity exports the cursor as a string; jar.seek(identity) jumps back to it. That means the place that seals a value and the place that retrieves it don't have to share any code — store the identity wherever you like:
jar = Jar("./cache")
class KVStore:
def __init__(self):
self.index: dict[str, str] = {}
def add(self, key, value):
jar.seek()
jar.include(KVStore.add.__code__)
jar.include(key)
jar.set(value)
self.index[key] = jar.identity # keep the hash
def get(self, key):
jar.seek(self.index[key]) # jump straight to it
return jar.get()
Core Mechanics
- Identity is a trace. The cursor is a running hash: every
includechains onto everything folded before it, in order.include(a); include(b)andinclude(b); include(a)are different identities — the key mirrors the path your execution took. - Invalidation is inclusion. Fold whatever governs a value's lifetime:
func.__code__to invalidate on edit, a schema version, a mtime. If it's part of the identity, changing it is invalidation. - Disk Persistent: Caches survive restarts. Identities are deterministic, so a fresh process folding the same state finds the same entries.
- Deep Serialization: Powered by
dill(notpickle), meaning it safely handles lambdas, nested classes, and complex closures — both as folded state and as sealed values. - Concurrency: A
Jaris a lightweight cursor over a shared store — give each concurrent task its own (Jarconstruction is just a path and a hash seed).
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 belljar-0.2.0.tar.gz.
File metadata
- Download URL: belljar-0.2.0.tar.gz
- Upload date:
- Size: 3.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"CachyOS Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d9f1678bb13feb1a6a9a52805f0d6c33bad8e0247b4765e9f80182264b6da4bd
|
|
| MD5 |
64db1163a218877ccb79b2f2d7e77cce
|
|
| BLAKE2b-256 |
22c2661bfb5e891dca3750b046fe0c9fcb55656fb4c5b8e5027dd39217d8fd9e
|
File details
Details for the file belljar-0.2.0-py3-none-any.whl.
File metadata
- Download URL: belljar-0.2.0-py3-none-any.whl
- Upload date:
- Size: 4.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"CachyOS Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e3302d3ce544ce658902a9b67a94b612df6f438bb89ee884ee3672ef31f41767
|
|
| MD5 |
22d2c753975bf5e624daf41437596778
|
|
| BLAKE2b-256 |
dd12ff3eb9b6357072c877dcd7a799198152e4ea08244b6329987238064e8cc6
|