fleche.storage

Storage subpackage public API.

This module re-exports the primary storage interfaces and implementations for backward compatibility with from fleche.storage import … imports.

Submodules

Exceptions

SaveError

Common base class for all non-exit exceptions.

AmbiguousDigestError

Inappropriate argument value (of correct type).

Classes

Storage

Abstract base class for defining storage mechanisms.

CallStorage

Special storage for saving Call instances.

CallStorageAdapter

Implement a CallStorage from a generic Storage.

DestructuringMixin

Mixin that recursively destructures collections on save/load.

DestructuringStorage

Storage wrapper that recursively destructures collections.

Memory

A concrete implementation of Storage that stores values in an in-memory dictionary.

Void

A concrete implementation of Storage that does not store anything.

FileStorage

File-based storage backend using pickle.

PickleFile

Store values as files on the filesystem using a serialization module.

BagOfHoldingH5File

File-based storage backend using pickle.

Sql

SQLAlchemy-backed CallStorage with JSON metadata and DB-backed expand().

Package Contents

exception fleche.storage.SaveError[source]

Bases: Exception

Common base class for all non-exit exceptions.

exception fleche.storage.AmbiguousDigestError[source]

Bases: ValueError

Inappropriate argument value (of correct type).

class fleche.storage.Storage[source]

Bases: StorageBase

Abstract base class for defining storage mechanisms.

save(value: Any, key: fleche.digest.Digest | None = None) fleche.digest.Digest[source]
abstractmethod _save(value: Any, key: fleche.digest.Digest) fleche.digest.Digest[source]
load(key: fleche.digest.Digest | str) Any[source]
abstractmethod _load(key: fleche.digest.Digest) Any[source]
_contains(key: fleche.digest.Digest) bool[source]
class fleche.storage.CallStorage[source]

Bases: StorageBase

Special storage for saving Call instances.

save(call: fleche.call.Call) fleche.digest.Digest[source]
abstractmethod _save(call: fleche.call.Call) fleche.digest.Digest[source]
load(key: str | fleche.digest.Digest) fleche.call.Call[source]
abstractmethod _load(key: fleche.digest.Digest) fleche.call.Call[source]
transform(func: Callable[[fleche.call.Call], fleche.call.Call] | None = None) None[source]

Applies a transformation function to all Call objects in the storage.

Parameters:

func (Callable[[Call], Call] | None) – A function that takes a Call and returns a transformed Call. If None, the identity function is used (useful for re-calculating keys).

query(template: fleche.call.QueryCall) Iterable[fleche.call.Call][source]

Find cached calls that ‘match’ the template.

Returns all calls where the given arguments, results or metadata match exactly the stored ones. Values may be given either as they are or as Digest.

Parameters:

template (Call) – specification for calls to return; use None as wildcard.

Returns:

an iterable over all matching call objects

Return type:

Iterable[Call]

_contains(key: fleche.digest.Digest) bool[source]
class fleche.storage.CallStorageAdapter[source]

Bases: CallStorage

Implement a CallStorage from a generic Storage.

storage: Storage
_save(call: fleche.call.Call) fleche.digest.Digest[source]
_load(key: fleche.digest.Digest) fleche.call.Call[source]
_contains(key: fleche.digest.Digest) bool[source]
_evict(key: fleche.digest.Digest) None[source]
list() Iterable[fleche.digest.Digest][source]
class fleche.storage.DestructuringMixin[source]

Bases: Storage

Mixin that recursively destructures collections on save/load.

Place before a concrete Storage in the MRO to add destructuring behavior. Lists, tuples, and dicts are broken apart so each element is stored independently; on load the original structure is reassembled.

Example:

class DestructuringMemory(DestructuringMixin, Memory):
    pass

# ``storage`` here is the backing dict required by ``Memory``, not a
# Storage instance.
dm = DestructuringMemory(storage={})
key = dm.save([1, [2, 3]])
assert dm.load(key) == [1, [2, 3]]
remaining_depth: int = 0
_intern_rec(value: Any) tuple[Any, int | float][source]

Post-order traversal: recurse to leaves, decide inline-vs-store on the way back up.

Returns (result, depth) where result is the plain value when depth < remaining_depth (the element is inlined in its parent’s Digested wrapper) or a Digest when the element was written to storage separately. Every node in the structure is visited exactly once (O(n)), unlike a separate depth-counting pass.

_save(value: Any, key: fleche.digest.Digest) fleche.digest.Digest[source]
_load(key: fleche.digest.Digest | Any) Any[source]
class fleche.storage.DestructuringStorage[source]

Bases: DestructuringMixin, _DelegatingStorage

Storage wrapper that recursively destructures collections.

This is a convenience class combining DestructuringMixin with delegation to a wrapped storage. Prefer using DestructuringMixin directly as a mixin with a concrete storage class when possible.

remaining_depth: int = 0
__post_init__()[source]
class fleche.storage.Memory[source]

Bases: fleche.storage.base.Storage

