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
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.