orthonym.assembly.name_tree_to_string#
Note
Internal API. Names and behaviour may change between releases.
a phase Pass-2 serializer (DECOMP-02 + internal notes).
Single source of truth for NameTreeNode -> str going forward. Plan-02
substrate ships this module ALONGSIDE the legacy _assemble_fragments
path at composer.py:7591 — the legacy path STAYS until commit 03-10 thinning
per internal notes incremental-migration discipline. Plan-02/03 first-wave
handlers return NamingResult(name=<existing string>, tree=None,...);
the legacy path is what actually produces those names. The Pass-2
serializer here is exercised by --dump-tree integration tests in Plan-04
and by + handlers that populate trees explicitly.
Contract per internal notes + internal notes-DECOMP.md:
If
node.fragment_legacy is not None: delegate to the legacy assembly path by wrapping the fragment list as_assemble_fragments([node.fragment_legacy], style). This is the first-wave compatibility mode that lets a phase ship byte-identical even before any handler emits a real tree.If
node.fragment_legacy is NoneANDnode.parent_stemis populated: assemble the name from the explicit fields in the order:stereo + prefixes + indicated_h + parent_stem + locants + unsaturation_locants + suffix
- applying multiplicative-prefix and parenthesization rules per
+.
If
node.fragment_legacy is NoneANDnode.parent_stem == "": raise an explicitNameTreeSerializerError— this is a malformed tree per DECOMP-02 honest-fail-on-data.
Byte-identical contract (DECOMP-03): for every input node whose
fragment_legacy round-trips through the legacy path, the output of
name_tree_to_string(node, style) MUST equal the corresponding
_assemble_fragments(fragments, style) output.
Anti-pattern hygiene: - -05 / -23: no postprocessor band-aid on inner-dispatch
output; the explicit-field branch below assembles deterministically from the IR fields.
-27: NameTreeNode field shape is locked — adding new fields to the serializer here without a corresponding NameTreeNode field addition is a Rule 4 architectural decision.
References:
- internal notes-DECOMP.md — Pass-2 serializer contract.
- 160-internal notes / Question 3 — explicit two-pass IR + serializer.
- internal notes — analog: composer.py:7591-7748 _assemble_fragments.
- internal notes § “Name-Tree IR Semantic Contract” — collision detection
per IUPAC; suffix-prefix locant priority; unsaturation infix.
- orthonym.assembly.name_tree_to_string.name_tree_to_string(node, style='pin')#
a phase + IUPAC Pass-2 serializer.
Composition order:
[stereo] + [prefixes (alpha-sorted; recursively serialized)] + [parent_stem] + [indicated_h] + [unsaturation_infix] + [suffix]
- Parameters:
node (NameTreeNode) – The NameTreeNode to serialize.
style (str) – Naming style (“pin”, “general”, “cas”); forwarded to the legacy assembler when
fragment_legacyis set.
- Returns:
IUPAC name string per internal notes byte-identical contract.
- Raises:
NameTreeSerializerError – if
node.parent_stem == ""ANDnode.fragment_legacy is None(malformed tree per DECOMP-02 honest-fail-on-data).- Return type:
str
- exception orthonym.assembly.name_tree_to_string.NameTreeSerializerError#
Bases:
ValueErrorRaised when a malformed NameTreeNode is passed to name_tree_to_string.
Per DECOMP-02 honest-fail-on-data (internal notes): a node with
parent_stem == ""andfragment_legacy is Nonecannot serialize deterministically. The fix is upstream in the handler that produced the malformed node, not a band-aid string default.