Skip to content

feat: add naming feature from rdkit mol obj - #43

Merged
AdrianM0 merged 1 commit into
mainfrom
name_rdkit_mol
Jul 21, 2026
Merged

feat: add naming feature from rdkit mol obj#43
AdrianM0 merged 1 commit into
mainfrom
name_rdkit_mol

Conversation

@AdrianM0

@AdrianM0 AdrianM0 commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

Summary by Sourcery

Introduce RDKit molecule–based naming and analysis alongside existing SMILES APIs, allowing callers to bypass SMILES round-trips while maintaining compatibility with verification and batch processing workflows.

New Features:

  • Add support for naming and analyzing existing RDKit Mol objects, including batch APIs that accept mixtures of SMILES and RDKit molecules

Enhancements:

  • Refactor the naming engine to accept a molecule object as primary input, deferring SMILES generation until needed (e.g., for OPSIN verification) and preserving caller molecules unmodified
  • Extend graph I/O to build the internal molecule representation directly from RDKit Mol inputs, including unsanitized and SD-style molecules with explicit hydrogens

Documentation:

  • Update README to document RDKit-based naming functions and mixed SMILES/RDKit batch usage

Tests:

  • Add tests covering RDKit molecule input, SD-style and unsanitized molecules, OPSIN verification behavior, and mixed SMILES/molecule batches

Summary by CodeRabbit

  • New Features

    • Added support for generating IUPAC names directly from RDKit molecules.
    • Added analysis and trace-enabled naming options for RDKit inputs.
    • Batch naming now accepts mixed SMILES strings and RDKit molecules.
    • Added safeguards to preserve input molecules, including explicit hydrogens.
    • Expanded public API exports and updated the package version to 0.1.5.
  • Documentation

    • Added usage examples and guidance for naming existing RDKit molecules.

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The naming pipeline now accepts existing RDKit molecules directly, converts them into internal graphs without mutating callers, supports tracing, analysis, verification, and mixed-input batches, and exposes the new entry points through the public API and README.

Changes

RDKit molecule naming

Layer / File(s) Summary
RDKit graph ingestion
src/openclatura/graph_io.py
Adds RDKit molecule conversion with copying, perception, explicit-hydrogen handling, Kekulization, and shared internal graph construction.
Unified engine input flow
src/openclatura/engine.py
Unifies SMILES and RDKit inputs, adds direct naming and analysis methods, derives verification SMILES when required, and supports mixed batch inputs in sequential and multiprocessing paths.
Public RDKit naming API
src/openclatura/__init__.py, src/openclatura/namer.py
Exports RDKit naming, tracing, analysis, typed-result, and batch entry points, and updates the package version.
RDKit behavior validation
src/openclatura/tests/test_public_api.py, README.md
Tests RDKit naming behavior and documents direct molecule, typed result, trace, analysis, and mixed-batch usage.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant NamingEngine
  participant read_rdkit_mol
  participant MoleculeGraph
  Caller->>NamingEngine: name_rdkit_mol(rdkit_mol)
  NamingEngine->>read_rdkit_mol: convert RDKit Mol
  read_rdkit_mol->>MoleculeGraph: build prepared Molecule
  NamingEngine->>MoleculeGraph: name connected components
  MoleculeGraph-->>NamingEngine: generated name
  NamingEngine-->>Caller: return name
Loading

Suggested reviewers: r-fedorov

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding naming support for existing RDKit Mol objects.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch name_rdkit_mol

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sourcery-ai

sourcery-ai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Reviewer's Guide

Add RDKit molecule-based naming and analysis APIs, refactor the naming engine to accept either SMILES or RDKit Mol input (including mixed batches), and extend graph I/O to safely build internal molecule graphs from RDKit molecules, while updating public exports, version, docs, and tests.

File-Level Changes

