Skip to main content

On-Disk Input-keyed Cache — disk-backed memoization with pydantic-aware encoding

Project description

emboss

On-Disk Input-keyed Cache — disk-backed memoization with pydantic-aware encoding.

Version: 0.1.0

pip install emboss              # core (just diskcache)
pip install emboss[pydantic]    # + pydantic v2 BaseModel support

Why

functools.lru_cache is per-process. diskcache survives invocations but pickles values as-is — which breaks the moment your cached return type is a pydantic BaseModel defined in __main__ (the new process can't unpickle __main__.MyModel). emboss fixes that by detecting BaseModel return annotations and converting to/from plain dicts at the cache boundary.

Plus: a None-aware sentinel so functions returning None actually cache instead of re-running every call.

Quick start

import diskcache
from emboss import cached

cache = diskcache.Cache("/tmp/my-cache")

@cached(cache)
def fetch(url: str) -> dict:
    import requests
    return requests.get(url).json()

fetch("https://api.example.com/users/1")  # network
fetch("https://api.example.com/users/1")  # cached, no network

Pydantic BaseModel returns

emboss reads the function's return type annotation. If it sees a BaseModel, list[BaseModel], dict[str, BaseModel], or BaseModel | None, it serialises via model.model_dump() before pickling and rehydrates via Model.model_validate(...) on read. The cached value on disk is a plain dict — round-trips cleanly across process boundaries, even for models defined in __main__.

from pydantic import BaseModel

class User(BaseModel):
    id: int
    name: str

@cached(cache)
def get_user(uid: int) -> User | None:
    ...

@cached(cache)
def list_users() -> list[User]:
    ...

@cached(cache)
def users_by_id() -> dict[str, User]:
    ...

Functions returning non-BaseModel types continue to pickle as-is — fully backward-compatible.

None caching

@cached(cache)
def lookup(query: str) -> str | None:
    return external_api(query)

lookup("missing")  # returns None, cached
lookup("missing")  # returns cached None, no re-run

The previous behaviour (skip-cache-on-None) is replaced by a _MISSING sentinel internally so None is a valid cached value.

Cache key

Arguments are converted via safe_jsonable_encoder (recursive JSON-friendly conversion handling sets, bytes, dates, Path, BaseModel, and objects with __dict__), then hashed with the function source + name. Re-decorating the same function body → same key; changing the function body → new key (transparent cache invalidation on code change).

Async support

@cached(cache)
async def fetch_async(url: str) -> dict:
    async with httpx.AsyncClient() as c:
        return (await c.get(url)).json()

Cache hits return a fresh awaitable wrapping the cached value, so the call site keeps await-ing as normal.

Daily-rolling caches

The diskcache.Cache instance you pass is yours to manage. A common pattern for "expire daily" without thinking about it:

from datetime import date
import diskcache
cache = diskcache.Cache(f"/tmp/my-cache-{date.today()}")

Each new day → new dir → effectively fresh cache. Old dirs land in /tmp and get reaped by the OS.

License

MIT.

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

emboss-0.1.0.tar.gz (6.5 kB view details)

Uploaded Source

Built Distribution

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

emboss-0.1.0-py3-none-any.whl (7.1 kB view details)

Uploaded Python 3

File details

Details for the file emboss-0.1.0.tar.gz.

File metadata

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

File hashes

Hashes for emboss-0.1.0.tar.gz
Algorithm Hash digest
SHA256 3bc7f266804894c55ff4ef6f99ca106e168d816cc17d0bb82286a14bf2b3dc2a
MD5 5c90a5bf8d152c020c469c7187a26b65
BLAKE2b-256 43f6dcf8ab88b62da1187e8c0d0b7434eb26a7fef9ccb4daf8e94ab931246145

See more details on using hashes here.

Provenance

The following attestation bundles were made for emboss-0.1.0.tar.gz:

Publisher: release.yml on DJRHails/emboss

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

File details

Details for the file emboss-0.1.0-py3-none-any.whl.

File metadata

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

File hashes

Hashes for emboss-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 560537dadaac6189ce13ab8c17e2c20eedaa852f7ab09e9b6d1ecd8e06e4717d
MD5 228e2d1850d36a5bd149ca5032f5281b
BLAKE2b-256 d6689674f13db8dbe341a702be1b414dadebab940eb1948e5e7a73c7bf38e1a3

See more details on using hashes here.

Provenance

The following attestation bundles were made for emboss-0.1.0-py3-none-any.whl:

Publisher: release.yml on DJRHails/emboss

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