Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 37 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,19 @@ Optional extras:
pip install "openclatura[opsin,datasets]"
```

The default install does **not** include OPSIN verification. Install the
`[opsin]` extra and make sure Java 8+ is available if you want round-trip
verification through OPSIN:

```bash
pip install "openclatura[opsin]"
java -version
```

If `py2opsin` is installed but Java is missing or inaccessible, OPSIN
verification is skipped gracefully. The name generation still succeeds and the
verification status is reported as `skipped_no_java`.

## Quick start

```python
Expand Down Expand Up @@ -59,6 +72,13 @@ result.opsin_check.status # 'matched' | 'mismatched' | 'skipped_no_java' | ...
result.verified # True when opsin_check is matched
```

`verify_opsin` defaults to `False`. When set to `True`, verification is
best-effort and does not raise if OPSIN support is unavailable:

- no `py2opsin` installed: `result.opsin_check.status == "skipped_no_opsin"`
- `py2opsin` installed but Java unavailable: `status == "skipped_no_java"`
- OPSIN parses and round-trips: `status == "matched"` or `"mismatched"`

Errors do not raise — they are captured on `result.error`, which makes
the batch API safe to point at noisy datasets:

Expand Down Expand Up @@ -90,12 +110,27 @@ openclatura name "CC(=O)Nc1ccccc1" # → N-phenylacetamide
openclatura name "CC(=O)Nc1ccccc1" --json # JSON with trace + rules
openclatura batch smiles.txt --output names.jsonl --processes auto
```
The CLI tool has OPSIN verification turned on by default. It can be turned off with

The CLI verifies with OPSIN by default when possible. This is different from the
Python API, where `verify_opsin=False` by default. Disable CLI verification with
`--no-verify`:

```bash
openclatura name "CN1C=NC2=C1C(=O)N(C(=O)N2C)C" --no-verify
openclatura name "CC(=O)Nc1ccccc1" --no-verify
```

If OPSIN support is unavailable, the command still prints the generated name and
reports the verification status:

```text
N-phenylacetamide
opsin: skipped_no_java
```

Other possible skipped statuses include `skipped_no_opsin` when `py2opsin` is
not installed. Install `openclatura[opsin]` and Java 8+ for full CLI
verification.

### Natural-language description (`describe`)

`openclatura.describe(smiles)` walks the same trace and renders a
Expand Down
File renamed without changes.
10 changes: 5 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ build-backend = "hatchling.build"

[project]
name = "openclatura"
version = "0.1.0"
version = "0.1.1"
description = "Deterministic SMILES-to-IUPAC name generator based on the IUPAC Blue Book"
readme = "README.md"
requires-python = ">=3.10"
requires-python = ">=3.11"
license = "MIT"
authors = [
{ name = "Adrian Mirza" },
Expand Down Expand Up @@ -59,9 +59,9 @@ dev = [
]

[project.urls]
Homepage = "https://github.com/lamalab-org/iupac-name-generator"
Repository = "https://github.com/lamalab-org/iupac-name-generator"
Issues = "https://github.com/lamalab-org/iupac-name-generator/issues"
Homepage = "https://github.com/lamalab-org/openclatura"
Repository = "https://github.com/lamalab-org/openclatura"
Issues = "https://github.com/lamalab-org/openclatura/issues"

[project.scripts]
openclatura = "openclatura.cli:main"
Expand Down
4 changes: 2 additions & 2 deletions src/openclatura/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ def _build_parser() -> argparse.ArgumentParser:
"--verify",
action=argparse.BooleanOptionalAction,
default=True,
help="round-trip via OPSIN or --no-verify to skip",
help="round-trip via OPSIN; use --no-verify to skip",
)
p_name.set_defaults(func=_cmd_name)

Expand All @@ -134,7 +134,7 @@ def _build_parser() -> argparse.ArgumentParser:
"--verify",
action=argparse.BooleanOptionalAction,
default=True,
help="round-trip via OPSIN or --no-verify to skip",
help="round-trip via OPSIN; use --no-verify to skip",
)
p_batch.add_argument(
"--processes",
Expand Down
15 changes: 14 additions & 1 deletion src/openclatura/opsin_verify.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

import shutil
import subprocess
import warnings
from dataclasses import dataclass
from typing import Literal

Expand Down Expand Up @@ -63,9 +64,21 @@ def to_dict(self) -> dict:
}


def _import_py2opsin():
import py2opsin

return py2opsin


def _try_import_py2opsin():
try:
import py2opsin
with warnings.catch_warnings():
# py2opsin checks Java at import time and may warn before callers
# can ask openclatura for the structured skipped_no_java status.
# Suppress RuntimeWarning broadly inside this optional import only.
warnings.filterwarnings("ignore", category=RuntimeWarning)
warnings.filterwarnings("ignore", message=r".*(Java|py2opsin).*", category=Warning)
py2opsin = _import_py2opsin()
except Exception: # pragma: no cover - optional dependency
return None
return py2opsin
Expand Down
20 changes: 20 additions & 0 deletions src/openclatura/tests/test_public_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import json
import subprocess
import sys
import types
import warnings

import pytest

Expand Down Expand Up @@ -231,6 +233,24 @@ def test_engine_run_with_verify_opsin_skips_gracefully_without_java(monkeypatch)
assert result.opsin_check.status == "skipped_no_java"


def test_py2opsin_java_import_warning_is_suppressed(monkeypatch):
import openclatura.opsin_verify as ov

def fake_import_py2opsin():
warnings.warn("Java may not be installed/accessible", RuntimeWarning, stacklevel=2)
return types.SimpleNamespace(py2opsin=lambda names: [])

monkeypatch.setattr(ov, "_import_py2opsin", fake_import_py2opsin)
monkeypatch.setattr(ov, "_java_available", lambda: False)

with warnings.catch_warnings(record=True) as caught:
warnings.simplefilter("always")
check = ov.verify_with_opsin("ethanol", "CCO")

assert check.status == "skipped_no_java"
assert not [warning for warning in caught if "Java may not be installed/accessible" in str(warning.message)]


def test_naming_engine_is_reusable():
engine = NamingEngine()
a = engine.run(NamingRequest(smiles="CCO"))
Expand Down
97 changes: 0 additions & 97 deletions test_opsin_qm9_batch.py

This file was deleted.

Loading