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.ContextVar holds 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 and verify mode 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 only).

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.

exception orthonym.assembly.memo.MemoMismatch(namespace, key, stored, fresh)#

Bases: Exception

Raised in verify mode 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 None when a scope already existed (a nested re-entry – it shares the outer cache and must NOT reset it).

orthonym.assembly.memo.pop_scope(token)#

Tear down the scope created by the matching:func:push_scope. A None token (nested re-entry) is a no-op, so only the outermost frame tears down.

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), or None.

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 key for the LIFE OF THE PROCESS.

Same contract as:func:cache_or_compute except that the entry outlives the naming scope. Bounded LRU (ORTHONYM_PROCESS_CACHE entries, default 20,000; 0 disables). ORTHONYM_MEMO=off/verify bypass 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 via compute_fn on a miss.

  • ORTHONYM_MEMO=off OR no active scope -> always compute_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.