Change Details Files
Allow the naming engine to accept RDKit Mol input alongside SMILES, including batch processing and trace/analysis paths.
  • Extend NamingRequest to carry an rdkit_mol field and make smiles optional.
  • Add NamingEngine methods to name and analyze RDKit molecules, including trace-capable variants.
  • Refactor NamingEngine.run to prepare input via a shared _prepare_input helper that builds Molecule from either SMILES or RDKit Mol and lazily derives SMILES only when needed for OPSIN verification.
  • Update name_many and multiprocessing helpers to accept a mixed iterable of SMILES strings and RDKit molecules via a new _request_for helper.
  • Change _name and _analyze to operate on prebuilt Molecule objects instead of always reading from SMILES.
src/openclatura/engine.py
Support building internal Molecule graphs directly from RDKit molecules, handling unsanitized inputs, ring perception, and explicit hydrogens without mutating the caller’s molecule.
  • Add read_rdkit_mol that copies (optionally) and prepares an RDKit Mol, including property cache, ring perception, hydrogen removal, and kekulization, then delegates to a common builder.
  • Factor out _ensure_perception to perform minimal RDKit property cache updates and ring finding for external molecules.
  • Factor out _build_molecule to construct the internal Molecule graph from a prepared RDKit Mol and associated atom metadata, and reuse it from read_smiles.
