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
2 changes: 1 addition & 1 deletion .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
version: "0.5.27"

- name: Set up Python
run: uv python install 3.10
run: uv python install 3.11

- name: Build docs and check for warnings
env:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ jobs:
fail-fast: true
matrix:
os: [ubuntu-latest, windows-latest]
python-version: ["3.10", "3.11", "3.12", "3.13"]
python-version: ["3.11", "3.12", "3.13"]

needs: [code-formatting-syntax-and-docstrings]
steps:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Python 3.10
- name: Set up Python 3.11
uses: actions/setup-python@v5
with:
python-version: '3.10'
python-version: '3.11'

- name: Install pypa/setuptools
run:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test_docs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
version: "0.5.27"

- name: Set up Python
run: uv python install 3.10
run: uv python install 3.11

- name: Build docs and check for warnings
env:
Expand Down
1 change: 0 additions & 1 deletion docs/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ in your project root.

| Python | macOS | Windows | Ubuntu |
| -- | -- | -- | -- |
| 3.10| | ✅ | ✅ |
| 3.11| | ✅ | ✅ |
| 3.12| | ✅ | ✅ |
| 3.13 | ✅ | ✅ | ✅ |
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ classifiers = [
"Programming Language :: Python :: 3",
"Topic :: Utilities",
]
requires-python = ">=3.10,<3.14"
requires-python = ">=3.11,<3.14"
dependencies = [
"beartype",
"cryptography",
Expand Down
68 changes: 35 additions & 33 deletions src/drug_discovery/structures/ligand.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from pathlib import Path
import random
import tempfile
from typing import Any, Literal, Optional
from typing import Any, Literal, Optional, Self
import warnings

from beartype import beartype
Expand Down Expand Up @@ -37,6 +37,7 @@


@dataclass
@beartype
class Ligand(Entity):
"""A class representing a ligand molecule in drug discovery workflows.

Expand Down Expand Up @@ -68,14 +69,13 @@ class Ligand(Entity):
_preferred_ext = ".sdf"

@classmethod
@beartype
def from_rdkit_mol(
cls,
mol: Chem.rdchem.Mol,
name: Optional[str] = None,
save_to_file: bool = False,
**kwargs: Any,
):
) -> Self:
"""
Create a Ligand instance from an RDKit Mol object.

Expand Down Expand Up @@ -108,7 +108,7 @@ def from_smiles(
name: str = "",
save_to_file: bool = False,
**kwargs: Any,
) -> "Ligand":
) -> Self:
"""
Create a Ligand instance from a SMILES string.

Expand Down Expand Up @@ -152,7 +152,7 @@ def from_block_content(
name: str = "",
save_to_file: bool = False,
**kwargs: Any,
) -> "Ligand":
) -> Self:
"""
Create a Ligand instance from block content.

Expand Down Expand Up @@ -182,7 +182,7 @@ def from_block_content(
def from_identifier(
cls,
identifier: str,
):
) -> Self:
"""
Create a Ligand instance from a compound name.

Expand Down Expand Up @@ -213,14 +213,13 @@ def from_identifier(
return cls(mol=mol, name=identifier)

@classmethod
@beartype
def from_base64(
cls,
base64_string: str,
name: str = "",
save_to_file: bool = False,
**kwargs: Any,
) -> "Ligand":
) -> Self:
"""
Create a Ligand instance from a base64 encoded SDF string.

Expand Down Expand Up @@ -271,11 +270,11 @@ def from_base64(
@classmethod
def from_sdf(
cls,
file_path: str,
file_path: str | Path,
*,
sanitize: bool = True,
remove_hydrogens: bool = False,
) -> "Ligand":
) -> Self:
Comment on lines +273 to +277
Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

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

The parameter type annotation was changed to accept both str and Path (line 273), but the docstring on line 282 (not shown in this diff but part of the method) likely still states file_path (str). Consider updating the docstring to reflect the new type: file_path (str | Path).

Copilot uses AI. Check for mistakes.
"""
Create a single Ligand instance from an SDF file containing exactly one molecule.

Expand Down Expand Up @@ -350,8 +349,7 @@ def process_mol(self) -> None:

self.mol = stripped_mol

@beartype
def prepare(self, *, remove_hydrogens: bool = False) -> "Ligand":
def prepare(self, *, remove_hydrogens: bool = False) -> Self:
"""Prepare the ligand for downstream workflows.

The routine performs the following using RDKit and internal utilities:
Expand Down Expand Up @@ -852,7 +850,7 @@ def draw(self):

return MolToImage(self.mol)

def show(self) -> str:
def show(self) -> str | None:
"""
Visualize the current state of the ligand molecule.

Expand Down Expand Up @@ -1076,6 +1074,7 @@ def ligands_to_dataframe(ligands: list[Ligand]):


@dataclass
@beartype
class LigandSet:
"""
A class representing a set of Ligand objects.
Expand Down Expand Up @@ -1146,8 +1145,7 @@ def __radd__(self, other):
else:
return NotImplemented

