openclatura 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- openclatura-0.1.0/.dockerignore +21 -0
- openclatura-0.1.0/.github/workflows/ci.yml +123 -0
- openclatura-0.1.0/.github/workflows/docker-image.yml +57 -0
- openclatura-0.1.0/.gitignore +68 -0
- openclatura-0.1.0/.pre-commit-config.yaml +18 -0
- openclatura-0.1.0/Dockerfile +55 -0
- openclatura-0.1.0/LICENSE +21 -0
- openclatura-0.1.0/PKG-INFO +241 -0
- openclatura-0.1.0/README.md +196 -0
- openclatura-0.1.0/docker/compose.yaml +14 -0
- openclatura-0.1.0/eval_failures/pubchem/all_failures.csv +5445 -0
- openclatura-0.1.0/eval_failures/pubchem/failures_seed_17.csv +1143 -0
- openclatura-0.1.0/eval_failures/pubchem/failures_seed_42.csv +1095 -0
- openclatura-0.1.0/eval_failures/pubchem/failures_seed_5.csv +1080 -0
- openclatura-0.1.0/eval_failures/pubchem/failures_seed_63.csv +1081 -0
- openclatura-0.1.0/eval_failures/pubchem/failures_seed_87.csv +1050 -0
- openclatura-0.1.0/eval_failures/pubchem/summary.csv +6 -0
- openclatura-0.1.0/eval_failures/zinc22/all_failures.csv +17524 -0
- openclatura-0.1.0/eval_failures/zinc22/failures_seed_17.csv +3491 -0
- openclatura-0.1.0/eval_failures/zinc22/failures_seed_42.csv +3443 -0
- openclatura-0.1.0/eval_failures/zinc22/failures_seed_5.csv +3605 -0
- openclatura-0.1.0/eval_failures/zinc22/failures_seed_63.csv +3531 -0
- openclatura-0.1.0/eval_failures/zinc22/failures_seed_87.csv +3458 -0
- openclatura-0.1.0/eval_failures/zinc22/summary.csv +6 -0
- openclatura-0.1.0/examples/README.md +27 -0
- openclatura-0.1.0/examples/eval_via_opsin.py +63 -0
- openclatura-0.1.0/examples/find_small_failures.py +183 -0
- openclatura-0.1.0/examples/opsin_eval_ZINC22.py +508 -0
- openclatura-0.1.0/examples/opsin_eval_pubchem.py +326 -0
- openclatura-0.1.0/examples/qm9_iupac_collect_token_confidence.py +204 -0
- openclatura-0.1.0/examples/random_sanity.py +125 -0
- openclatura-0.1.0/examples/sanity_examples.py +70 -0
- openclatura-0.1.0/examples/test_opsin_mac.py +96 -0
- openclatura-0.1.0/examples/test_opsin_mac_ZINC.py +217 -0
- openclatura-0.1.0/examples/test_opsin_mac_ZINC22.py +226 -0
- openclatura-0.1.0/examples/test_opsin_mac_ZINC22_light.py +240 -0
- openclatura-0.1.0/examples/test_qm9_opsin_batch.py +250 -0
- openclatura-0.1.0/pyproject.toml +130 -0
- openclatura-0.1.0/scripts/regenerate_goldens.py +49 -0
- openclatura-0.1.0/src/openclatura/__init__.py +110 -0
- openclatura-0.1.0/src/openclatura/additive.py +50 -0
- openclatura-0.1.0/src/openclatura/assembler.py +427 -0
- openclatura-0.1.0/src/openclatura/assembly_charge.py +105 -0
- openclatura-0.1.0/src/openclatura/assembly_parent.py +229 -0
- openclatura-0.1.0/src/openclatura/assembly_parts.py +122 -0
- openclatura-0.1.0/src/openclatura/assembly_prefixes.py +193 -0
- openclatura-0.1.0/src/openclatura/assembly_spiro.py +373 -0
- openclatura-0.1.0/src/openclatura/assembly_utils.py +44 -0
- openclatura-0.1.0/src/openclatura/chains.py +1154 -0
- openclatura-0.1.0/src/openclatura/charge_pair_roles.py +286 -0
- openclatura-0.1.0/src/openclatura/charge_specs.py +6 -0
- openclatura-0.1.0/src/openclatura/cli.py +181 -0
- openclatura-0.1.0/src/openclatura/component_group_rules.py +85 -0
- openclatura-0.1.0/src/openclatura/component_modifiers.py +281 -0
- openclatura-0.1.0/src/openclatura/component_namer.py +671 -0
- openclatura-0.1.0/src/openclatura/data/fused_emission_examples.json +40 -0
- openclatura-0.1.0/src/openclatura/data/fused_ion_templates.json +126 -0
- openclatura-0.1.0/src/openclatura/data/heteroatom_substituents.json +156 -0
- openclatura-0.1.0/src/openclatura/data/namer_rules.json +2657 -0
- openclatura-0.1.0/src/openclatura/data/parser_grammar_snapshot.json +463 -0
- openclatura-0.1.0/src/openclatura/data/parser_xml_resources/alkanes.json +522 -0
- openclatura-0.1.0/src/openclatura/data/parser_xml_resources/aminoAcids.json +1713 -0
- openclatura-0.1.0/src/openclatura/data/parser_xml_resources/arylGroups.json +3037 -0
- openclatura-0.1.0/src/openclatura/data/parser_xml_resources/arylSubstituents.json +1014 -0
- openclatura-0.1.0/src/openclatura/data/parser_xml_resources/atomHydrides.json +292 -0
- openclatura-0.1.0/src/openclatura/data/parser_xml_resources/carbohydrates.json +849 -0
- openclatura-0.1.0/src/openclatura/data/parser_xml_resources/carboxylicAcids.json +1990 -0
- openclatura-0.1.0/src/openclatura/data/parser_xml_resources/chargeAndOxidationNumberSpecifiers.json +349 -0
- openclatura-0.1.0/src/openclatura/data/parser_xml_resources/cyclicUnsaturableHydrocarbon.json +213 -0
- openclatura-0.1.0/src/openclatura/data/parser_xml_resources/elementaryAtoms.json +1128 -0
- openclatura-0.1.0/src/openclatura/data/parser_xml_resources/fragments.json +10864 -0
- openclatura-0.1.0/src/openclatura/data/parser_xml_resources/functionalTerms.json +1072 -0
- openclatura-0.1.0/src/openclatura/data/parser_xml_resources/fusionComponents.json +1439 -0
- openclatura-0.1.0/src/openclatura/data/parser_xml_resources/germanTokens.json +103 -0
- openclatura-0.1.0/src/openclatura/data/parser_xml_resources/groupStemsAllowingAllSuffixes.json +53 -0
- openclatura-0.1.0/src/openclatura/data/parser_xml_resources/groupStemsAllowingInlineSuffixes.json +403 -0
- openclatura-0.1.0/src/openclatura/data/parser_xml_resources/heteroAtoms.json +3243 -0
- openclatura-0.1.0/src/openclatura/data/parser_xml_resources/hwHeteroAtoms.json +732 -0
- openclatura-0.1.0/src/openclatura/data/parser_xml_resources/hwSuffixes.json +506 -0
- openclatura-0.1.0/src/openclatura/data/parser_xml_resources/infixes.json +209 -0
- openclatura-0.1.0/src/openclatura/data/parser_xml_resources/inlineChargeSuffixes.json +63 -0
- openclatura-0.1.0/src/openclatura/data/parser_xml_resources/inlineSuffixes.json +412 -0
- openclatura-0.1.0/src/openclatura/data/parser_xml_resources/miscTokens.json +808 -0
- openclatura-0.1.0/src/openclatura/data/parser_xml_resources/multiRadicalSubstituents.json +977 -0
- openclatura-0.1.0/src/openclatura/data/parser_xml_resources/multipliers.json +1090 -0
- openclatura-0.1.0/src/openclatura/data/parser_xml_resources/naturalProducts.json +302 -0
- openclatura-0.1.0/src/openclatura/data/parser_xml_resources/nonCarboxylicAcids.json +2605 -0
- openclatura-0.1.0/src/openclatura/data/parser_xml_resources/regexTokens.json +355 -0
- openclatura-0.1.0/src/openclatura/data/parser_xml_resources/regexes.json +1668 -0
- openclatura-0.1.0/src/openclatura/data/parser_xml_resources/simpleCyclicGroups.json +1313 -0
- openclatura-0.1.0/src/openclatura/data/parser_xml_resources/simpleGroups.json +3291 -0
- openclatura-0.1.0/src/openclatura/data/parser_xml_resources/simpleSubstituents.json +4082 -0
- openclatura-0.1.0/src/openclatura/data/parser_xml_resources/substituents.json +138 -0
- openclatura-0.1.0/src/openclatura/data/parser_xml_resources/suffixApplicability.json +1260 -0
- openclatura-0.1.0/src/openclatura/data/parser_xml_resources/suffixPrefix.json +167 -0
- openclatura-0.1.0/src/openclatura/data/parser_xml_resources/suffixRules.json +1587 -0
- openclatura-0.1.0/src/openclatura/data/parser_xml_resources/suffixes.json +1003 -0
- openclatura-0.1.0/src/openclatura/data/parser_xml_resources/unsaturators.json +71 -0
- openclatura-0.1.0/src/openclatura/data/parser_xml_resources/wordRules.json +1325 -0
- openclatura-0.1.0/src/openclatura/data/retained_fused_graph_templates.json +584 -0
- openclatura-0.1.0/src/openclatura/describer.py +614 -0
- openclatura-0.1.0/src/openclatura/engine.py +369 -0
- openclatura-0.1.0/src/openclatura/formatting.py +122 -0
- openclatura-0.1.0/src/openclatura/functional_groups.py +61 -0
- openclatura-0.1.0/src/openclatura/functional_prefixes.py +277 -0
- openclatura-0.1.0/src/openclatura/fused_ion_templates.py +311 -0
- openclatura-0.1.0/src/openclatura/fused_topology.py +592 -0
- openclatura-0.1.0/src/openclatura/grammar_snapshot_data.py +172 -0
- openclatura-0.1.0/src/openclatura/graph_io.py +110 -0
- openclatura-0.1.0/src/openclatura/group_atom_roles.py +58 -0
- openclatura-0.1.0/src/openclatura/heteroatom_subgraphs.py +790 -0
- openclatura-0.1.0/src/openclatura/heteroatom_substituent_specs.py +116 -0
- openclatura-0.1.0/src/openclatura/heterocumulene_roles.py +85 -0
- openclatura-0.1.0/src/openclatura/human_descriptor.py +549 -0
- openclatura-0.1.0/src/openclatura/hypervalent_roles.py +185 -0
- openclatura-0.1.0/src/openclatura/ionic_naming.py +666 -0
- openclatura-0.1.0/src/openclatura/locants.py +110 -0
- openclatura-0.1.0/src/openclatura/map_namer.md +183 -0
- openclatura-0.1.0/src/openclatura/molecule.py +243 -0
- openclatura-0.1.0/src/openclatura/name_assembly.py +2486 -0
- openclatura-0.1.0/src/openclatura/name_bindings.py +1445 -0
- openclatura-0.1.0/src/openclatura/name_operations.py +67 -0
- openclatura-0.1.0/src/openclatura/name_postprocessing.py +173 -0
- openclatura-0.1.0/src/openclatura/namer.py +1746 -0
- openclatura-0.1.0/src/openclatura/namer_config.py +34 -0
- openclatura-0.1.0/src/openclatura/naming_audit.py +65 -0
- openclatura-0.1.0/src/openclatura/naming_context.py +167 -0
- openclatura-0.1.0/src/openclatura/naming_data.py +64 -0
- openclatura-0.1.0/src/openclatura/nitrogen_roles.py +769 -0
- openclatura-0.1.0/src/openclatura/nomenclature.py +454 -0
- openclatura-0.1.0/src/openclatura/numbering.py +369 -0
- openclatura-0.1.0/src/openclatura/operations.py +52 -0
- openclatura-0.1.0/src/openclatura/opsin_verify.py +172 -0
- openclatura-0.1.0/src/openclatura/overview_namer.md +172 -0
- openclatura-0.1.0/src/openclatura/oxoacid_roles.py +164 -0
- openclatura-0.1.0/src/openclatura/oxoacid_templates.py +154 -0
- openclatura-0.1.0/src/openclatura/parent_pipeline.py +211 -0
- openclatura-0.1.0/src/openclatura/parent_selection.py +449 -0
- openclatura-0.1.0/src/openclatura/perception.py +734 -0
- openclatura-0.1.0/src/openclatura/peroxy_carbonyl_roles.py +171 -0
- openclatura-0.1.0/src/openclatura/polycycle_topology.py +809 -0
- openclatura-0.1.0/src/openclatura/principal_groups.py +132 -0
- openclatura-0.1.0/src/openclatura/principal_suffixes.py +32 -0
- openclatura-0.1.0/src/openclatura/resonance_compare.py +96 -0
- openclatura-0.1.0/src/openclatura/retained_fused_production.py +112 -0
- openclatura-0.1.0/src/openclatura/retained_fused_templates.py +576 -0
- openclatura-0.1.0/src/openclatura/retained_specs.py +44 -0
- openclatura-0.1.0/src/openclatura/ring_parent.py +92 -0
- openclatura-0.1.0/src/openclatura/ring_renderer.py +66 -0
- openclatura-0.1.0/src/openclatura/ring_systems.py +78 -0
- openclatura-0.1.0/src/openclatura/role_certificate.py +194 -0
- openclatura-0.1.0/src/openclatura/rule_layout.py +170 -0
- openclatura-0.1.0/src/openclatura/rules/__init__.py +0 -0
- openclatura-0.1.0/src/openclatura/rules/bonds.py +73 -0
- openclatura-0.1.0/src/openclatura/rules/elements.py +47 -0
- openclatura-0.1.0/src/openclatura/rules/elision.py +123 -0
- openclatura-0.1.0/src/openclatura/rules/locants.py +182 -0
- openclatura-0.1.0/src/openclatura/rules/multipliers.py +64 -0
- openclatura-0.1.0/src/openclatura/rules/retained.py +966 -0
- openclatura-0.1.0/src/openclatura/rules/stems.py +65 -0
- openclatura-0.1.0/src/openclatura/rules/substituents.py +34 -0
- openclatura-0.1.0/src/openclatura/rules/suffixes.py +48 -0
- openclatura-0.1.0/src/openclatura/small_ring_stereo.py +112 -0
- openclatura-0.1.0/src/openclatura/special_cases.py +2237 -0
- openclatura-0.1.0/src/openclatura/spiro_assembly.py +12 -0
- openclatura-0.1.0/src/openclatura/stereo_audit.py +108 -0
- openclatura-0.1.0/src/openclatura/stereo_descriptors.py +20 -0
- openclatura-0.1.0/src/openclatura/subgraph_tools.py +126 -0
- openclatura-0.1.0/src/openclatura/substituent_tokens.py +631 -0
- openclatura-0.1.0/src/openclatura/subtractive.py +63 -0
- openclatura-0.1.0/src/openclatura/suffix_stack.py +185 -0
- openclatura-0.1.0/src/openclatura/tests/test_analysis.py +5296 -0
- openclatura-0.1.0/src/openclatura/tests/test_public_api.py +274 -0
- openclatura-0.1.0/src/openclatura/tests_roundtrip/__init__.py +1 -0
- openclatura-0.1.0/src/openclatura/tests_roundtrip/roundtrip_helpers.py +136 -0
- openclatura-0.1.0/src/openclatura/tests_roundtrip/test_acids.py +27 -0
- openclatura-0.1.0/src/openclatura/tests_roundtrip/test_alcohols.py +24 -0
- openclatura-0.1.0/src/openclatura/tests_roundtrip/test_alkanes.py +26 -0
- openclatura-0.1.0/src/openclatura/tests_roundtrip/test_alkenes_alkynes.py +24 -0
- openclatura-0.1.0/src/openclatura/tests_roundtrip/test_api.py +20 -0
- openclatura-0.1.0/src/openclatura/tests_roundtrip/test_bridges.py +42 -0
- openclatura-0.1.0/src/openclatura/tests_roundtrip/test_charged_fused_heteroaromatics.py +17 -0
- openclatura-0.1.0/src/openclatura/tests_roundtrip/test_composite_bridges.py +24 -0
- openclatura-0.1.0/src/openclatura/tests_roundtrip/test_fused_multiplicity.py +20 -0
- openclatura-0.1.0/src/openclatura/tests_roundtrip/test_fused_parents.py +149 -0
- openclatura-0.1.0/src/openclatura/tests_roundtrip/test_haloalkanes.py +21 -0
- openclatura-0.1.0/src/openclatura/tests_roundtrip/test_heteroatom_substituents.py +23 -0
- openclatura-0.1.0/src/openclatura/tests_roundtrip/test_heterocycles.py +73 -0
- openclatura-0.1.0/src/openclatura/tests_roundtrip/test_imines.py +24 -0
- openclatura-0.1.0/src/openclatura/tests_roundtrip/test_ketones.py +21 -0
- openclatura-0.1.0/src/openclatura/tests_roundtrip/test_locant_display.py +21 -0
- openclatura-0.1.0/src/openclatura/tests_roundtrip/test_nitro.py +22 -0
- openclatura-0.1.0/src/openclatura/tests_roundtrip/test_numbering.py +20 -0
- openclatura-0.1.0/src/openclatura/tests_roundtrip/test_oxyacids.py +27 -0
- openclatura-0.1.0/src/openclatura/tests_roundtrip/test_p13_additive.py +22 -0
- openclatura-0.1.0/src/openclatura/tests_roundtrip/test_p13_conjunctive.py +38 -0
- openclatura-0.1.0/src/openclatura/tests_roundtrip/test_p13_fusion.py +23 -0
- openclatura-0.1.0/src/openclatura/tests_roundtrip/test_p13_multiplicative.py +23 -0
- openclatura-0.1.0/src/openclatura/tests_roundtrip/test_p13_operations.py +21 -0
- openclatura-0.1.0/src/openclatura/tests_roundtrip/test_p13_replacement.py +22 -0
- openclatura-0.1.0/src/openclatura/tests_roundtrip/test_p13_substitutive.py +28 -0
- openclatura-0.1.0/src/openclatura/tests_roundtrip/test_p13_subtractive.py +23 -0
- openclatura-0.1.0/src/openclatura/tests_roundtrip/test_polycycles.py +29 -0
- openclatura-0.1.0/src/openclatura/tests_roundtrip/test_polycyclic_descriptors.py +23 -0
- openclatura-0.1.0/src/openclatura/tests_roundtrip/test_polyfunctional_fragments.py +30 -0
- openclatura-0.1.0/src/openclatura/tests_roundtrip/test_principal_group_breadth.py +37 -0
- openclatura-0.1.0/src/openclatura/tests_roundtrip/test_replacement_parents.py +55 -0
- openclatura-0.1.0/src/openclatura/tests_roundtrip/test_role_models.py +24 -0
- openclatura-0.1.0/src/openclatura/tests_roundtrip/test_stereo.py +20 -0
- openclatura-0.1.0/src/openclatura/tests_roundtrip/test_substituents.py +25 -0
- openclatura-0.1.0/src/openclatura/token_grammar.py +88 -0
- openclatura-0.1.0/src/openclatura/trace_helpers.py +528 -0
- openclatura-0.1.0/src/openclatura/utils.py +48 -0
- openclatura-0.1.0/src/openclatura/von_baeyer.py +470 -0
- openclatura-0.1.0/src/openclatura/web/__init__.py +10 -0
- openclatura-0.1.0/src/openclatura/web/__main__.py +32 -0
- openclatura-0.1.0/src/openclatura/web/app.py +99 -0
- openclatura-0.1.0/test_opsin_qm9_batch.py +97 -0
- openclatura-0.1.0/testing_guide.md +52 -0
- openclatura-0.1.0/tests/datasets/test_pubchem_sample.py +63 -0
- openclatura-0.1.0/tests/datasets/test_qm9_sample.py +61 -0
- openclatura-0.1.0/tests/fixtures/diverse_corpus.csv +107 -0
- openclatura-0.1.0/tests/fixtures/diverse_corpus.golden.json +532 -0
- openclatura-0.1.0/tests/fuzz/test_smiles_strategies.py +212 -0
- openclatura-0.1.0/tests/integration/test_corpus_golden.py +62 -0
- openclatura-0.1.0/tests/integration/test_describer.py +196 -0
- openclatura-0.1.0/tests/integration/test_diverse_corpus.py +133 -0
- openclatura-0.1.0/tests/integration/test_human_descriptor.py +69 -0
- openclatura-0.1.0/tests/integration/test_web_app.py +81 -0
- openclatura-0.1.0/to-uninstall.txt +109 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, refactor_namer]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: ["**"]
|
|
8
|
+
|
|
9
|
+
concurrency:
|
|
10
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
11
|
+
cancel-in-progress: true
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
lint:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
- uses: actions/setup-python@v5
|
|
19
|
+
with:
|
|
20
|
+
python-version: "3.12"
|
|
21
|
+
- name: Install ruff
|
|
22
|
+
run: pip install "ruff==0.15.15"
|
|
23
|
+
- name: Ruff check
|
|
24
|
+
run: ruff check src/openclatura
|
|
25
|
+
- name: Ruff format check
|
|
26
|
+
run: ruff format --check src/openclatura
|
|
27
|
+
|
|
28
|
+
test:
|
|
29
|
+
runs-on: ubuntu-latest
|
|
30
|
+
strategy:
|
|
31
|
+
fail-fast: false
|
|
32
|
+
matrix:
|
|
33
|
+
python-version: ["3.11", "3.12"]
|
|
34
|
+
steps:
|
|
35
|
+
- uses: actions/checkout@v4
|
|
36
|
+
|
|
37
|
+
- uses: actions/setup-python@v5
|
|
38
|
+
with:
|
|
39
|
+
python-version: ${{ matrix.python-version }}
|
|
40
|
+
cache: pip
|
|
41
|
+
|
|
42
|
+
- name: Set up JDK (for OPSIN)
|
|
43
|
+
uses: actions/setup-java@v4
|
|
44
|
+
with:
|
|
45
|
+
distribution: temurin
|
|
46
|
+
java-version: "17"
|
|
47
|
+
|
|
48
|
+
- name: Install package
|
|
49
|
+
run: |
|
|
50
|
+
python -m pip install --upgrade pip
|
|
51
|
+
pip install -e ".[dev]"
|
|
52
|
+
|
|
53
|
+
- name: Run fast tests
|
|
54
|
+
run: |
|
|
55
|
+
pytest -m "not slow and not dataset and not golden" \
|
|
56
|
+
--maxfail=20 \
|
|
57
|
+
--durations=20
|
|
58
|
+
|
|
59
|
+
rdkit-compat:
|
|
60
|
+
# Strict golden-output regression across RDKit versions. Detects
|
|
61
|
+
# naming output that drifts with the RDKit upgrade cycle (aromaticity,
|
|
62
|
+
# kekulisation, H-count perception). Each matrix row pins a specific
|
|
63
|
+
# rdkit and runs only the `golden` test.
|
|
64
|
+
runs-on: ubuntu-latest
|
|
65
|
+
needs: [lint]
|
|
66
|
+
strategy:
|
|
67
|
+
fail-fast: false
|
|
68
|
+
matrix:
|
|
69
|
+
rdkit-version:
|
|
70
|
+
- "2024.03.6"
|
|
71
|
+
- "2024.09.6"
|
|
72
|
+
- "2025.03.6"
|
|
73
|
+
- "2025.09.5"
|
|
74
|
+
- "2026.3.2"
|
|
75
|
+
steps:
|
|
76
|
+
- uses: actions/checkout@v4
|
|
77
|
+
|
|
78
|
+
- uses: actions/setup-python@v5
|
|
79
|
+
with:
|
|
80
|
+
python-version: "3.12"
|
|
81
|
+
cache: pip
|
|
82
|
+
|
|
83
|
+
- name: Install package + pinned rdkit
|
|
84
|
+
run: |
|
|
85
|
+
python -m pip install --upgrade pip
|
|
86
|
+
pip install -e ".[dev]"
|
|
87
|
+
pip install --force-reinstall --no-deps "rdkit==${{ matrix.rdkit-version }}"
|
|
88
|
+
|
|
89
|
+
- name: Show installed versions
|
|
90
|
+
run: |
|
|
91
|
+
python -c "import rdkit, sys; print('python', sys.version); print('rdkit', rdkit.__version__)"
|
|
92
|
+
|
|
93
|
+
- name: Run golden corpus test
|
|
94
|
+
run: pytest -m golden -v --no-header -rA
|
|
95
|
+
|
|
96
|
+
docker:
|
|
97
|
+
runs-on: ubuntu-latest
|
|
98
|
+
needs: [lint]
|
|
99
|
+
steps:
|
|
100
|
+
- uses: actions/checkout@v4
|
|
101
|
+
|
|
102
|
+
- uses: docker/setup-buildx-action@v3
|
|
103
|
+
|
|
104
|
+
- name: Build image
|
|
105
|
+
uses: docker/build-push-action@v6
|
|
106
|
+
with:
|
|
107
|
+
context: .
|
|
108
|
+
push: false
|
|
109
|
+
load: true
|
|
110
|
+
tags: openclatura:ci
|
|
111
|
+
|
|
112
|
+
- name: Smoke-test the image
|
|
113
|
+
run: |
|
|
114
|
+
docker run -d --name openclatura-ci -p 8000:8000 openclatura:ci
|
|
115
|
+
for i in $(seq 1 30); do
|
|
116
|
+
if curl -fsS http://127.0.0.1:8000/healthz > /tmp/health.json; then
|
|
117
|
+
cat /tmp/health.json
|
|
118
|
+
exit 0
|
|
119
|
+
fi
|
|
120
|
+
sleep 2
|
|
121
|
+
done
|
|
122
|
+
docker logs openclatura-ci
|
|
123
|
+
exit 1
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
name: Build Docker image
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, tests]
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
packages: write
|
|
12
|
+
|
|
13
|
+
env:
|
|
14
|
+
IMAGE_NAME: ghcr.io/${{ github.repository }}
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
docker:
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
|
|
20
|
+
steps:
|
|
21
|
+
- name: Check out repository
|
|
22
|
+
uses: actions/checkout@v6
|
|
23
|
+
|
|
24
|
+
- name: Set up Docker Buildx
|
|
25
|
+
uses: docker/setup-buildx-action@v3
|
|
26
|
+
|
|
27
|
+
- name: Log in to GitHub Container Registry
|
|
28
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
29
|
+
uses: docker/login-action@v4
|
|
30
|
+
with:
|
|
31
|
+
registry: ghcr.io
|
|
32
|
+
username: ${{ github.actor }}
|
|
33
|
+
password: ${{ secrets.GITHUB_TOKEN }}
|
|
34
|
+
|
|
35
|
+
- name: Build local image for smoke test
|
|
36
|
+
uses: docker/build-push-action@v7
|
|
37
|
+
with:
|
|
38
|
+
context: .
|
|
39
|
+
load: true
|
|
40
|
+
tags: openclatura:test
|
|
41
|
+
|
|
42
|
+
- name: Smoke test Docker image
|
|
43
|
+
run: |
|
|
44
|
+
docker run --rm -d -p 8000:8000 --name openclatura-test openclatura:test
|
|
45
|
+
sleep 5
|
|
46
|
+
curl --fail http://localhost:8000/healthz
|
|
47
|
+
docker stop openclatura-test
|
|
48
|
+
|
|
49
|
+
- name: Build and push image from main
|
|
50
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
51
|
+
uses: docker/build-push-action@v7
|
|
52
|
+
with:
|
|
53
|
+
context: .
|
|
54
|
+
push: true
|
|
55
|
+
tags: |
|
|
56
|
+
${{ env.IMAGE_NAME }}:main
|
|
57
|
+
${{ env.IMAGE_NAME }}:${{ github.sha }}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# Distribution / packaging
|
|
7
|
+
.Python
|
|
8
|
+
build/
|
|
9
|
+
develop-eggs/
|
|
10
|
+
dist/
|
|
11
|
+
downloads/
|
|
12
|
+
eggs/
|
|
13
|
+
.eggs/
|
|
14
|
+
lib/
|
|
15
|
+
lib64/
|
|
16
|
+
parts/
|
|
17
|
+
sdist/
|
|
18
|
+
var/
|
|
19
|
+
wheels/
|
|
20
|
+
*.egg-info/
|
|
21
|
+
.installed.cfg
|
|
22
|
+
*.egg
|
|
23
|
+
MANIFEST
|
|
24
|
+
|
|
25
|
+
# Hatch
|
|
26
|
+
.hatch/
|
|
27
|
+
|
|
28
|
+
# Unit test / coverage / type checkers
|
|
29
|
+
.tox/
|
|
30
|
+
.nox/
|
|
31
|
+
.coverage
|
|
32
|
+
.coverage.*
|
|
33
|
+
.cache
|
|
34
|
+
.pytest_cache/
|
|
35
|
+
.mypy_cache/
|
|
36
|
+
.ruff_cache/
|
|
37
|
+
htmlcov/
|
|
38
|
+
coverage.xml
|
|
39
|
+
*.cover
|
|
40
|
+
.hypothesis/
|
|
41
|
+
|
|
42
|
+
# Environments
|
|
43
|
+
.env
|
|
44
|
+
.venv
|
|
45
|
+
env/
|
|
46
|
+
venv/
|
|
47
|
+
ENV/
|
|
48
|
+
.python-version
|
|
49
|
+
|
|
50
|
+
# IDEs
|
|
51
|
+
.vscode/
|
|
52
|
+
.idea/
|
|
53
|
+
*.swp
|
|
54
|
+
*.swo
|
|
55
|
+
.DS_Store
|
|
56
|
+
|
|
57
|
+
# Notebooks
|
|
58
|
+
.ipynb_checkpoints/
|
|
59
|
+
|
|
60
|
+
# Project-specific
|
|
61
|
+
*.log
|
|
62
|
+
*.jar
|
|
63
|
+
opsin*.jar
|
|
64
|
+
data/cache/
|
|
65
|
+
.cache/
|
|
66
|
+
|
|
67
|
+
# Docker
|
|
68
|
+
.docker/
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
3
|
+
rev: v4.6.0
|
|
4
|
+
hooks:
|
|
5
|
+
- id: trailing-whitespace
|
|
6
|
+
- id: end-of-file-fixer
|
|
7
|
+
- id: check-yaml
|
|
8
|
+
- id: check-toml
|
|
9
|
+
- id: check-merge-conflict
|
|
10
|
+
- id: check-added-large-files
|
|
11
|
+
args: ["--maxkb=1024"]
|
|
12
|
+
|
|
13
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
14
|
+
rev: v0.6.9
|
|
15
|
+
hooks:
|
|
16
|
+
- id: ruff
|
|
17
|
+
args: [--fix]
|
|
18
|
+
- id: ruff-format
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# syntax=docker/dockerfile:1.7
|
|
2
|
+
|
|
3
|
+
# ----- builder stage -----------------------------------------------------
|
|
4
|
+
FROM python:3.12-slim AS builder
|
|
5
|
+
|
|
6
|
+
ENV PIP_DISABLE_PIP_VERSION_CHECK=1 \
|
|
7
|
+
PIP_NO_CACHE_DIR=1 \
|
|
8
|
+
PYTHONDONTWRITEBYTECODE=1
|
|
9
|
+
|
|
10
|
+
WORKDIR /build
|
|
11
|
+
|
|
12
|
+
# Build deps for rdkit wheels: typically just runtime; the manylinux wheels
|
|
13
|
+
# carry their own libs. Keep this stage thin.
|
|
14
|
+
RUN apt-get update -qq \
|
|
15
|
+
&& apt-get install -y --no-install-recommends build-essential \
|
|
16
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
17
|
+
|
|
18
|
+
COPY pyproject.toml README.md ./
|
|
19
|
+
COPY src ./src
|
|
20
|
+
|
|
21
|
+
# Install into a clean prefix so we can copy a slim layer into the runtime.
|
|
22
|
+
RUN python -m pip install --upgrade pip \
|
|
23
|
+
&& python -m pip install --prefix=/install ".[web,opsin]"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# ----- runtime stage -----------------------------------------------------
|
|
27
|
+
FROM python:3.12-slim AS runtime
|
|
28
|
+
|
|
29
|
+
ENV PYTHONUNBUFFERED=1 \
|
|
30
|
+
PYTHONDONTWRITEBYTECODE=1 \
|
|
31
|
+
PATH="/usr/local/bin:${PATH}"
|
|
32
|
+
|
|
33
|
+
# Java runtime for OPSIN round-trip verification (py2opsin shells out to java;
|
|
34
|
+
# OPSIN itself requires Java >=8 so the newer JRE is fine).
|
|
35
|
+
RUN apt-get update -qq \
|
|
36
|
+
&& apt-get install -y --no-install-recommends \
|
|
37
|
+
default-jre-headless \
|
|
38
|
+
curl \
|
|
39
|
+
ca-certificates \
|
|
40
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
41
|
+
|
|
42
|
+
# Bring in the installed package + deps from the builder stage.
|
|
43
|
+
COPY --from=builder /install /usr/local
|
|
44
|
+
|
|
45
|
+
# Non-root user.
|
|
46
|
+
RUN useradd --create-home --shell /bin/bash openclatura
|
|
47
|
+
USER openclatura
|
|
48
|
+
WORKDIR /home/openclatura
|
|
49
|
+
|
|
50
|
+
EXPOSE 8000
|
|
51
|
+
|
|
52
|
+
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
|
|
53
|
+
CMD curl -fsS http://127.0.0.1:8000/healthz || exit 1
|
|
54
|
+
|
|
55
|
+
CMD ["python", "-m", "openclatura.web", "--host", "0.0.0.0", "--port", "8000"]
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 lamalab-org
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: openclatura
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Deterministic SMILES-to-IUPAC name generator based on the IUPAC Blue Book
|
|
5
|
+
Project-URL: Homepage, https://github.com/lamalab-org/openclatura
|
|
6
|
+
Project-URL: Repository, https://github.com/lamalab-org/openclatura
|
|
7
|
+
Project-URL: Issues, https://github.com/lamalab-org/openclatura/issues
|
|
8
|
+
Author: Adrian Mirza, Kevin Maik Jablonka, Rostislav Fedorov
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: chemistry,iupac,nomenclature,rdkit,smiles
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: Chemistry
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
|
+
Requires-Dist: rdkit>=2023.09
|
|
22
|
+
Provides-Extra: datasets
|
|
23
|
+
Requires-Dist: datasets>=2.0; extra == 'datasets'
|
|
24
|
+
Requires-Dist: huggingface-hub>=0.20; extra == 'datasets'
|
|
25
|
+
Requires-Dist: pandas>=2.0; extra == 'datasets'
|
|
26
|
+
Requires-Dist: tqdm>=4.65; extra == 'datasets'
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: fastapi>=0.110; extra == 'dev'
|
|
29
|
+
Requires-Dist: httpx>=0.27; extra == 'dev'
|
|
30
|
+
Requires-Dist: hypothesis>=6.92; extra == 'dev'
|
|
31
|
+
Requires-Dist: pre-commit>=3.5; extra == 'dev'
|
|
32
|
+
Requires-Dist: py2opsin>=1.2; extra == 'dev'
|
|
33
|
+
Requires-Dist: pydantic>=2.5; extra == 'dev'
|
|
34
|
+
Requires-Dist: pytest-cov>=4.1; extra == 'dev'
|
|
35
|
+
Requires-Dist: pytest-xdist>=3.5; extra == 'dev'
|
|
36
|
+
Requires-Dist: pytest>=7.4; extra == 'dev'
|
|
37
|
+
Requires-Dist: ruff==0.15.15; extra == 'dev'
|
|
38
|
+
Provides-Extra: opsin
|
|
39
|
+
Requires-Dist: py2opsin>=1.2; extra == 'opsin'
|
|
40
|
+
Provides-Extra: web
|
|
41
|
+
Requires-Dist: fastapi>=0.110; extra == 'web'
|
|
42
|
+
Requires-Dist: pydantic>=2.5; extra == 'web'
|
|
43
|
+
Requires-Dist: uvicorn[standard]>=0.27; extra == 'web'
|
|
44
|
+
Description-Content-Type: text/markdown
|
|
45
|
+
|
|
46
|
+
# openclatura
|
|
47
|
+
|
|
48
|
+
**Open Nomenclature Framework**
|
|
49
|
+
|
|
50
|
+
`openclatura` is a deterministic SMILES-to-IUPAC name generator inspired by the IUPAC Blue Book 2013 recommendations.
|
|
51
|
+
|
|
52
|
+
Built on top of RDKit, the package walks the molecular graph, detects functional groups and ring systems, selects the principal parent, assigns locants, and constructs the corresponding substitutive IUPAC name. Every step is recorded in an inspectable
|
|
53
|
+
decision trace so the *why* of a name is recoverable, not just the *what*.
|
|
54
|
+
|
|
55
|
+
> **Status:** alpha. The naming engine handles a broad slice of organic
|
|
56
|
+
> structures (alkanes/alkenes/alkynes, common functional groups, simple
|
|
57
|
+
> heterocycles, fused/spiro/bridged systems, retained names from the Blue
|
|
58
|
+
> Book). PubChem/QM9/ZINC22 coverage is being measured; see `examples/`.
|
|
59
|
+
|
|
60
|
+
## Install
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
pip install openclatura
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Optional extras:
|
|
67
|
+
|
|
68
|
+
| extra | adds |
|
|
69
|
+
| ------------ | ----------------------------------------------------- |
|
|
70
|
+
| `[opsin]` | `py2opsin` for OPSIN-based round-trip verification |
|
|
71
|
+
| `[datasets]` | `datasets` + `tqdm` for PubChem/QM9-style evaluations |
|
|
72
|
+
| `[web]` | FastAPI + uvicorn for the HTTP service |
|
|
73
|
+
| `[dev]` | pytest, ruff, pre-commit, hypothesis, py2opsin |
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
pip install "openclatura[opsin,datasets]"
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Quick start
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
from openclatura import name_smiles
|
|
83
|
+
|
|
84
|
+
name_smiles("CCO") # 'ethanol'
|
|
85
|
+
name_smiles("c1ccccc1") # 'benzene'
|
|
86
|
+
name_smiles("CC(=O)O") # 'acetic acid'
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Typed result with rules hit + OPSIN round-trip
|
|
90
|
+
|
|
91
|
+
For everything richer than the bare string, use `openclatura.name`:
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
from openclatura import name
|
|
95
|
+
|
|
96
|
+
result = name("CC(=O)Nc1ccccc1", include_trace=True, verify_opsin=True)
|
|
97
|
+
|
|
98
|
+
result.name # 'N-phenylacetamide'
|
|
99
|
+
result.smiles # 'CC(=O)Nc1ccccc1'
|
|
100
|
+
result.ok # True
|
|
101
|
+
result.rules_hit # ('P-44', 'P-45', 'P-41', 'P-61', 'P-67', ...)
|
|
102
|
+
result.rule_hints # ('Parent hydride / parent structure: Blue Book P-44 ...',)
|
|
103
|
+
result.opsin_check.status # 'matched' | 'mismatched' | 'skipped_no_java' | ...
|
|
104
|
+
result.verified # True when opsin_check is matched
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Errors do not raise — they are captured on `result.error`, which makes
|
|
108
|
+
the batch API safe to point at noisy datasets:
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
from openclatura import name_many
|
|
112
|
+
|
|
113
|
+
results = name_many(
|
|
114
|
+
["CCO", "c1ccccc1", "definitely-not-a-smiles"],
|
|
115
|
+
processes="auto", # or an integer, or 1 for in-process
|
|
116
|
+
verify_opsin=False,
|
|
117
|
+
)
|
|
118
|
+
[r.name for r in results if r.ok]
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
For the full decision trace (one `TraceStep` per phase: parse, perception,
|
|
122
|
+
parent selection, numbering, assembly, …):
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
from openclatura import analyze_smiles
|
|
126
|
+
|
|
127
|
+
analysis = analyze_smiles("CC(=O)Nc1ccccc1")
|
|
128
|
+
for step in analysis.decisions:
|
|
129
|
+
print(step.phase, step.decision, step.reason)
|
|
130
|
+
```
|
|
131
|
+
### CLI
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
openclatura name "CC(=O)Nc1ccccc1" # → N-phenylacetamide
|
|
135
|
+
openclatura name "CC(=O)Nc1ccccc1" --json # JSON with trace + rules
|
|
136
|
+
openclatura batch smiles.txt --output names.jsonl --processes auto
|
|
137
|
+
```
|
|
138
|
+
The CLI tool has OPSIN verification turned on by default. It can be turned off with
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
openclatura name "CN1C=NC2=C1C(=O)N(C(=O)N2C)C" --no-verify
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Natural-language description (`describe`)
|
|
145
|
+
|
|
146
|
+
`openclatura.describe(smiles)` walks the same trace and renders a
|
|
147
|
+
deterministic, multi-paragraph explanation of how the name is built.
|
|
148
|
+
Useful for explainability views and for generating (SMILES, name,
|
|
149
|
+
description) training tuples:
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
from openclatura import describe
|
|
153
|
+
|
|
154
|
+
d = describe("CC(=O)Nc1ccccc1")
|
|
155
|
+
print(d) # multi-paragraph prose
|
|
156
|
+
d.rules_hit # ('P-44', 'P-45', 'P-41', 'P-61', 'P-67')
|
|
157
|
+
d.components[0] # DescribedComponent(phase='parse', text='RDKit parsed ...')
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Same input → same output. No LLM in the loop.
|
|
161
|
+
|
|
162
|
+
## Human-like description
|
|
163
|
+
|
|
164
|
+
Openclatura can generate uncanny human-like descriptions of molecules.
|
|
165
|
+
```python
|
|
166
|
+
|
|
167
|
+
from openclatura import describe_human
|
|
168
|
+
|
|
169
|
+
d = describe_human("CN1C=NC2=C1C(=O)N(C(=O)N2C)C")
|
|
170
|
+
print(d.text)
|
|
171
|
+
|
|
172
|
+
""" Processed SMILES: Cn1cnc2c1c(=O)n(C)c(=O)n2C
|
|
173
|
+
Atom ids in that SMILES: C{0}n{1}1c{2}n{3}c{4}2c{5}1c{6}(=O{7})n{8}(C{13})c{9}(=O{10})n{11}2C{12}
|
|
174
|
+
|
|
175
|
+
The molecule is named 2,4,7-trimethyl-2,4,7,9-tetraazabicyclo[4.3.0]nona-1(6),8-diene-3,5-dione.
|
|
176
|
+
|
|
177
|
+
The molecule is built around a 9-membered bicyclic [4.3.0] heteroskeleton.
|
|
178
|
+
Within that parent framework, there is nitrogen at positions 2 (atom id 11), 4 (atom id 8), 7 (atom id 1), and 9 (atom id 3).
|
|
179
|
+
Within that parent framework, there is a double bond between position 1 (atom id 4) and position 6 (atom id 5) and a double bond between position 8 (atom id 2) and position 9 (atom id 3).
|
|
180
|
+
The principal characteristic feature is oxo groups at positions 3 (atom id 9) and 5 (atom id 6).
|
|
181
|
+
Attached to this framework are methyl groups at positions 2 (atom id 11), 4 (atom id 8), and 7 (atom id 1). """
|
|
182
|
+
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Development
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
git clone https://github.com/lamalab-org/iupac-name-generator
|
|
189
|
+
cd iupac-name-generator
|
|
190
|
+
pip install -e ".[dev]"
|
|
191
|
+
|
|
192
|
+
# run the unit + round-trip tests
|
|
193
|
+
pytest
|
|
194
|
+
|
|
195
|
+
# run only fast tests
|
|
196
|
+
pytest -m "not slow and not dataset and not golden"
|
|
197
|
+
|
|
198
|
+
# strict RDKit-version regression suite (also runs in the rdkit-compat CI job)
|
|
199
|
+
pytest -m golden
|
|
200
|
+
|
|
201
|
+
# lint and format
|
|
202
|
+
ruff check --fix src/openclatura
|
|
203
|
+
ruff format src/openclatura
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Java is required for the OPSIN-based round-trip checks (see `py2opsin`).
|
|
207
|
+
|
|
208
|
+
## HTTP service (Docker)
|
|
209
|
+
|
|
210
|
+
The `[web]` extra ships a FastAPI app with `name`, `batch`, `describe`
|
|
211
|
+
and `healthz` endpoints. The bundled `Dockerfile` includes a headless JRE
|
|
212
|
+
so `verify_opsin=True` works out of the box.
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
# build + run
|
|
216
|
+
docker build -t openclatura:local .
|
|
217
|
+
docker run --rm -p 8000:8000 openclatura:local
|
|
218
|
+
|
|
219
|
+
# or via compose
|
|
220
|
+
docker compose -f docker/compose.yaml up --build
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Call the API:
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
curl -X POST localhost:8000/name -H 'content-type: application/json' \
|
|
227
|
+
-d '{"smiles":"CC(=O)Nc1ccccc1","include_trace":true,"verify_opsin":true}'
|
|
228
|
+
|
|
229
|
+
curl -X POST localhost:8000/batch -H 'content-type: application/json' \
|
|
230
|
+
-d '{"smiles":["CCO","c1ccccc1","CC(=O)O"],"processes":1}'
|
|
231
|
+
|
|
232
|
+
curl -X POST localhost:8000/describe -H 'content-type: application/json' \
|
|
233
|
+
-d '{"smiles":"CC(=O)Nc1ccccc1"}'
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
OpenAPI docs are served at `http://localhost:8000/docs`.
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
## License
|
|
240
|
+
|
|
241
|
+
MIT. See `LICENSE`.
|