src/openclatura/graph_io.py
Expose new RDKit-based public APIs, broaden batch naming input types, and bump the package version.
  • Introduce name_mol as the RDKit-molecule analogue of name, returning NamingResult and deferring SMILES generation unless OPSIN verification is requested.
  • Update name_many to accept Iterable[str
Any] so callers can mix SMILES and RDKit molecules in a single batch.
  • Re-export analyze_rdkit_mol, name_rdkit_mol, and name_rdkit_mol_with_trace from the top-level package and include them in all.
  • Bump version from 0.1.0 to 0.1.5 to reflect the new feature set.
  • Add RDKit-molecule focused naming helpers and extend documentation to describe RDKit-based APIs and mixed-input batches.
    • Add namer-layer convenience functions name_rdkit_mol, name_rdkit_mol_with_trace, and analyze_rdkit_mol that delegate to the NamingEngine RDKit methods.
    • Extend README with a new section and example code for naming existing RDKit molecules, using name_rdkit_mol, name_mol, and name_many with mixed inputs, and highlighting immutability and explicit-hydrogen handling.
    src/openclatura/namer.py
    README.md
    Add tests to validate RDKit molecule naming parity, molecule immutability, explicit-hydrogen handling, OPSIN verification behavior, RDKit trace/analysis parity, and mixed SMILES/RDKit batch support.
    • Add helper _mol in tests to construct RDKit molecules from SMILES.
    • Test that name_rdkit_mol produces the same result as name_smiles for a given structure and that the caller’s molecule remains aromatic and unchanged after naming.
    • Test name_mol behavior regarding NamingResult, lazy SMILES population, and OPSIN verification-induced SMILES generation via monkeypatching verify_with_opsin.
    • Test RDKit molecule reading for SD-style explicit hydrogens and unsanitized molecules, including None input producing an empty name.
    • Test parity between RDKit-based and SMILES-based trace and analysis paths, and that name_many correctly processes mixed lists of RDKit molecules and SMILES strings.
    • Import and assert behavior of new public helpers in the public API test module.
    src/openclatura/tests/test_public_api.py

    Assessment against linked issues

    Issue Objective Addressed Explanation
    #41 Provide a Python API that accepts an existing rdkit.Chem.rdchem.Mol object (including molecules read from SD files) to generate a name, without requiring SMILES input.
    #41 Ensure RDKit-molecule-based naming avoids unnecessary SMILES round-trips and does not modify the caller’s RDKit molecule, including support for SD-style explicit hydrogens and unsanitized molecules.
    #41 Document the new RDKit molecule-based APIs and usage in the README / public interface.

    Possibly linked issues


    Tips and commands

    Interacting with Sourcery

    • Trigger a new review: Comment @sourcery-ai review on the pull request.
    • Continue discussions: Reply directly to Sourcery's review comments.
    • Generate a GitHub issue from a review comment: Ask Sourcery to create an
      issue from a review comment by replying to it. You can also reply to a
      review comment with @sourcery-ai issue to create an issue from it.
    • Generate a pull request title: Write @sourcery-ai anywhere in the pull
      request title to generate a title at any time. You can also comment
      @sourcery-ai title on the pull request to (re-)generate the title at any time.
    • Generate a pull request summary: Write @sourcery-ai summary anywhere in
      the pull request body to generate a PR summary at any time exactly where you
      want it. You can also comment @sourcery-ai summary on the pull request to
      (re-)generate the summary at any time.
    • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
      request to (re-)generate the reviewer's guide at any time.
    • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
      pull request to resolve all Sourcery comments. Useful if you've already
      addressed all the comments and don't want to see them anymore.
    • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
      request to dismiss all existing Sourcery reviews. Especially useful if you
      want to start fresh with a new review - don't forget to comment
      @sourcery-ai review to trigger a new review!

    Customizing Your Experience

    Access your dashboard to:

    • Enable or disable review features such as the Sourcery-generated pull request
      summary, the reviewer's guide, and others.
    • Change the review language.
    • Add, remove or edit custom review instructions.
    • Adjust other review settings.

    Getting Help

    @AdrianM0 AdrianM0 linked an issue Jul 21, 2026 that may be closed by this pull request
    @AdrianM0
    AdrianM0 marked this pull request as ready for review July 21, 2026 21:15

    @sourcery-ai sourcery-ai Bot left a comment

    Copy link
    Copy Markdown

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    Hey - I've found 1 issue, and left some high level feedback:

    • The new public APIs and engine methods all type rdkit_mol as Any; consider tightening these annotations (e.g., to Chem.Mol | None) so misuse is caught earlier and IDEs can provide better assistance.
    • In name_many, the Iterable[str | Any] and _request_for logic will treat any non-string item as an RDKit molecule; it may be safer to explicitly check for Chem.Mol (or a protocol) rather than a broad Any to avoid misinterpreting other object types.
    • The multiprocessing worker _name_one_for_worker now accepts tuple[str | Any, bool, bool, bool]; making this signature consistent with the narrowed RDKit types and documenting the expected item types would reduce ambiguity around what is pickled and sent to workers.
    Prompt for AI Agents
    Please address the comments from this code review:
    
    ## Overall Comments
    - The new public APIs and engine methods all type `rdkit_mol` as `Any`; consider tightening these annotations (e.g., to `Chem.Mol | None`) so misuse is caught earlier and IDEs can provide better assistance.
    - In `name_many`, the `Iterable[str | Any]` and `_request_for` logic will treat any non-string item as an RDKit molecule; it may be safer to explicitly check for `Chem.Mol` (or a protocol) rather than a broad `Any` to avoid misinterpreting other object types.
    - The multiprocessing worker `_name_one_for_worker` now accepts `tuple[str | Any, bool, bool, bool]`; making this signature consistent with the narrowed RDKit types and documenting the expected item types would reduce ambiguity around what is pickled and sent to workers.
    
    ## Individual Comments
    
    ### Comment 1
    <location path="src/openclatura/engine.py" line_range="415-416" />
    <code_context>
         )
    
    
     def _run_parallel(
    -    smiles_list: list[str],
    +    smiles_list: list[str | Any],
         *,
         include_trace: bool,
    </code_context>
    <issue_to_address>
    **issue (bug_risk):** Multiprocessing with RDKit molecules is likely to fail because RDKit Mol objects are not picklable.
    
    `name_many` now accepts RDKit `Mol` instances in `smiles_iter`, and `_run_parallel` passes these directly to `multiprocessing.Pool.map` via `_name_one_for_worker`. Because RDKit `Mol` objects are not picklable, any parallel run (`processes != 1`) with molecules will likely fail with pickling errors. To avoid this, either restrict parallel mode to SMILES-only inputs (and enforce/document that), or convert `Mol` objects to a picklable form (e.g., SMILES) before sending them to worker processes.
    </issue_to_address>

    Sourcery is free for open source - if you like our reviews please consider sharing them ✨
    Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

    Comment thread src/openclatura/engine.py
    @AdrianM0 AdrianM0 changed the title add naming feature from rdkit mol obj feat: add naming feature from rdkit mol obj Jul 21, 2026
    @AdrianM0
    AdrianM0 merged commit 08d0a73 into main Jul 21, 2026
    11 of 12 checks passed

    @coderabbitai coderabbitai Bot left a comment

    Copy link
    Copy Markdown

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    Actionable comments posted: 1

    🧹 Nitpick comments (2)
    src/openclatura/engine.py (1)

    311-312: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

    Trace phase text is now misleading for RDKit-molecule input.

    _analyze now accepts a pre-built Molecule graph from either SMILES or an RDKit Mol (per the _prepare_input refactor), but the first TracePhase.PARSE decision it emits still hardcodes "parsed SMILES" / "RDKit parsing populated..." and records data={"smiles": smiles, ...} — for analyze_rdkit_mol/name_rdkit_mol_with_trace calls without verify_opsin, smiles is "", so the trace claims a SMILES was parsed with an empty SMILES value, which is confusing for consumers of the explainability trace (a headline feature of this PR).

    Consider branching the wording (e.g. "parsed RDKit molecule" when request.rdkit_mol was the source) so the trace accurately reflects the input path, since analyze_rdkit_mol is explicitly meant to mirror analyze_smiles's explainability contract.

    🤖 Prompt for AI Agents
    Verify each finding against current code. Fix only still-valid issues, skip the
    rest with a brief reason, keep changes minimal, and validate.
    
    In `@src/openclatura/engine.py` around lines 311 - 312, Update the initial
    TracePhase.PARSE decision in _analyze to distinguish RDKit-molecule input from
    SMILES input, using wording and trace data that accurately identify the source;
    preserve the existing SMILES details for SMILES analysis and emit RDKit-specific
    text when request.rdkit_mol is the source, including cases where smiles is
    empty.
    
    src/openclatura/graph_io.py (1)

    28-63: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

    Add a stereo regression test for the explicit-H path. The current explicit-H test only exercises benzoic acid, so it doesn’t cover a stereocenter whose configuration depends on an explicit hydrogen. Add a case built from Chem.AddHs/MolToMolBlock/MolFromMolBlock and assert name_rdkit_mol matches the SMILES path.

    🤖 Prompt for AI Agents
    Verify each finding against current code. Fix only still-valid issues, skip the
    rest with a brief reason, keep changes minimal, and validate.
    
    In `@src/openclatura/graph_io.py` around lines 28 - 63, Add a regression test for
    the explicit-hydrogen path in read_rdkit_mol using a stereocenter whose
    configuration depends on an explicit hydrogen. Build the molecule through
    Chem.AddHs, serialize with MolToMolBlock, reload with MolFromMolBlock, and
    assert name_rdkit_mol produces the same result as the corresponding SMILES-based
    molecule.
    
    🤖 Prompt for all review comments with AI agents
    Verify each finding against current code. Fix only still-valid issues, skip the
    rest with a brief reason, keep changes minimal, and validate.
    
    Inline comments:
    In `@README.md`:
    - Around line 96-116: Update the “Naming an existing RDKit molecule” example so
    the calls to name_mol and name_many use a separately defined, known-valid
    molecule rather than the for-loop variable mol. Keep the SD supplier loop
    focused on naming each non-None record and ensure the batch example still
    demonstrates both RDKit-molecule and SMILES inputs.
    
    ---
    
    Nitpick comments:
    In `@src/openclatura/engine.py`:
    - Around line 311-312: Update the initial TracePhase.PARSE decision in _analyze
    to distinguish RDKit-molecule input from SMILES input, using wording and trace
    data that accurately identify the source; preserve the existing SMILES details
    for SMILES analysis and emit RDKit-specific text when request.rdkit_mol is the
    source, including cases where smiles is empty.
    
    In `@src/openclatura/graph_io.py`:
    - Around line 28-63: Add a regression test for the explicit-hydrogen path in
    read_rdkit_mol using a stereocenter whose configuration depends on an explicit
    hydrogen. Build the molecule through Chem.AddHs, serialize with MolToMolBlock,
    reload with MolFromMolBlock, and assert name_rdkit_mol produces the same result
    as the corresponding SMILES-based molecule.
    
    🪄 Autofix (Beta)

    Fix all unresolved CodeRabbit comments on this PR:

    • Push a commit to this branch (recommended)
    • Create a new PR with the fixes

    ℹ️ Review info
    ⚙️ Run configuration

    Configuration used: defaults

    Review profile: CHILL

    Plan: Pro

    Run ID: 099f3e9c-f794-4768-9c8c-7640bc3b6921

    📥 Commits

    Reviewing files that changed from the base of the PR and between f3c308f and c41bc17.

    📒 Files selected for processing (6)
    • README.md
    • src/openclatura/__init__.py
    • src/openclatura/engine.py
    • src/openclatura/graph_io.py
    • src/openclatura/namer.py
    • src/openclatura/tests/test_public_api.py

    Comment thread README.md
    Comment on lines +96 to +116
    ### Naming an existing RDKit molecule

    If you already hold an `rdkit.Chem.rdchem.Mol` — from an SD file, a reaction,
    or an earlier step in a pipeline — skip the SMILES round-trip:

    ```python
    from rdkit import Chem
    from openclatura import name_rdkit_mol, name_mol, name_many

    for mol in Chem.SDMolSupplier("compounds.sdf"):
    if mol is not None:
    print(name_rdkit_mol(mol)) # -> 'benzoic acid'

    name_mol(mol) # typed NamingResult, as `name`
    name_many([mol, "CCO"]) # batches take either form
    ```

    The input molecule is never modified, explicit hydrogens (as SD files usually
    carry them) are handled, and `name_rdkit_mol_with_trace` / `analyze_rdkit_mol`
    mirror their SMILES counterparts.

    Copy link
    Copy Markdown

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

    Example reuses the for loop's leaked variable, which can be None or the wrong molecule.

    for mol in Chem.SDMolSupplier("compounds.sdf"):
        if mol is not None:
            print(name_rdkit_mol(mol))          # -> 'benzoic acid'
    
    name_mol(mol)                                # typed NamingResult, as `name`
    name_many([mol, "CCO"])                      # batches take either form

    After the loop, mol is whatever the supplier yielded on its last iteration — including None if the last record failed to parse — not a value guarded by the if mol is not None check. Copy-pasting this as-is can silently call name_mol/name_many on None or an unintended molecule.

    📝 Proposed fix
    -for mol in Chem.SDMolSupplier("compounds.sdf"):
    -    if mol is not None:
    -        print(name_rdkit_mol(mol))          # -> 'benzoic acid'
    -
    -name_mol(mol)                                # typed NamingResult, as `name`
    -name_many([mol, "CCO"])                      # batches take either form
    +mols = [m for m in Chem.SDMolSupplier("compounds.sdf") if m is not None]
    +for mol in mols:
    +    print(name_rdkit_mol(mol))          # -> 'benzoic acid'
    +
    +name_mol(mols[0])                            # typed NamingResult, as `name`
    +name_many([mols[0], "CCO"])                  # batches take either form
    🤖 Prompt for AI Agents
    Verify each finding against current code. Fix only still-valid issues, skip the
    rest with a brief reason, keep changes minimal, and validate.
    
    In `@README.md` around lines 96 - 116, Update the “Naming an existing RDKit
    molecule” example so the calls to name_mol and name_many use a separately
    defined, known-valid molecule rather than the for-loop variable mol. Keep the SD
    supplier loop focused on naming each non-None record and ensure the batch
    example still demonstrates both RDKit-molecule and SMILES inputs.
    

    @AdrianM0
    AdrianM0 deleted the name_rdkit_mol branch July 23, 2026 09:27
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

    Labels

    None yet

    Projects

    None yet

    Development

    Successfully merging this pull request may close these issues.

    Possible to supply an RDKit molecule or SD file instead of SMILES?

    1 participant