httk.store.storage_layout

Backend-neutral store declaration machinery shared by storage backends.

This module owns the logical entry-family declaration, its canonical JSON encoding, and trust-on-reopen validation. Physical names and backend-specific layout validation belong to each storage backend.

Attributes

DECLARATION_PROTOCOL_VERSION

The current backend-neutral declaration protocol.

ADDITIVE_UPGRADE_HINT

The reopen hint appended when an additive-only schema mismatch is not applied.

Exceptions

StorageLayoutUpgradeRequiredError

A database does not exactly implement the current persisted store layout.

EntryLayoutBindingError

A persisted application-owned layout needs explicit Python class bindings.

Classes

EntryRecordDeclaration

Bind one stable store-local name to a concrete record class.

EntryFamilyDeclaration

Declare one application-owned entry family without global registration.

EntryFamilyLayout

One immutable configured entry family and its concrete records.

StorageLayout

The immutable normalized entry declaration of an initialized store.

AdditiveUpgradePlan

The nullable parent columns an additive fingerprint upgrade must add per table.

Functions

validate_entry_id_fields(layout)

Require entry-id fields on every backing of a defined entry family.

normalize_entry_records(entry_records)

Validate an explicit class declaration and replace it with stable registry names.

normalize_entry_families(entry_families)

Validate application-owned entry declarations and build a store layout.

declaration_json(layout)

Serialize a normalized declaration in its exact deterministic persisted form.

schema_fingerprint_json(layout)

Serialize the resolved per-table schema of layout in deterministic form.

schema_fingerprint_diff(stored, current)

Diff a stored fingerprint against the current one, per differing table.

classify_schema_upgrade(stored, current)

Classify a fingerprint mismatch as an additive upgrade plan or a rejection.

Module Contents

httk.store.storage_layout.DECLARATION_PROTOCOL_VERSION: Final = '2'

The current backend-neutral declaration protocol.

The value is the major generation only, compared for strict equality on reopen and never parsed. Bump it to “3” solely on a breaking change to the declaration protocol that an existing store could not be reopened against.

httk.store.storage_layout.ADDITIVE_UPGRADE_HINT: Final = 'the schema difference is purely additive (new nullable columns / lazily created tables); reopen...

The reopen hint appended when an additive-only schema mismatch is not applied.

exception httk.store.storage_layout.StorageLayoutUpgradeRequiredError(diff, *, hint=None)

Bases: RuntimeError

A database does not exactly implement the current persisted store layout.

diff is immutable and JSON-shaped. Its top-level keys are stable categories (currently protocol, declaration and schema), so a caller can present a precise upgrade diagnostic without parsing the human-readable exception message. The declaration category maps named aspect keys (metadata_keys, store_timestamps, write_profile, entry_declaration) to their own diagnostics, so several independent declaration mismatches are reported together; the exception message names the mismatched aspects.

Parameters:
  • diff (collections.abc.Mapping[str, object]) – The immutable JSON-shaped category-keyed difference.

  • hint (str | None) – An optional remediation appended to the message and exposed as hint (e.g. that a purely additive schema change can be applied with upgrade=True).

diff: collections.abc.Mapping[str, object]
hint: str | None = None
exception httk.store.storage_layout.EntryLayoutBindingError

Bases: ValueError

A persisted application-owned layout needs explicit Python class bindings.

class httk.store.storage_layout.EntryRecordDeclaration

Bind one stable store-local name to a concrete record class.

Parameters:
  • name – Stable record identity persisted in the store declaration.

  • record – Concrete frozen dataclass used for storage and hydration.

  • definition_id – Optional entry-type definition IRI described by the record.

name: str
record: type
definition_id: str | None = None
class httk.store.storage_layout.EntryFamilyDeclaration

Declare one application-owned entry family without global registration.

Explicit declarations provide stable persistence identities directly to a store. They are intended for application-private families which should not participate in plugin discovery. The same declaration must be supplied whenever such a store is reopened.

Parameters:
  • name – Stable family identity persisted in the store declaration.

  • family – Logical entry-family class exposed through entry_layout.

  • records – Ordered concrete record declarations belonging to the family.

  • definition_id – Optional entry-type definition IRI for the family.

name: str
family: type
records: tuple[EntryRecordDeclaration, Ellipsis]
definition_id: str | None = None
class httk.store.storage_layout.EntryFamilyLayout

One immutable configured entry family and its concrete records.

name: str
family: type
definition_id: str | None
record_names: tuple[str, Ellipsis]
records: tuple[type, Ellipsis]
record_definition_ids: tuple[str | None, Ellipsis]
class httk.store.storage_layout.StorageLayout

