orthonym.errors#
Note
Internal API. Names and behaviour may change between releases.
Named limit/error catalog for out-of-scope inputs (, a phase).
Orthonym’s default posture is always-emit: it returns a name (or a descriptive fallback string) for every input. That reaches inputs a refuse-when-unsure system would decline, but it cannot tell a caller whether a result is a confident name or a plausible-but-wrong guess for something Orthonym genuinely cannot handle (the C failure mode — e.g. a bare atom or a wildcard structure named as if it were a real molecule).
This module adds AUTONOM-style named limit codes so a caller can distinguish “can’t handle” from “got it wrong” WITHOUT changing the default always-emit behaviour. The structured OrthonymLimitError is surfaced only via opt-in paths (Orthonym.name(…, raise_on_limit=True), name_with_confidence[‘limit’], and classify_limit); the default string path is byte-identical to before.
Provenance (each code cites the AUTONOM analog it mirrors): internal notes ` (“Hard limits & error catalog”): 40 codes in 0x104–0x18f, limits table (125 total atoms / 44 per chain·ring·assembly / 32 stem candidates / 2 components / 255 chars), representative refusals (out of organic range), (radicals), (bare atom: [H]/[Na+]), (inorganic: O/N), (unidentified FG), (atoms not assignable).
- exception orthonym.errors.OrthonymLimitError(code, message, design_note_ref=None, smiles=None)#
Bases:
ExceptionThe reason a structure is out of scope.
Returned by:func:orthonym.classify_limit, and raised when you ask for it with
raise_on_limit=True. It tells “cannot handle this structure” apart from a name.- Variables:
code (str) – The reason code:
WILDCARD_ATOMS,UNSUPPORTED_ELEMENT,ISOLATED_ATOM,STRUCTURE_TOO_LARGE,UNSUPPORTED_RING_SYSTEM,UNNAMEABLEorNO_VERIFIED_PIN(the default tier built a name but not a verified preferred IUPAC name; a wider tier returns it).message (str) – The label the plain call returns in place of a name.
design_note_ref (str or None) – A reference to the design note the code follows.
smiles (str or None) – The input, when known.
Examples
>>> from orthonym import Orthonym, OrthonymLimitError >>> try: ... Orthonym.name("O=[U](=O)=O", raise_on_limit=True) ... except OrthonymLimitError as err: ... print(err.code, "|", err.message) UNSUPPORTED_ELEMENT | inorganic compound (not supported)
- as_dict()#
- orthonym.errors.unsupported_ring_system(smiles=None)#
Build the G0 fail-closed refusal for a complex ring system Orthonym cannot yet name correctly (DD7 S1). Raised mid-assembly by the von-Baeyer / bicyclo / polycomponent-fusion paths; caught once at
Orthonym.name(default path returns.message= ‘unknown organic compound’;raise_on_limit=Truere-raises this error).
- orthonym.errors.no_verified_pin(message, smiles=None)#
The default tier’s decline of a name that is not a verified PIN and not one of its exceptions (
Orthonym.namewithraise_on_limit=True).messageis the label the plain call returns for the structure.
- orthonym.errors.unsupported_element_branch(symbol, smiles=None)#
Fail-closed refusal for a substituent branch that Orthonym cannot name and that carries an element outside
_ORGANIC_ELEMENTS.Raised mid-assembly and caught at the single
Orthonym.namecatch point, exactly likeunsupported_ring_system. It exists because SKIPPING such a branch is not a smaller error than mis-naming it — it ships a name for a DIFFERENT molecule: with the refusal sentinel no longer leaking through as a string,CCS[Zn]SCCwent from the visibly-broken ‘zinc compound (not supported)ylethane’ to the plausible and therefore far more dangerous ‘ethane’, which no failure predicate can flag.The message names the element when known, so the refusal stays as informative as the descriptive fallback it mirrors.
- exception orthonym.errors.UnnameableSubstituentError(detail='')#
Bases:
ExceptionA branch of the CANDIDATE being built has no correct substituent name.
Raised by a prefix producer INSTEAD of writing a failure sentinel into the prefix. The old convention wrote the literal
'unknown'as the prefix text (composer._generate_ring_substituent_prefixes), and the handler then welded it into a real-looking name –(2E)-2-methyl-5-unknownpent-2-enoic acid– whichOrthonym.nameandname_tieredshipped whenever the OPSIN validity gate was off.The consumer voids that CANDIDATE, never the molecule.
dispatch_innertreats the signal as the handler’s gate-fail (-04: the next handler is tried);assemble_namereturns its documented empty result when the signal escapes the inline cascade or the general_acyclic safety net; and a consumer with its own decline value returns that (name_polyfunctionalNone,name_quaternary_aminium‘’). The whole-molecule failure path – the general engine lane and every late rescue inOrthonym.name– then runs exactly as it did for the welded string (all of it keyed onis_failure_name, which reads ‘’ and the welded string alike).Deliberately NOT an
OrthonymLimitError(that one aborts_name_impland skips the late rescues) and NOT aValueError(name_compoundre-raisesValueErroras “invalid SMILES”).
- orthonym.errors.is_refusal_sentinel(name)#
True if
nameis any refusal sentinel and so must never be CONSUMED as a name component (a substituent prefix, a parent stem, an ester word…).This is the slot-level predicate. It is deliberately built ON TOP of
is_failure_namerather than beside it: that function already recognises three of the four sentinel families exactly (empty,'unknown...', and the'... (not supported)'descriptive fallbacks), and duplicating them is how this class of bug reached six copies in the first place. What it does NOT recognise is the substituent cascade’s bare'substituent'placeholder, because that string is not a whole-molecule failure signal — a caller asking “did naming fail?” of a finished name must not be told yes merely because the word appears. Hence one extra leg here, and no re-implementation.The failure this closes: a sentinel accepted into a substituent slot is silently welded into a name —
CCS[Zn]SCCproduced'zinc compound (not supported)ylethane', which every “did I get a non-empty string?” caller reads as success.⚠ The placeholder leg is a SUBSTRING test, not equality. Measured 2026-08-04 over the 10,000-row head-to-head: exact equality missed every decorated occurrence, because the placeholder reaches a slot check with a locant or italic element prefix already attached —
'N-substituentformamide','N-substituenthydroxyphosphonooxytricos…','…-3-amino-sulfanyl-N-substituentpropanamide'. 310 rows shipped such a name. Widening is safe by measurement, not by assumption: 0 of the 3,616 round-tripping names in that corpus contain the substring, so the predicate cannot fire on a name known to be correct (thecheck-targetbar). The other three families were already substring-matched byis_failure_name(), which is why only this leg leaked.
- orthonym.errors.classify_scope_limit(mol)#
Pre-naming structural refusal — ONLY for zero-false-positive classes.
The sole provably-safe pre-check is a wildcard (*, atomic number 0): no definite molecule contains one and the namer would silently drop it (CC*->”ethane”). Everything else (bare atoms, metals, oversize) is left to classify_failure_limit, which keys off an actual naming failure and so can never refuse an input Orthonym does in fact name (e.g. [H][H]->molecular hydrogen, sodium salts).
- orthonym.errors.classify_failure_limit(mol, smiles=None)#
Map an already-failed input to a named limit code.
Returns a code that explains why Orthonym could not produce a real name. The
.messageis byte-identical to the legacy_descriptive_fallbackstring for that branch (so the default always-emit output never changes); the.code/.design_note_refadd the new “can’t handle” signal. The richer detail for the size/isolated branches lives in the code, not the message, precisely to preserve the legacy strings.