A concrete implementation of Storage that stores values in an in-memory dictionary.

storage: dict[fleche.digest.Digest, Any]
_save(value: Any, key: fleche.digest.Digest) fleche.digest.Digest[source]
_load(key: fleche.digest.Digest) Any[source]
_contains(key: fleche.digest.Digest) bool[source]
list() Iterable[fleche.digest.Digest][source]
_evict(key: fleche.digest.Digest) None[source]
class fleche.storage.Void[source]

Bases: fleche.storage.base.Storage

A concrete implementation of Storage that does not store anything.

_save(value: Any, key: fleche.digest.Digest) fleche.digest.Digest[source]
_load(key: fleche.digest.Digest) Any[source]
list() Iterable[fleche.digest.Digest][source]
_evict(key: fleche.digest.Digest) None[source]
_contains(key: fleche.digest.Digest) bool[source]
class fleche.storage.FileStorage[source]

Bases: fleche.storage.base.Storage

File-based storage backend using pickle.

Stores objects on the filesystem.

root: pathlib.Path
lock_timeout: float = 1.0
lock_wait_start: float = 0.001
__post_init__() None[source]
_path(key: str) pathlib.Path[source]
list() Iterable[fleche.digest.Digest][source]
_evict(key: fleche.digest.Digest) None[source]
_save(value: Any, key: fleche.digest.Digest) fleche.digest.Digest[source]
_load(key: fleche.digest.Digest) Any[source]
abstractmethod _to_file(value: Any, path: pathlib.Path) None[source]
abstractmethod _from_file(path: pathlib.Path) Any[source]
_contains(key: fleche.digest.Digest) bool[source]
class fleche.storage.PickleFile[source]

Bases: fleche.storage.file.FileStorage

Store values as files on the filesystem using a serialization module.

secret_key: list[bytes] = []
serializer: Any
compress: bool = False
__post_init__()[source]
classmethod with_pickle(*args, **kwargs)[source]

Construct a PickleFile using the standard pickle module.

classmethod with_cloudpickle(*args, **kwargs)[source]

Construct a PickleFile using the cloudpickle module.

classmethod with_dill(*args, **kwargs)[source]

Construct a PickleFile using the dill module.

__getstate__()[source]
__setstate__(state)[source]
_to_file(value: Any, path: pathlib.Path) None[source]
_from_file(path: pathlib.Path) Any[source]
class fleche.storage.BagOfHoldingH5File[source]

Bases: fleche.storage.file.FileStorage

File-based storage backend using pickle.

Stores objects on the filesystem.

__post_init__()[source]
_to_file(value: Any, path: pathlib.Path) None[source]
_from_file(path: pathlib.Path) Any[source]
class fleche.storage.Sql[source]

Bases: fleche.storage.base.CallStorage

SQLAlchemy-backed CallStorage with JSON metadata and DB-backed expand().

url: str | None = None
echo: bool = False
engine: Any
session: Any
__post_init__() None[source]
__getstate__()[source]
__setstate__(state)[source]
_save(call: fleche.call.Call) fleche.digest.Digest[source]
_load(key: fleche.digest.Digest) fleche.call.Call[source]
_contains(key: fleche.digest.Digest) bool[source]
list() Iterable[fleche.digest.Digest][source]
expand(key: fleche.digest.Digest | str) fleche.digest.Digest[source]

Expands a short-hand digest to the full length one.

_evict(key: fleche.digest.Digest) None[source]
_normalize_value(v: Any) str[source]

Return the stored form used in SQL for argument/result matching.

We must match the generic CallStorage.query semantics which compare digest(template_value) == digest(stored_call_value). In this backend, stored argument/result values are hex-digest strings, and digest(Digest(x)) == x. Therefore we should always compare Arg.value/CallModel.result to str(digest(template_value)).

_build_call_conditions(template: fleche.call.QueryCall) List[Any][source]
_apply_argument_filters(stmt: Any, arguments: dict[str, Any] | None) Any[source]
_apply_metadata_filters(stmt: Any, meta_specs: dict[str, dict[str, Any]] | None) Any[source]
query(template: fleche.call.QueryCall) Iterable[fleche.call.Call][source]

Find cached calls matching a template using SQL-side filtering.

Semantics match CallStorage.query: - Fields set to None are wildcards. - Arguments and result are compared by digest(template_value) == digest(stored_value). - Metadata can be filtered by providing template.metadata as a mapping of

metadata name -> dict of key/value filters. An empty dict for a given name means “presence of that metadata name”. Filters with simple types (str, bool, int, float) are pushed down to SQL via JSON-extract expressions; other types (e.g., lists) or None values fall back to client-side checks after loading.

This method builds a SELECT over calls, joining the arguments table and metadata table as needed to reduce candidate rows, then loads the resulting calls and performs any remaining client-side validation.

Parameters:

template – A Call used as a template. None-valued fields are wildcards.

Yields:

Call – Matching calls including their decoded metadata.