The immutable normalized entry declaration of an initialized store.

protocol_version: str
families: tuple[EntryFamilyLayout, Ellipsis]
property entry_records: collections.abc.Mapping[type, tuple[type, Ellipsis]]

Configured family classes mapped to their ordered concrete record classes.

property declaration: collections.abc.Mapping[str, tuple[str, Ellipsis]]

Configured stable family names mapped to their ordered stable record names.

httk.store.storage_layout.validate_entry_id_fields(layout)

Require entry-id fields on every backing of a defined entry family.

httk.store.storage_layout.normalize_entry_records(entry_records)

Validate an explicit class declaration and replace it with stable registry names.

Registry aliases are rejected rather than selected arbitrarily: a persistent declaration must have exactly one stable spelling for every supplied class.

httk.store.storage_layout.normalize_entry_families(entry_families)

Validate application-owned entry declarations and build a store layout.

Unlike normalize_entry_records(), this path does not require the family or record classes to be globally registered. Stable names and optional definition identities are supplied by the application itself.

Parameters:

entry_families (collections.abc.Sequence[EntryFamilyDeclaration]) – Explicit family declarations in any order.

Returns:

The immutable normalized storage layout.

Raises:
  • TypeError – If the declaration container or its members are invalid.

  • ValueError – If names, classes, definitions, or storage schemas conflict.

Return type:

StorageLayout

httk.store.storage_layout.declaration_json(layout)

Serialize a normalized declaration in its exact deterministic persisted form.

httk.store.storage_layout.schema_fingerprint_json(layout)

Serialize the resolved per-table schema of layout in deterministic form.

The fingerprint covers every declared record class plus the transitive closure of referenced storable classes, resolved through resolve_schema(). It captures what determines the on-disk layout, the stored value encoding, and the content identity of each table — the logical identity name, dedup policy, composite indexes, relationship links, and per-field roles, codecs, shapes, columns, child tables, identity participation, and list-vs-tuple container — so that reopening a store whose record classes changed is rejected up front.

A code move or rename is safe only when the record pins an explicit identity_name (every shipped httk record does); without a pin the qualified class name is the content identity, so the move changes content_id and the store correctly refuses to open. cls and python_type themselves are excluded — the identity name and resolved columns capture everything the store depends on.

Parameters:

layout (StorageLayout) – The normalized storage layout to fingerprint.

Returns:

A deterministic sort_keys JSON document describing tables and definition-backed entry-id tables.

Return type:

str

httk.store.storage_layout.schema_fingerprint_diff(stored, current)

Diff a stored fingerprint against the current one, per differing table.

The persisted fingerprint shape is versioned by the store protocol, so this only needs to survive a corrupt stored value gracefully.

Parameters:
  • stored (str | None) – The persisted fingerprint JSON, or None when absent.

  • current (str) – The fingerprint recomputed from the persisted layout.

Returns:

A mapping of table name to {"expected", "actual"} for each table that differs; {} when the two fingerprints are byte-equal. A stored value that is not a parseable fingerprint yields a single "<fingerprint>" entry.

Return type:

dict[str, object]

class httk.store.storage_layout.AdditiveUpgradePlan

The nullable parent columns an additive fingerprint upgrade must add per table.

Parameters:

added_columns – Physical table name mapped to the ordered ColumnSpec values newly present in the current fingerprint. New tables carry no entry (they are created whole); a table appears only when it already exists in the stored fingerprint and gained one or more nullable parent columns.

added_columns: collections.abc.Mapping[str, tuple[httk.store.backend.schema.ColumnSpec, Ellipsis]]
httk.store.storage_layout.classify_schema_upgrade(stored, current)

Classify a fingerprint mismatch as an additive upgrade plan or a rejection.

The whole diff must be additive. A table present only in the current fingerprint is a new table (additive; it is created whole). A table present in both is additive only when its identity_name, dedup, composite_indexes and links are byte-equal, it carries no unrecognized top-level key, every stored field is present and byte-equal in the current fingerprint, and every added field is a non-child, non-derived, content-identity-excluded (IdentitySkip) field whose parent columns are all nullable. Identity participation is required so a pre-existing row’s content_id (and therefore dedup, dispatch, and federation identity) is unchanged by the upgrade.

Parameters:
  • stored (str | None) – The persisted fingerprint JSON, or None when absent.

  • current (str) – The fingerprint recomputed from the persisted layout.

Returns:

An AdditiveUpgradePlan when fully additive, otherwise a human-readable rejection reason naming the offending table/field/column.

Return type:

AdditiveUpgradePlan | str