moczarr.products
Multi-product store roots (zagg D19, mortie spec §6.5), read side.
A multi-product store is a directory of stores: each product lives under
its own human-readable root prefix {name}/, and a product subtree is a
complete, unmodified morton-hive store (bare-named manifest, MOC, digit
tree). A bare single-product store — the manifest at the store root, today's
common form — remains fully valid; readers distinguish the two root forms
by content (spec §6.5): a manifest at the root ⇒ bare store; name-shaped
prefixes ⇒ product directory.
Product names are constrained by the §6.5 grammar ([a-z0-9_-]{1,192},
URL-safe by construction) with the base-component exclusion: a name must
never match the morton base-component grammar -?[1-6], so the walker's
child classification stays unambiguous.
Discovery is one delimiter-LIST of the store root, one manifest GET per
name-shaped child, and one HEAD per product (the aggregation.yaml
probe) — the enumeration recipe the design fixes for every viewer
("list {store_root}/ and read each {name}/morton_hive.json directly —
no name↔hash translation layer") — so one product this reader is too old for
must never take down the enumeration: an unsupported-but-well-formed spec is
listed with manifest: None, not raised on. The D19 semantic_hash
(sha256 of the product's canonical semantic core) is surfaced from the
manifest, and the presence of the derived aggregation.yaml convenience
copy is probed —
the hash is truth; the YAML is a regenerable cache (D9 class).
is_product_name(name)
Whether name is a legal §6.5 product name (grammar + exclusion).
Source code in src/moczarr/products.py
56 57 58 | |
list_products(store_root, *, store=None, **store_kwargs)
Enumerate the named products of a multi-product store root (D19).
One delimiter-LIST of the root, then one {name}/morton_hive.json GET
per name-shaped child, then one HEAD per product for the
has_aggregation_yaml probe — two sequential round-trips per product,
measured on the committed fixture (1 LIST + 5 GET + 4 HEAD for its four
products plus the non-product scratch/ child). Bounded, but stated
because list_products is a per-page-load call for a viewer. Returns
one record per product, sorted by name::
{
"name": ..., # the {name}/ prefix (§6.5 grammar)
"spec": ..., # manifest spec string
"dataset": ..., # manifest dataset block (or None)
"semantic_hash": ..., # D19 identity hash (or None, pre-D19)
"has_aggregation_yaml": ., # derived semantic-core copy present?
"manifest": ..., # the full parsed manifest (None: the
# spec is one this reader can't open)
}
A bare single-product store (a manifest at the root — the §6.5
content discrimination) returns []: the degenerate form has no
named products; open it directly. Name-shaped children without a
manifest are skipped with a debug log (a prefix is a product iff it
carries one); a present-but-malformed product manifest raises, the
:func:moczarr.store.read_manifest posture — a manifest is a bootstrap,
never a degradable cache.
A product whose manifest carries a well-formed but unsupported hive
spec (morton-hive/3 and up — a store this reader is too old for, not
a broken one) is neither raised on nor hidden: it is listed with
manifest: None (and dataset/semantic_hash None) plus a
warning, so a caller sees the product exists and why it can't be opened.
Raising instead would make one newer sibling erase every readable
product from an enumeration that D19 makes a viewer-facing contract.
Opening it is still loud — open_hive(root, product=...) raises the
spec error from the manifest bootstrap.
Source code in src/moczarr/products.py
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 | |
validate_product_name(name)
Validate a product name against the §6.5 grammar; returns it.
Source code in src/moczarr/products.py
61 62 63 64 65 66 67 68 69 | |