@beartype
def random_sample(self, n: int) -> "LigandSet":
def random_sample(self, n: int) -> Self:
"""
Return a new LigandSet containing n randomly selected ligands.

Expand Down Expand Up @@ -1335,9 +1333,9 @@ def from_rdkit_mols(cls, mols: list[Chem.rdchem.Mol]):
@classmethod
def from_csv(
cls,
file_path: str,
file_path: str | Path,
smiles_column: str = "smiles",
) -> "LigandSet":
) -> Self:
"""
Create a LigandSet instance from a CSV file containing SMILES strings and additional properties.

Expand Down Expand Up @@ -1435,11 +1433,11 @@ def from_csv(
@classmethod
def from_sdf(
cls,
file_path: str,
file_path: str | Path,
*,
sanitize: bool = True,
remove_hydrogens: bool = False,
) -> "LigandSet":
) -> Self:
"""
Create a LigandSet instance from an SDF file containing one or more molecules.

Expand Down Expand Up @@ -1496,7 +1494,7 @@ def from_sdf_files(
*,
sanitize: bool = True,
remove_hydrogens: bool = False,
) -> "LigandSet":
) -> Self:
"""
Create a LigandSet instance from multiple SDF files by concatenating them together.

Expand Down Expand Up @@ -1540,27 +1538,33 @@ def from_sdf_files(
return cls(ligands=all_ligands)

@classmethod
def from_dir(cls, directory: str) -> "LigandSet":
def from_dir(cls, directory: str | Path) -> Self:
"""
Create a LigandSet instance from a directory containing SDF files.
"""
sdf_files = [f for f in os.listdir(directory) if f.endswith(".sdf")]
directory = Path(directory)
if not directory.is_dir():
raise ValueError(f"{directory} is not a valid directory")

ligands = []

# Process SDF files
sdf_files = list(directory.glob("*.sdf"))
for sdf_file in sdf_files:
this_file = os.path.join(directory, sdf_file)
this_file = str(sdf_file)
this_set = cls.from_sdf(this_file)
for ligand in this_set.ligands:
ligand.file_path = this_file
ligands.extend(this_set.ligands)

# now get all CSV files
csv_files = [f for f in os.listdir(directory) if f.endswith(".csv")]
# Process CSV files
csv_files = list(directory.glob("*.csv"))
for csv_file in csv_files:
this_file = os.path.join(directory, csv_file)
this_file = str(csv_file)
this_set = cls.from_csv(this_file)
for ligand in this_set.ligands:
ligand.file_path = this_file
ligands.extend(this_set)
ligands.extend(this_set.ligands)

return cls(ligands=ligands)

Expand All @@ -1570,7 +1574,7 @@ def add_hydrogens(self) -> None:
ligand.add_hydrogens()

@jupyter_visualization
def show(self):
def show(self) -> str | None:
"""
Visualize all ligands in this LigandSet in 3D
"""
Expand Down Expand Up @@ -1715,9 +1719,8 @@ def to_smiles(self) -> list[str]:
"""
return [ligand.smiles for ligand in self.ligands]

@beartype
@classmethod
def from_smiles(cls, smiles: list[str] | set[str]) -> "LigandSet":
def from_smiles(cls, smiles: list[str] | set[str]) -> Self:
"""
Create a LigandSet from a list of SMILES strings.
"""
Expand Down Expand Up @@ -1771,7 +1774,7 @@ def to_rdkit_mols(self) -> list[Chem.Mol]:
"""
return [ligand.mol for ligand in self.ligands]

def mcs(self) -> str:
def mcs(self) -> Chem.Mol:
Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

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

The return type annotation was changed from str to Chem.Mol. This appears to be a typing improvement, but the method's docstring (not shown in this diff but exists in the codebase) may need to be updated to reflect this change if it previously indicated a string return type.

Copilot uses AI. Check for mistakes.
"""
Generates the Most Common Substructure (MCS) for ligands in a LigandSet

Expand All @@ -1784,8 +1787,7 @@ def mcs(self) -> str:

return align.mcs(self.to_rdkit_mols())

@beartype
def filter_top_poses(self, *, by_pose_score: bool = False) -> "LigandSet":
def filter_top_poses(self, *, by_pose_score: bool = False) -> Self:
"""
Filter ligands to keep only the best pose for each unique molecule.

Expand Down
8 changes: 4 additions & 4 deletions src/drug_discovery/structures/pocket.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from dataclasses import dataclass, field
import os
from pathlib import Path
from typing import Any, Optional
from typing import Any, Optional, Self

import numpy as np
import pandas as pd
Expand Down Expand Up @@ -79,7 +79,7 @@ def from_pdb_file(
pdb_file_path: str | Path,
name: Optional[str] = None,
**kwargs: Any,
) -> "Pocket":
) -> Self:
"""
Create a Pocket instance from a PDB file.

Expand Down Expand Up @@ -173,7 +173,7 @@ def from_residue_number(
residue_number: int,
chain_id: str | None = None,
cutoff: float = 5.0,
) -> "Pocket":
) -> Self:
"""
Creates a pocket centered on a given residue (by number)

Expand Down Expand Up @@ -331,7 +331,7 @@ def from_ligand(
cls,
ligand: "Ligand",
name: Optional[str] = None,
) -> "Pocket":
) -> Self:
"""
Create a Pocket instance from a Ligand instance.
"""
Expand Down
Loading
Loading