Skip to main content

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 that folds the same trace.

Usage

A Jar is a persistent store plus a cursor: it opens at a fresh identity, and include() advances it by folding runtime state in. 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

# 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 = Jar()  # caches under ./.jar by default; pass any path to move it
    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.

Identity Is Reached, Not Given

Identities are deterministic: folding the same state in the same order always arrives at the same position. So the place that seals a value and the place that retrieves it don't have to share anything but the trace — factor it into a function and both sides reach the same entry:

class KVStore:
    def _entry(self, key):
        jar = Jar("./cache")
        jar.include(KVStore._entry.__code__)
        jar.include(key)
        return jar

    def add(self, key, value):
        self._entry(key).set(value)

    def get(self, key):
        return self._entry(key).get()

Core Mechanics

  • Identity is a trace. The cursor is a running hash: every include chains onto everything folded before it, in order. include(a); include(b) and include(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 (not pickle), meaning it safely handles lambdas, nested classes, and complex closures — both as folded state and as sealed values.
  • Concurrency: A Jar is a lightweight cursor over a shared store — construction is just a path and a hash seed, so make one per call or task instead of sharing a cursor.

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

belljar-0.4.0.tar.gz (3.5 kB view details)

Uploaded Source

Built Distribution

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

belljar-0.4.0-py3-none-any.whl (4.1 kB view details)

Uploaded Python 3

File details

Details for the file belljar-0.4.0.tar.gz.

File metadata

  • Download URL: belljar-0.4.0.tar.gz
  • Upload date:
  • Size: 3.5 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

Hashes for belljar-0.4.0.tar.gz
Algorithm Hash digest
SHA256 9f06d4447e25977d478f8e39aaa4f8aca27db09c983c55656d3d277349290d17
MD5 0365335cb4ac749cba50eb2a51def6e6
BLAKE2b-256 f9a8942038078c60facad54d4da7218104572e6f2241eefeaaf68f1429d9b98f

See more details on using hashes here.

File details

Details for the file belljar-0.4.0-py3-none-any.whl.

File metadata

  • Download URL: belljar-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 4.1 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

Hashes for belljar-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 23b5dfff2b0f8b2c8b8137ccdd432b14f06873a0a0104e6a0edb56d0f4a2b7a8
MD5 6f480452472326101ee72a6eb1102891
BLAKE2b-256 d7cde30c5d19f0c902fc2927d4dd2bb8e18ed373d7a55436bf157225d2aa09e6

See more details on using hashes here.

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