caching
Contains classes designed for easy caching/persistence.
These classes are designed to be used in cases where you would normally use a dict
but want to have the ability to persist between restarts/runs or do not/cannot store
the entire dict in memory.
Module
Functions
ensure_scheme_version
def ensure_scheme_version( db: diskcache.Cache, scheme: str, *, marker_filename: str = 'cache-scheme',) ‑> bool:Discard a diskcache store's contents when its recorded scheme is stale.
Derived-data caches whose keys or value shapes are a function of the code that wrote them cannot reuse entries across a layout change. Those entries become unreachable rather than wrong, but they keep occupying the store's size budget until eviction pressure clears them — competing with the fresh entries that replaced them. Recording the scheme a store was written under, and emptying it on a mismatch, reclaims that space at open time instead.
The recorded scheme lives in a sidecar file, marker_filename under
db.directory, rather than as a cache entry. A cache entry is subject to
the store's own eviction: diskcache culls in store_time order with no
exemption for any key, so a marker written once at first open would be
the oldest row in the table and the first one culled once the store
reaches its size cap. A subsequent open would then find no marker, read
that as an unversioned store, and clear a store that was in fact warm and
current. A file that is not a row in the Cache table cannot be culled
by it.
Opt-in: a store adopts this by calling it with its own scheme constant. A store that never calls it is unaffected. Adoption is a per-store decision because the policy is destructive — a cache whose contents are expensive or impossible to rebuild wants migration, not a discard.
db.clear() is called with its default retry=False, so it raises
Timeout if the store is locked rather than blocking indefinitely. That
is caught here: the clear is abandoned, the marker file is left as-is (so
the next open retries), and the call returns False. This is safe only
because a caller of this function is also expected to fold scheme into
its own cache keys — an uncleared, unmarked store then just misses on
every current-scheme lookup rather than serving a previous layout's entry
as though it matched.
Writing the marker can itself fail (a full disk, a read-only mount, a permissions error) after the store has already been cleared. That is caught too: the store stays cleared (so nothing wrong is served), no marker is written, a warning is logged, and the call returns False, leaving the next open to retry both the clear and the marker write.
That retry is not free. The store is empty only at the moment this
function returns; the process then goes on to refill it under the current
scheme, and the next open finds the marker still missing, reads that as an
unversioned store, and discards everything built since. A marker write that
keeps failing costs the whole cache once per open, silently apart from the
warning. Nothing incorrect is served — a caller folds scheme into its own
keys, so a surviving previous-layout entry could not be read either way —
but a store that cannot record its scheme cannot stay warm. The trigger is
narrow: the marker is written into the store's own directory, so most
conditions that stop it (no space, no permission) stop diskcache writing
entries there as well. The marker is written to a uniquely
named temporary file in the same directory (tempfile.mkstemp, not a
fixed name) and moved into place with os.replace, so two processes
racing to rewrite the marker cannot land a truncated or empty write from
one inside the other's replace, and an interrupted write cannot leave a
truncated scheme string that reads as a different scheme.
A marker file that exists but cannot be decoded as UTF-8 is treated the same as a missing one: an unknown scheme, i.e. stale.
Arguments
db: The diskcache store to check.scheme: The scheme identifier this build writes.marker_filename: The filename, underdb.directory, the scheme is recorded in.
Returns
Whether the store was cleared. False means it was already at
scheme, that clearing it timed out, or that it was cleared but its
marker could not be written.
get_cache
def get_cache( *, cache_name: str | None = None, datasource_name: str | None = None, project_id: str | None = None, task_id: str | None = None, use_tmp_dir: bool = False,) ‑> Cache:Create/retrieve cache given various name/specifier options.
At least one specifier option must be provided.
If a cache with the same specifiers exists, it will be opened and returned. If one does not exist it will be created.
Specifiers should be provided when a cache that is associated with a given datasource/task/project is wanted. For instance, if a cache is wanted for a given datasource for a given project, both datasource_name and project_id should be provided. Such a cache would be shared across all task runs within that project when using that datasource.
Arguments
cache_name: Human-provided name/additional specifier for cache.datasource_name: Name of datasource this cache is related to.project_id: UUID of the project this cache is related to.task_id: UUID of the task run this cache is related to.use_tmp_dir: Whether to use a temporary directory for the cache. If this is true then the cache is unlikely to be reusable across runs.
Returns Cache instance named for this combination of specifiers.
Raises
ValueError: if no specifiers are provided.
Classes
Cache
class Cache(cache_name: str):An on-disk cache/persistence class.
This can be used as though a standard mutable mapping.
Ancestors
Methods
add
def add(self, k: str) ‑> None:Add key to cache.
Stores key with a sentinel value so that key can be checked for via key in cache but where the value does not matter.
close
def close(self) ‑> None:Close the cache.
delete
def delete(self, k: str, error: bool = False) ‑> None:Delete key from cache.
Raises
KeyError: if key is not present anderroris True
delete_cache
def delete_cache(self) ‑> None:Delete the cache.
set
def set(self, k: str, v: _JSON) ‑> None:Set a value against key.
EncryptedDiskcacheFunctionCache
class EncryptedDiskcacheFunctionCache(json_compression_level: int = 6):An encrypted function cache implementation using diskcache.
This class provides a secure way to cache function results, with both keys and values encrypted on disk.
Initialize the EncryptedDiskcacheFunctionCache.
Arguments
json_compression_level: The compression level for JSON data (default: 6).
Ancestors
Methods
clear
def clear(self) ‑> None:Clear the cache.
memoize
def memoize( self, expire: float | None = 21600, ignore: Container[int | str] = (),) ‑> collections.abc.Callable[[collections.abc.Callable[~_P, ~_T]], Memoized[~_P, ~_T]]:Memoize a function, caching its results.
FunctionCache
class FunctionCache():A cache for applying to function calls à la functools.cache().
Subclasses
Methods
clear
def clear(self) ‑> None:Clear all entries from the cache.
This method removes all memoized results from the cache, effectively resetting it.
memoize
def memoize( self, expire: float | None = 21600, ignore: Container[int | str] = (),) ‑> collections.abc.Callable[[collections.abc.Callable[~_P, ~_T]], Memoized[~_P, ~_T]]:Memoize a function, caching its results.
This method returns a decorator that can be applied to functions to cache their results.
Arguments
expire: The time-to-live for cached entries in seconds. If None, entries do not expire.ignore: A container of argument indices or names to ignore when creating the cache key. Note that you may need to pass both the index and the keyword name if the argument can be passed by both.
Returns A decorator function that, when applied to a function, returns a memoized version of that function.
Memoized
class Memoized(*args, **kwargs):A protocol for memoized functions.
This protocol defines the interface for memoized functions, including methods for generating cache keys and calling the memoized function.