orthonym.validation.opsin_grammar#

Note

Internal API. Names and behaviour may change between releases.

OPSIN-grammar pre-validator (a phase).

OPSIN-XML-driven authoritative validator + round-trip-gated suggest_fix. Layered ON TOP OF a phase format_validator heuristics per internal notes.

Architecture (Plan-02 deliverable):
  • Module-import-time XML loader : parses opsin/…/regexTokens.xml, expands %name% placeholders to fixed point, and compiles each into a Python re.Pattern. Loud ImportError on adapter mismatch — never silent degradation .

  • OpsinGrammar.validate(name) -> bool: hot-path API. Layer- cake : format_validator pre-screen first, then OPSIN-XML strict checks for {bracket, hyphen, stereo} surfaces.

  • OpsinGrammar.suggest_fix(name, source_smiles=None) ( LOCKED signature, name FIRST, source_smiles SECOND): tries the bounded repair tables from internal notes in order [bracket, hyphen, stereo], gates each candidate through validate AND opsin_roundtrip_check(smiles, candidate) (SMILES-first inside the oracle), and returns (repaired, repair_class) on success or (None, None) otherwise. One repair attempt only — no retry loop .

  • Per-instance _stats counter (,) — never module- global mutable state.

Anti-pattern hygiene (internal notes):
/: no _postprocess_name/re.sub chains outside the

explicit _suggest_* table.

: every _suggest_* regex literal cites its internal notes

row.

: repairs only re-position / re-bracket / re-hyphenate;

never change parent/locants/substituents.

: _load_opsin_token_regexes raises ImportError on miss. : round-trip oracle is (smiles, name). : read result[“passed”], never truthy-check the dict. : no fictitious “fast OPSIN call” timing claims; the

verified empirical figure on this host is ~1269ms mean.

: default jar_version=”2.9.0”; never assume an older JAR. : per-instance counter only. : no RDKit imports inside _suggest_*.

class orthonym.validation.opsin_grammar.OpsinGrammar(stats=None)#

Bases: object

OPSIN-grammar pre-validator with bounded round-trip-gated repair.

Three responsibilities (internal notes <domain>):
  1. validate(name) -> bool — fast OPSIN-XML-driven check across three surfaces (bracket nesting, stereo position, hyphen placement). Layered ON TOP OF a phase’s format_validator.validate_name_format heuristic pre-screen per.

  2. suggest_fix(name, source_smiles=None) — bounded repair function with the LOCKED signature (name FIRST). Each repair candidate is gated through validate AND opsin_roundtrip_check(smiles, candidate) (SMILES-first inside the oracle). One attempt per repair class; NO retry loop (/).

  3. Per-instance telemetry via self._stats and get_validation_stats.

Construction: OpsinGrammar (or OpsinGrammar(stats=shared_dict) to share counters with an outer container per ref-pass pattern).

STAT_KEYS: Tuple[str, ...] = ('validate_passed', 'repair_succeeded_bracket', 'repair_succeeded_stereo', 'repair_succeeded_hyphen', 'repair_failed_validate', 'repair_failed_roundtrip', 'no_repair_offered')#
validate(name)#

Fast OPSIN-grammar pre-validation. Returns True on pass.

suggest_fix(name, source_smiles=None)#

Bounded round-trip-gated repair (LOCKED signature).

Parameters:
  • name (str) – The (validate-failing) IUPAC name to repair. FIRST positional arg per.

  • source_smiles (str | None) – Original SMILES for the round-trip gate. When None the round-trip gate is replaced by a validate-only check and an INFO log records the degraded path .

Returns:

(repaired_name, repair_class) if a repair fired AND re-validates AND (when source_smiles is given) round- trips through OPSIN. Otherwise (None, None).

Return type:

Tuple[str | None, str | None]

Notes

  • Tries each repair class once in deterministic order [bracket, hyphen, stereo]. NO retry loop (/).

  • The round-trip oracle is called as opsin_roundtrip_check(source_smiles, candidate) — SMILES-first per. The (smiles, name) arg order to the ORACLE is the OPPOSITE of suggest_fix’s own signature; this is the most common confusion source in the codebase and the grep gate enforces it.

get_validation_stats()#

Return a defensive copy of the per-instance counters .

last_repair_class()#

Diagnostic accessor: most recent successful repair class.

orthonym.validation.opsin_grammar.opsin_grammar_validate(name)#

Module-level convenience wrapper around OpsinGrammar.validate.

Backed by an internal singleton with its own _stats counter (NOT shared with any Orthonym instance per).

orthonym.validation.opsin_grammar.opsin_grammar_suggest_fix(name, source_smiles=None)#

Module-level convenience wrapper around OpsinGrammar.suggest_fix.

Return-shape divergence: OpsinGrammar.suggest_fix returns Tuple[Optional[str], Optional[str]] (per LOCKED); this helper drops the repair-class slot and returns Optional[str] only, for backward-compatible callers that just want the repaired name. Use the class API directly when the repair class is needed.