orthonym.assembly.handlers.ester_family#

Note

Internal API. Names and behaviour may change between releases.

a phase Plan-06 composite handler: ester family (polyfunctional → multi_ester → ester).

Per internal notes (gap-closure) + -02 Option A: encodes the multi-try-on-None cascade for the 3 ester-family handlers as a SINGLE composite handler so the dispatch_inner first-match-wins interface (internal notes) stays untouched.

LIFT SOURCE: composer.py:859-949 (verbatim, with return pool.best.name replaced by return NamingResult(name=pool.best.name, tree=<coarse NameTreeNode>, atom_to_locant_hint=None) — a phase SCORE-01 attaches a coarse tree at every return site).

Internal cascade order (preserved from composer.py inline body): 1. polyfunctional (if features.is_polyfunctional) 2. multi_ester (if features.principal_group == ‘ester’ AND >= 2 ester matches)

  • dicarboxylic_diester / polyol_polyester / independent sub-classifications

  1. ester (if features.principal_group == ‘ester’ AND ester_match)

Returns None if ALL three sub-paths fall through (the surrounding dispatch_inner loop then continues to the next handler — typically the Tier-A ring cascade composite).

IUPAC cite: (ester family); (polyfunctional); (diester).

orthonym.assembly.handlers.ester_family.name_ester_family(features, mol=None, style='pin')#

Composite handler: tries polyfunctional → multi_ester → ester in order.

Returns the FIRST sub-path’s NamingResult; returns None if all fall through.

Byte-identical preservation per DECOMP-03: each sub-path’s pool.add call is preserved verbatim from composer.py:859-949 (same handler_id, same features argument, same return type semantics).

Per a phase / -04 handler contract: when pool.add accepts a candidate but pool.best returns None (pool rejected the candidate per quality threshold / wildcard / ratio-floor), the handler raises AttributeError verbatim mirroring the pre-Plan-03-01 inline cascade behavior at composer.py:875 (return pool.best.name).

The AttributeError propagates UN-WRAPPED through dispatch_inner per the a phase exception list in dispatch_inner; namer’s broad except (TypeError, KeyError, IndexError, AttributeError) at namer.py:1888 catches it and falls through to _descriptive_fallback (wildcard-bearing molecules) OR — when called recursively from the decomposition engine — signals “this fragment is unnameable; try another decomposition strategy.”

This is the SAME exception flow as the pre-amendment inline cascade at composer.py:875 (return pool.best.name); it is NOT a new behavior. The cascade-removal sweep (Plan-03-01) keeps the exception propagation path that decomposition engine + namer.name_compound’s broad except both depend on.