orthonym.assembly.memo#
Note
Internal API. Names and behaviour may change between releases.
Scoped-per-call memoization infrastructure (M1, Levers C1 + E).
A byte-identity-preserving cache whose lifetime is exactly ONE top-level naming
call. The engine re-names the same substituent fragments many times inside one
molecule – the dispatch cascade, the _retry_cascade_on_gate_rejection
re-entries and the recursive Tier-4 cascade all re-derive the same fragment
prefixes – so a per-call cache removes that redundant work without changing any
emitted name. See internal notes Part 2.
Design invariants (each is load-bearing for the 0-wrong / byte-identity contract):
Scope is per top-level call. A
contextvars.ContextVarholds the cache dict; it is created at the outermost naming entry and torn down on exit, so cross-molecule staleness, tier drift and env-flag changes are structurally impossible and memory stays bounded.Fail-open, never fail-wrong. With no active scope (or
ORTHONYM_MEMO=off) every call recomputes. A cache MISS can never corrupt output; only a false HIT could, and that is exactly what a COMPLETE key andverifymode prevent.``verify`` mode is the continuous completeness check. It always recomputes and raises:class:MemoMismatch the instant a stored value disagrees with a fresh one for the same key – turning an incomplete key into a loud failure rather than a silent wrong name.
Dependency-light on purpose (contextvars + os and the provenance context variables).
- orthonym.assembly.memo.verify_mismatch_count()#
Number of verify-mode same-key-different-result events since the last reset.
- orthonym.assembly.memo.reset_verify_mismatches()#
Clear the verify-mode mismatch log (call before a validation corpus run).
- orthonym.assembly.memo.pin_promotion_var = <ContextVar name='orthonym_pin_promotion' default=False>#
True while the default (PIN) tier re-names a molecule it could not name, with the best-effort ring-substituent producers admitted under the PIN-vocabulary guard (
rules.pin_vocabulary.promote_at_pin_tier;Orthonym.name). Values computed in that re-run can differ from the first run’s for the same key, so every cache below keys them apart: neither run is ever served the other’s value, and the first run stays byte-identical to the engine without the re-run.
- orthonym.assembly.memo.prefixes_apart_var = <ContextVar name='orthonym_prefixes_apart' default=False>#
True while an isotope-labelled molecule’s skeleton is named with its identical N- and C-prefixes cited apart (
composition_primitives. identical_prefixes_cited_apart). Its values differ from the grouped run’s for the same key, so the caches below key them apart, as for the promotion re-run.
- orthonym.assembly.memo.book_forms_var = <ContextVar name='orthonym_book_forms' default=True>#
True (the default) while the writers spell the Blue Book’s forms (
assembly.book_prefixes.book_forms_enabled);book_prefixes.mechanical_formssets it False for a run that wants the spellings before N5. Some of those runs share one memo scope with the book spellings (the terminal-fragment writer’s mechanical pre-pass, the assembly tier’s von Baeyer gate), and their values differ for the same key, so the caches below key them apart, as for the promotion re-run.- Type:
Roadmap N5
- orthonym.assembly.memo.kept_forms_var = <ContextVar name='orthonym_kept_forms' default=frozenset()>#
The kinds of book spelling kept inside
book_prefixes.mechanical_forms(keep_forms=...): every writer gives its mechanical spelling except those of the kept kinds (‘ring’: the single-ring names ofrules.monocycle_forms). A middle rung of the retry that follows a book-spelled name no round trip confirmed: one failing spelling must not take the kept kinds with it.
- orthonym.assembly.memo.dropped_forms_var = <ContextVar name='orthonym_dropped_forms' default=frozenset()>#
every writer gives its book spelling except those of the dropped kinds (‘chain’: the N/O group prefixes and ‘cyano’). The rung that follows a book-spelled name no round trip confirmed when the failing spelling is one kind: the other book spellings stay.
- Type:
The kinds of book spelling switched OFF inside
book_prefixes.book_forms_minus(kinds)
- orthonym.assembly.memo.suffix_nitrogen_hydron_var = <ContextVar name='orthonym_suffix_nitrogen_hydron' default=False>#
True while a neutral amine is named for the cation its caller makes of it by a hydron on the nitrogen (
locant_omission.suffix_nitrogen_gains_hydron): the locant of a group on a one-carbon parent is decided as if the nitrogen held that hydrogen, so a value computed in that scope differs from the plain one for the same key and the caches below key them apart, as for the promotion re-run.
- exception orthonym.assembly.memo.MemoMismatch(namespace, key, stored, fresh)#
Bases:
ExceptionRaised in
verifymode when a cached result disagrees with a fresh recompute for the same key – i.e. the cache key is INCOMPLETE and a hit would have shipped a wrong name. Carries the offending namespace/key and both values so the missing key component can be identified.
- orthonym.assembly.memo.push_scope()#
Open a memo scope for this (and nested) calls, unless one is already open.
Returns a ContextVar token when THIS call created the scope (the caller owns teardown and must pass the token to:func:pop_scope), or
Nonewhen a scope already existed (a nested re-entry – it shares the outer cache and must NOT reset it).A scope opened inside:func:seeded_scopes starts with the entries it was given (see there), and one opened inside:func:recording_scopes is handed to the recorder.
- orthonym.assembly.memo.pop_scope(token)#
Tear down the scope created by the matching:func:push_scope. A
Nonetoken (nested re-entry) is a no-op, so only the outermost frame tears down.
- orthonym.assembly.memo.fresh_scope()#
An EMPTY copy of the open scope for a computation whose ambient declarations differ from the enclosing one’s (a scope’s caches are keyed on the assumption that they stay what they were when it opened:
locant_omission.isotopic_naming_scope). Returns a token for:func:pop_sandbox; with no scope open nothing changes (no caching, as before).
- orthonym.assembly.memo.recording_scopes()#
Collect every top-level memo scope (see:func:push_scope) opened in the body, in the order opened, in the list it yields. The scopes stay readable after their naming has ended.
The entries of
scopesthat another naming of the molecule may start with (see_SHAREABLE_NAMESPACES), added tointo: the stored values, and for each the side record the nested-call memo replays on a hit (side_put).
- orthonym.assembly.memo.seeded_scopes(entries)#
Every top-level memo scope opened in the body starts with a copy of
entries(shareable_entries()).
- orthonym.assembly.memo.push_sandbox()#
Open a throwaway COPY of the current scope for a speculative computation.
Reads still hit what the enclosing scope already holds; writes land in the copy and are discarded by:func:pop_sandbox, so the enclosing naming can never be served a value the speculation computed in its own context. With no scope open, nothing changes (no caching, as before). Always returns a token for:func:pop_sandbox.
- orthonym.assembly.memo.pop_sandbox(token)#
Discard the sandbox opened by the matching:func:push_sandbox.
- orthonym.assembly.memo.side_get(namespace, key)#
Return the scope-bound side value for
(namespace, key), orNone.
- orthonym.assembly.memo.side_put(namespace, key, value)#
Store a scope-bound side value for
(namespace, key)(no verify compare). A no-op with no active scope (fail-open: no scope -> no hit -> no replay).
- orthonym.assembly.memo.process_cache_stats()#
{"size", "maxsize", "hits", "misses"}for the process-wide pure cache.
- orthonym.assembly.memo.clear_process_cache()#
Drop every process-wide entry (tests; a worker that changes engine flags).
- orthonym.assembly.memo.pure_cache_or_compute(namespace, key, compute_fn)#
Memoise a value that is a pure function of
keyfor the LIFE OF THE PROCESS.Same contract as:func:cache_or_compute except that the entry outlives the naming scope. Bounded LRU (
ORTHONYM_PROCESS_CACHEentries, default 20,000;0disables).ORTHONYM_MEMO=off/verifybypass it exactly as they bypass the scope cache. The caller guarantees the key determines the value.
- orthonym.assembly.memo.cache_or_compute(namespace, key, compute_fn)#
Return the memoized value for
(namespace, key), computing it viacompute_fnon a miss.ORTHONYM_MEMO=offOR no active scope -> alwayscompute_fn(fail-open; never caches).on-> return the cached value if present, else compute + store + return.verify-> ALWAYS recompute; if a value is already stored for the key and differs, raise:class:MemoMismatch; then store + return the fresh value.