fleche.storage.destructuring

Attributes

_DESTRUCTURERS

Classes

Digested

Helper class that provides a standard way to create an ABC using

DigestedIterable

Helper class that provides a standard way to create an ABC using

DigestedDict

Helper class that provides a standard way to create an ABC using

DigestedFields

Common base for record-shaped value markers (dataclasses, attrs).

DigestedDataclass

Per-field marker for stdlib dataclasses instances.

DigestedAttrs

Per-field marker for attrs-decorated instances.

HasChildDigests

Structural protocol for value storages that expose a reference-graph edge query.

DestructuringMixin

Mixin that recursively destructures collections on save/load.

Functions

register_destructurer(→ None)

Register a custom container destructurer.

Module Contents

class fleche.storage.destructuring.Digested[source]

Bases: abc.ABC

Helper class that provides a standard way to create an ABC using inheritance.

abstractmethod underlying()[source]

Return plain underlying value, ie. list/dict/etc of nested values or their partial digests

__digest__()[source]
abstractmethod mend(storage: DestructuringMixin)[source]
classmethod sunder(intern: Callable[[Any], tuple[Any, int | float]], value: Any)[source]
Abstractmethod:

static get(storage, key)[source]
class fleche.storage.destructuring.DigestedIterable[source]

Bases: Digested

Helper class that provides a standard way to create an ABC using inheritance.

items: list | tuple[source]
underlying()[source]

Return plain underlying value, ie. list/dict/etc of nested values or their partial digests

mend(storage: DestructuringMixin) list | tuple[source]
classmethod sunder(intern: Callable[[Any], tuple[Any, int | float]], value: list | tuple)[source]
class fleche.storage.destructuring.DigestedDict[source]

Bases: Digested

Helper class that provides a standard way to create an ABC using inheritance.

items: dict[source]
underlying()[source]

Return plain underlying value, ie. list/dict/etc of nested values or their partial digests

mend(storage: DestructuringMixin) dict[source]
classmethod sunder(intern: Callable[[Any], tuple[Any, int | float]], value: dict)[source]
class fleche.storage.destructuring.DigestedFields[source]

Bases: Digested

Common base for record-shaped value markers (dataclasses, attrs).

Subclasses provide _field_items() to enumerate (name, value) pairs from a live instance; sunder(), mend(), and __digest__() are shared. Field values may be replaced by Digest back-references when they are stored independently. mend() reconstructs the original instance by bypassing __init__ / __post_init__, so InitVar and init=False fields (and attrs slots / frozen instances) are all handled uniformly.

cls: type[source]
fields: dict[source]
underlying()[source]

Return plain underlying value, ie. list/dict/etc of nested values or their partial digests

__digest__()[source]
mend(storage: DestructuringMixin)[source]
static _field_items(value: Any) list[tuple[str, Any]][source]
Abstractmethod:

classmethod sunder(intern: Callable[[Any], tuple[Any, int | float]], value: Any)[source]
class fleche.storage.destructuring.DigestedDataclass[source]

Bases: DigestedFields

Per-field marker for stdlib dataclasses instances.

static _field_items(value: Any) list[tuple[str, Any]][source]
class fleche.storage.destructuring.DigestedAttrs[source]

Bases: DigestedFields

Per-field marker for attrs-decorated instances.

static _field_items(value: Any) list[tuple[str, Any]][source]
fleche.storage.destructuring._DESTRUCTURERS: list[tuple[Callable[[Any], bool], Callable]][source]
fleche.storage.destructuring.register_destructurer(pred: Callable[[Any], bool], fn: Callable) None[source]

Register a custom container destructurer.

pred(value) should return True for values this destructurer handles. fn must accept (intern, value) where intern is DestructuringMixin._intern_rec(). Entries are appended after the built-in ones; first match wins, so registering a handler for an entirely new container type is safe without displacing list/dict/dataclass/attrs. Call before any DestructuringMixin instance is used.

class fleche.storage.destructuring.HasChildDigests[source]

Bases: Protocol

Structural protocol for value storages that expose a reference-graph edge query.

Any ValueStorage that implements child_digests() satisfies this protocol automatically — no explicit registration or inheritance required. Use isinstance(storage, HasChildDigests) to check at runtime whether the storage supports transitive reachability walks.

child_digests(key: fleche.digest.Digest) set[fleche.digest.Digest][source]

Return the set of digests directly referenced by the entry at key.

Raises:

KeyError – if key is not present in the storage.

class fleche.storage.destructuring.DestructuringMixin[source]

Bases: fleche.storage.base.ValueStorage

Mixin that recursively destructures collections on save/load.

Place before a ValueMixin 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

>>> from fleche.storage.base import ValueMixin
>>> from fleche.storage.memory import MemoryBackend
>>> @dataclass(frozen=True)
... class MyValueStorage(DestructuringMixin, ValueMixin, MemoryBackend): ...
>>> vm = MyValueStorage(storage={})
>>> key = vm.save([1, [2, 3]])
>>> vm.load(key) == [1, [2, 3]]
True
remaining_depth: int = 0[source]
static _is_trojan_tuple(value)[source]
_intern_rec(value: Any, key: fleche.digest.Digest | None = None) 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 | None = None) fleche.digest.Digest[source]
load(key: fleche.digest.Digest | str) Any[source]
_raw_sub_digests(raw: Any) set[fleche.digest.Digest][source]

Direct digest children of a raw stored entry.

A raw entry is what super().load returns — i.e. what was written to the underlying backend before mend() rewires sub-digests back into their parent container. Only Digested wrappers carry child references; scalars and plain (non-destructured) containers return an empty set.

child_digests(key: fleche.digest.Digest | str) set[fleche.digest.Digest][source]

Direct digest children of the raw entry stored at key.

Bypasses mend(), so destructured sub-references are returned as opaque Digest keys rather than being followed. Intended for reference-graph traversals (GC, debugging) where loading the mended value would flatten the structure we need to inspect.

Raises:

KeyError – if key is not present in the underlying backend.

count_reuses() collections.Counter[fleche.digest.Digest][source]

Return a counter of how many times each stored key is referenced as a sub-component.

Scans every raw entry and tallies Digest back-references found inside DigestedIterable and DigestedDict wrappers. A count of 0 means the key is not pointed to by any other stored value (i.e. a top-level entry). A count greater than 1 indicates a sub-value shared between multiple parent containers.

Returns:

A Counter mapping each Digest key to the number of times it is referenced by other stored entries.

Example

>>> from fleche.storage.memory import ValueMemory
>>> ds = ValueMemory(storage={})
>>> shared = [2, 3]
>>> _ = ds.save([1, shared])
>>> _ = ds.save([4, shared])
>>> hits = ds.count_reuses()
>>> hits[ds.save(shared)]  # [2, 3] is referenced by both outer lists
2