From b0fc55d8992d6d9c9c139729bddc8eaf3f548cec Mon Sep 17 00:00:00 2001 From: Ramil Nugmanov Date: Sun, 7 Dec 2025 12:10:41 +0100 Subject: [PATCH 1/6] added depiction of extended stereo --- chython/algorithms/depict.py | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/chython/algorithms/depict.py b/chython/algorithms/depict.py index 46e8b20e..6e45fce0 100644 --- a/chython/algorithms/depict.py +++ b/chython/algorithms/depict.py @@ -55,7 +55,7 @@ 'span_size': .35, 'other_size': 0.3, 'monochrome': False, 'bond_color': 'black', 'bond_width': .04, 'other_color': 'black', 'bond_radius': .02, 'atom_radius': -.2, 'mapping_size': .25, 'atoms_colors': cpk, 'triple_space': .13, 'double_space': .06, 'mapping_color': '#0305A7', - 'aromatic_space': .14, 'aromatic_dashes': (.15, .05), 'dx_m': .05, 'dy_m': .2, + 'aromatic_space': .14, 'aromatic_dashes': (.15, .05), 'dx_m': .05, 'dy_m': .2, 'dx_s': .05, 'dy_s': .1, 'other_font_style': 'monospace', 'dx_ci': .05, 'dy_ci': 0.2, 'symbols_font_style': 'sans-serif', 'mapping_font_style': 'monospace', 'wedge_space': .08, 'arrow_color': 'black'} @@ -164,11 +164,12 @@ def depict_settings(*, carbon: bool = False, aam: bool = True, monochrome: bool bond_color: str = 'black', aam_color: str = '#0305A7', atoms_colors: tuple = cpk, bond_width: float = .04, wedge_space: float = .08, dashes: Tuple[float, float] = (.2, .1), aromatic_dashes: Tuple[float, float] = (.15, .05), dx_ci: float = .05, dy_ci: float = .2, - dx_m: float = .05, dy_m: float = .2, span_dy: float = .15, double_space: float = .06, - triple_space: float = .13, aromatic_space: float = .14, atom_radius: float = .2, bond_radius=.02, - font_size: float = .5, other_size: float = .3, span_size: float = .35, aam_size: float = .25, - symbols_font_style: str = 'sans-serif', other_font_style: str = 'monospace', - other_color: str = 'black', arrow_color: str = 'black', mapping_font_style: str = 'monospace'): + dx_m: float = .05, dy_m: float = .2, dx_s: float = .05, dy_s: float = .1, span_dy: float = .15, + double_space: float = .06, triple_space: float = .13, aromatic_space: float = .14, + atom_radius: float = .2, bond_radius=.02, font_size: float = .5, other_size: float = .3, + span_size: float = .35, aam_size: float = .25, symbols_font_style: str = 'sans-serif', + other_font_style: str = 'monospace', other_color: str = 'black', arrow_color: str = 'black', + mapping_font_style: str = 'monospace'): """ Settings for depict of chemical structures @@ -198,6 +199,8 @@ def depict_settings(*, carbon: bool = False, aam: bool = True, monochrome: bool :param dy_ci: y-axis offset relative to the center of the atom symbol for radical, charges, isotope :param dx_m: x-axis offset relative to the center of the atom symbol for atom-to-atom mapping :param dy_m: y-axis offset relative to the center of the atom symbol for atom-to-atom mapping + :param dx_s: x-axis offset relative to the center of the atom symbol for extended stereo label + :param dy_s: y-axis offset relative to the center of the atom symbol for extended stereo label :param span_dy: y-axis offset relative to the center of the atom symbol for hydrogen count :param mapping_font_style: font style for mapping :param wedge_space: wedge bond width @@ -226,6 +229,7 @@ def depict_settings(*, carbon: bool = False, aam: bool = True, monochrome: bool _render_config['dx_m'], _render_config['dy_m'] = dx_m, dy_m _render_config['other_font_style'] = other_font_style _render_config['dx_ci'], _render_config['dy_ci'] = dx_ci, dy_ci + _render_config['dx_s'], _render_config['dy_s'] = dx_s, dy_s _render_config['symbols_font_style'] = symbols_font_style _render_config['mapping_font_style'] = mapping_font_style _render_config['wedge_space'] = wedge_space @@ -311,7 +315,7 @@ def __render_bonds(self: Union['MoleculeContainer', 'DepictMolecule']): nx, ny = atoms[n].xy mx, my = atoms[m].xy ny, my = -ny, -my - dx, dy = _rotate_vector(0, wedge_space, mx - nx, ny - my) + dx, dy = _rotate_vector(0, wedge_space, mx - nx, my - ny) svg.append(f' ') @@ -368,6 +372,7 @@ def __render_atoms(self: 'MoleculeContainer', uid): mapping_size = _render_config['mapping_size'] dx_m, dy_m = _render_config['dx_m'], _render_config['dy_m'] dx_ci, dy_ci = _render_config['dx_ci'], _render_config['dy_ci'] + dx_s, dy_s = _render_config['dx_s'], _render_config['dy_s'] symbols_font_style = _render_config['symbols_font_style'] span_dy = _render_config['span_dy'] other_font_style = _render_config['other_font_style'] @@ -412,6 +417,10 @@ def __render_atoms(self: 'MoleculeContainer', uid): if atom.isotope: others.append(f' {atom.isotope}') + if atom.stereo is not None and atom.extended_stereo: + label = f'&{atom.extended_stereo}' if atom.extended_stereo > 0 else f'o{-atom.extended_stereo}' + others.append(f' ' + f'{label}') if len(symbol) > 1: dx = font7 @@ -448,8 +457,13 @@ def __render_atoms(self: 'MoleculeContainer', uid): if mapping: maps.append(f' {n}') - elif mapping: - maps.append(f' {n}') + else: + if mapping: + maps.append(f' {n}') + if atom.stereo is not None and atom.extended_stereo: + label = f'&{atom.extended_stereo}' if atom.extended_stereo > 0 else f'o{-atom.extended_stereo}' + others.append(f' ' + f'{label}') if svg: # group atoms symbols if fill_zone: From 426dff59f183ce4358d225341be26c84d3211e0e Mon Sep 17 00:00:00 2001 From: Ramil Nugmanov Date: Tue, 9 Dec 2025 10:25:12 +0100 Subject: [PATCH 2/6] added amino SEM PG --- chython/reactor/deprotection.py | 5 +++++ pyproject.toml | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/chython/reactor/deprotection.py b/chython/reactor/deprotection.py index b293702b..22962538 100644 --- a/chython/reactor/deprotection.py +++ b/chython/reactor/deprotection.py @@ -325,6 +325,11 @@ 'c1ccccc1NC(=O)OCC[Si](C)(C)C', 'c1ccccc1N'), ) +_amine_sem = ( + ('[N;D2,D3:1]-;!@[C;D2;x2;z1][O;D2;x0]-[C;D2;z1;x1][C;D2;x1;z1][Si;D4;z1;x0]([C;D1])([C;D1])[C;D1]', '[A:1]', + 'CN(C)COCC[Si](C)(C)C', 'CNC'), +) + _amine_troc = ( # [Zn] ('[N;D2,D3:1]-;!@[C;z2;x3](=O)[O;D2;x0]-[C;D2][C;D4;x3]([Cl;D1])([Cl;D1])[Cl;D1]', '[A:1]', 'c1ccccc1NC(=O)OCC(Cl)(Cl)Cl', 'c1ccccc1N', 'c1ccccc1NC(=O)OC(C)C(Cl)(Cl)Cl'), diff --git a/pyproject.toml b/pyproject.toml index bd206d03..dcf0d6d5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = 'chython' -version = '2.13' +version = '2.14' description = 'Library for processing molecules and reactions in python way' authors = ['Ramil Nugmanov '] license = 'LGPLv3' From 372d904138c8ee8c0557e14492c71ee6a471c4dc Mon Sep 17 00:00:00 2001 From: Ramil Nugmanov Date: Sat, 13 Dec 2025 18:37:40 +0100 Subject: [PATCH 3/6] added XEC reaction template --- chython/reactor/reactions/__init__.py | 1 + chython/reactor/reactions/_xec_sp2_sp3.py | 47 +++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 chython/reactor/reactions/_xec_sp2_sp3.py diff --git a/chython/reactor/reactions/__init__.py b/chython/reactor/reactions/__init__.py index 78e2f9e9..3ee46ec3 100644 --- a/chython/reactor/reactions/__init__.py +++ b/chython/reactor/reactions/__init__.py @@ -29,6 +29,7 @@ from ._sonogashira import template as songashira_template from ._sulfonamidation import template as sulfonamidation_template from ._suzuki_miyaura import template as suzuki_miyaura_template +from ._xec_sp2_sp3 import template as xec_template from ..reactor import Reactor, fix_mapping_overlap from ... import smarts, ReactionContainer, MoleculeContainer diff --git a/chython/reactor/reactions/_xec_sp2_sp3.py b/chython/reactor/reactions/_xec_sp2_sp3.py new file mode 100644 index 00000000..3c225fae --- /dev/null +++ b/chython/reactor/reactions/_xec_sp2_sp3.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2025 Kostia Chernichenko +# This file is part of chython. +# +# chython is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, see . +# + + +template = { + 'name': 'XEC', + 'description': 'Cross-electrophile C-sp2-X C-sp3-X coupling reaction', + 'templates': [ + { + 'A': [ + # Hal-Ar + '[Cl,Br,I;D1:1]-[C;a:2]', + # Hal-pseudoaromatic, more specifically C5, C6 vinylic + '[Cl,Br,I;D1:1]-[C;z2;r5,r6:2]', + # Ar triflate + '[C;a:2]-[O;D2;x1:1]-[S;x3;D4:10](=[O:11])(=[O:12])-[C;D4:13](-[F;D1:14])(-[F;D1:15])-[F;D1:16]' + ], + 'B': [ + # sp3-C-X + '[Cl,Br,I;D1:3]-[C;z1:4]' + ], + 'product': '[A:2]-[A:4]', + 'alerts': [], + 'ufe': { + 'A': '[A:2][At;M]', + 'B': 3 + } + } + ], + 'alerts': [] +} From 843ebeeffc6a16f8bd8f8ae88136dc20da544a3a Mon Sep 17 00:00:00 2001 From: Ramil Nugmanov Date: Wed, 24 Dec 2025 17:02:50 +0100 Subject: [PATCH 4/6] fixed another tautomerization case --- chython/algorithms/standardize/_groups.py | 5 +++++ chython/algorithms/standardize/test/test_groups.py | 1 + 2 files changed, 6 insertions(+) diff --git a/chython/algorithms/standardize/_groups.py b/chython/algorithms/standardize/_groups.py index 58a5d52d..828a3c67 100644 --- a/chython/algorithms/standardize/_groups.py +++ b/chython/algorithms/standardize/_groups.py @@ -553,6 +553,11 @@ def _rules_single(): bonds_fix = ((1, 2, 1), (2, 3, 2)) rules.append((q, atom_fix, bonds_fix, True)) + q = smarts('[O,S;D1;z2;x0]=[C;D3;r5]1[N;D2;z1][A;z2]=[N][N;z1]1') + atom_fix = {} + bonds_fix = ((1, 2, 1), (2, 3, 2)) + rules.append((q, atom_fix, bonds_fix, True)) + # # fix pyraz-imin # diff --git a/chython/algorithms/standardize/test/test_groups.py b/chython/algorithms/standardize/test/test_groups.py index b44e3af5..c34cc505 100644 --- a/chython/algorithms/standardize/test/test_groups.py +++ b/chython/algorithms/standardize/test/test_groups.py @@ -76,6 +76,7 @@ ('C=C(O)O', 'CC(=O)O'), ('C=C(O)N', 'CC(=O)N'), ('OC=C', 'O=CC'), ('OC(C)=C', 'O=C(C)C'), ('O=C1N=CC=CC1', 'OC=1N=CC=CC=1'), ('OC=1N=CC=CC=1', 'OC=1N=CC=CC=1'), ('N=C1N=CC=CC1', 'NC=1N=CC=CC=1'), + ('CN1N=CNC1=O', 'CN1N=CN=C1O'), ('CN1N=CN=C1O', 'CN1N=CN=C1O'), ('S=C1NC=CN1', 'S=C1NC=CN1'), ('SC1=NC=CN1', 'S=C1NC=CN1'), ('S=C1NCCN1', 'S=C1NCCN1'), ('SC1=NCCN1', 'S=C1NCCN1'), ('CN=C1NC=CN1', 'CNC1=NC=CN1'), ('CNC1=NC=CN1', 'CNC1=NC=CN1'), ('CN=C1NCCN1', 'CNC1=NCCN1'), ('CNC1=NCCN1', 'CNC1=NCCN1'), ('S=C1NNC=C1', 'SC1=NNC=C1'), ('SC1=NNC=C1', 'SC1=NNC=C1'), ('S=C1NNCC1', 'S=C1NNCC1'), ('SC1=NNCC1', 'S=C1NNCC1'), From e99ddfebfef46a039ffaef268f0487f59b336145 Mon Sep 17 00:00:00 2001 From: Ramil Nugmanov Date: Tue, 30 Dec 2025 16:27:41 +0100 Subject: [PATCH 5/6] integrated OPSIN to convert IUPAC into molecule. integrated CDK/OpenBabel/Indigo for 2d layout generation. --- chython/__init__.py | 4 +- chython/algorithms/calculate2d/molecule.py | 27 ++++++- chython/containers/chimera.py | 92 ++++++++++++++++++++++ chython/containers/molecule.py | 3 +- chython/files/__init__.py | 5 +- chython/files/opsin.py | 46 +++++++++++ pyproject.toml | 7 +- 7 files changed, 178 insertions(+), 6 deletions(-) create mode 100644 chython/containers/chimera.py create mode 100644 chython/files/opsin.py diff --git a/chython/__init__.py b/chython/__init__.py index 97868a7a..803ae753 100644 --- a/chython/__init__.py +++ b/chython/__init__.py @@ -18,6 +18,7 @@ # You should have received a copy of the GNU Lesser General Public License # along with this program; if not, see . # +from os import getenv from typing import Literal from .algorithms.depict import depict_settings from .containers import * @@ -27,8 +28,9 @@ torch_device = 'cpu' # AAM model device. Change before first `reset_mapping` call! -clean2d_engine: Literal['smilesdrawer', 'rdkit'] = 'smilesdrawer' +clean2d_engine: Literal['rdkit', 'smilesdrawer', 'cdk', 'obabel', 'indigo'] = 'smilesdrawer' conformer_engine: Literal['rdkit', 'cdpkit'] = 'rdkit' +class_paths = [getenv('CDK_PATH', 'cdk.jar'), getenv('OPSIN_PATH', 'opsin.jar')] __all__ = [] diff --git a/chython/algorithms/calculate2d/molecule.py b/chython/algorithms/calculate2d/molecule.py index 6536b1f1..282451e1 100644 --- a/chython/algorithms/calculate2d/molecule.py +++ b/chython/algorithms/calculate2d/molecule.py @@ -47,7 +47,7 @@ class Calculate2DMolecule: _bonds: Dict[int, Dict[int, 'Bond']] def clean2d(self: Union['MoleculeContainer', 'Calculate2DMolecule'], - *, engine: Literal['rdkit', 'smilesdrawer'] = None): + *, engine: Literal['rdkit', 'smilesdrawer', 'cdk', 'obabel', 'indigo'] = None): """ Calculate 2d layout of graph. @@ -85,6 +85,31 @@ def clean2d(self: Union['MoleculeContainer', 'Calculate2DMolecule'], shift_x, shift_y = xy[0] for n, (x, y) in zip(order, xy): plane[n] = (x - shift_x, shift_y - y) + elif engine == 'cdk': + sdg = self._cdk_engine.layout.StructureDiagramGenerator() + sdg.setUseTemplates(False) + sdg.setMolecule(self.to_cdk()) + sdg.generateCoordinates() + mol = sdg.getMolecule() + + for i, n in enumerate(self.smiles_atoms_order): + xy = mol.getAtom(i).getPoint2d() + plane[n] = (xy.x, xy.y) + elif engine == 'obabel': + mol = self.to_openbabel() + assert self._obgen2d(mol), 'OpenBabel failed to generate 2d layout' + assert mol.NumAtoms() == len(self), 'OpenBabel modified molecule' + + for i, n in enumerate(self.smiles_atoms_order, 1): + xy = mol.GetAtom(i).GetVector() + plane[n] = (xy.GetX(), xy.GetY()) + elif engine == 'indigo': + mol = self.to_indigo() + assert not mol.layout(), 'Indigo failed to generate 2d layout' + + for n, a in zip(self.smiles_atoms_order, mol.iterateAtoms()): + x, y, _ = a.xyz() + plane[n] = (x, y) else: raise ValueError(f'Invalid clean2d engine: {engine}') bonds = [] diff --git a/chython/containers/chimera.py b/chython/containers/chimera.py new file mode 100644 index 00000000..0200a82c --- /dev/null +++ b/chython/containers/chimera.py @@ -0,0 +1,92 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2025 Ramil Nugmanov +# This file is part of chython. +# +# chython is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, see . +# +from CachedMethods import class_cached_property + + +class Chimera: + __slots__ = () + + def to_cdk(self): + """ + Convert molecule to CDK Molecule object. + + Due to translation through SMILES string, atom order is not preserved. + Use `self.smiles_atoms_order` to map atoms back. + """ + parser = self._cdk_engine.smiles.SmilesParser(self._cdk_engine.DefaultChemObjectBuilder.getInstance()) + return parser.parseSmiles(str(self)) + + def to_openbabel(self): + """ + Convert molecule to OpenBabel OBMol object. + + Due to translation through SMILES string, atom order is not preserved. + Use `self.smiles_atoms_order` to map atoms back. + """ + from openbabel import openbabel + + mol = openbabel.OBMol() + assert self._obparser(mol, str(self)), 'OpenBabel failed to parse smiles' + return mol + + def to_indigo(self): + """ + Convert molecule to Indigo molecule object. + + Due to translation through SMILES string, atom order is not preserved. + Use `self.smiles_atoms_order` to map atoms back. + """ + return self._indigo_engine.loadMolecule(str(self)) + + @class_cached_property + def _cdk_engine(self): + try: + from jpype import isJVMStarted, startJVM, JPackage + + if not isJVMStarted(): + from chython import class_paths + + startJVM('--enable-native-access=ALL-UNNAMED', classpath=class_paths) + + return JPackage('org').openscience.cdk + except (ImportError, AttributeError): + raise ImportError('Java/JPype/CDK.jar is not installed or broken. make sure CDK_PATH env variable is set') + + @class_cached_property + def _indigo_engine(self): + from indigo import Indigo + + return Indigo() + + @class_cached_property + def _obparser(self): + from openbabel import openbabel + + obparser = openbabel.OBConversion() + obparser.SetInFormat('smi') + return obparser.ReadString + + @class_cached_property + def _obgen2d(self): + from openbabel import openbabel + + return openbabel.OBOp.FindType('gen2D').Do + + +__all__ = ['Chimera'] diff --git a/chython/containers/molecule.py b/chython/containers/molecule.py index f695436d..291327df 100644 --- a/chython/containers/molecule.py +++ b/chython/containers/molecule.py @@ -25,6 +25,7 @@ from zlib import compress, decompress from .bonds import Bond, DynamicBond from .cgr import CGRContainer +from .chimera import Chimera from .graph import Graph from .rdkit import RDkit from ..algorithms.aromatics import Aromatize @@ -65,7 +66,7 @@ def _rotable_rules(): class MoleculeContainer(MoleculeStereo, Graph[Element, Bond], Morgan, Rings, MoleculeIsomorphism, Aromatize, StandardizeMolecule, MoleculeSmiles, DepictMolecule, Calculate2DMolecule, - Conformers, Fingerprints, Tautomers, RDkit, MCS, X3domMolecule): + Conformers, Fingerprints, Tautomers, RDkit, Chimera, MCS, X3domMolecule): __slots__ = ('_meta', '_name', '_conformers', '_changed', '_backup') def __init__(self): diff --git a/chython/files/__init__.py b/chython/files/__init__.py index e5b0778a..9b8d9823 100644 --- a/chython/files/__init__.py +++ b/chython/files/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright 2014-2023 Ramil Nugmanov +# Copyright 2014-2025 Ramil Nugmanov # This file is part of chython. # # chython is free software; you can redistribute it and/or modify @@ -19,11 +19,12 @@ from .daylight import * from .libinchi import * from .MRVrw import * +from .opsin import * from .PDBrw import * from .RDFrw import * from .SDFrw import * from .xyz import * -__all__ = ['smiles', 'smarts', 'mdl_mol', 'mdl_rxn', 'xyz', 'xyz_file', 'inchi'] +__all__ = ['smiles', 'smarts', 'mdl_mol', 'mdl_rxn', 'xyz', 'xyz_file', 'inchi', 'opsin'] __all__.extend(x for x in locals() if x.endswith(('Read', 'Write'))) diff --git a/chython/files/opsin.py b/chython/files/opsin.py new file mode 100644 index 00000000..2704270c --- /dev/null +++ b/chython/files/opsin.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2025 Ramil Nugmanov +# This file is part of chython. +# +# chython is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, see . +# +from jpype import JPackage, isJVMStarted, startJVM +from .daylight import smiles + + +_nametostruct = _opsin = None + + +def opsin(string): + """Parse IUPAC name into MoleculeContainer. + """ + global _opsin, _nametostruct + + if _opsin is None: + if not isJVMStarted(): + from chython import class_paths + + startJVM('--enable-native-access=ALL-UNNAMED', classpath=class_paths) + + _opsin = JPackage('uk').ac.cam.ch.wwmm.opsin + _nametostruct = _opsin.NameToStructure.getInstance() + + result = _nametostruct.parseChemicalName(string) + if str(result.getStatus()) == 'FAILURE': + raise ValueError(f'Failed to convert `{string}`: {result.getMessage()}') + return smiles(str(result.getSmiles())) + + +__all__ = ['opsin'] diff --git a/pyproject.toml b/pyproject.toml index dcf0d6d5..fc6ebe76 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = 'chython' -version = '2.14' +version = '2.15' description = 'Library for processing molecules and reactions in python way' authors = ['Ramil Nugmanov '] license = 'LGPLv3' @@ -39,6 +39,9 @@ py-mini-racer = {version = '>=0.6.0', optional = true} chytorch-rxnmap = {version = '>=1.4', optional = true} rdkit = {version = '>=2023.9', optional = true} pyppeteer = {version = '>=2.0.0', optional = true} +jpype1 = {version = '>=1.6.0', optional = true} +openbabel-wheel = {version = '>=3.1.1.22', optional = true} +cdpkit = {version = '>=1.2.3', optional = true} numpy = ">=1.21.0" [tool.poetry.extras] @@ -47,6 +50,8 @@ rdkit = ['rdkit'] png = ['pyppeteer'] racer-default = ['mini-racer'] racer-deprecated = ['py-mini-racer'] +extra-clean2d = ['jpype1', 'openbabel-wheel'] +extra-clean3d = ['cdpkit'] [tool.poetry.group.dev] optional = true From 5c10b13de726d6ecd196de0e6a6afc096322eef0 Mon Sep 17 00:00:00 2001 From: Ramil Nugmanov Date: Tue, 30 Dec 2025 17:10:12 +0100 Subject: [PATCH 6/6] added iupac alias to opsin --- chython/files/__init__.py | 2 +- chython/files/opsin.py | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/chython/files/__init__.py b/chython/files/__init__.py index 9b8d9823..1265b583 100644 --- a/chython/files/__init__.py +++ b/chython/files/__init__.py @@ -26,5 +26,5 @@ from .xyz import * -__all__ = ['smiles', 'smarts', 'mdl_mol', 'mdl_rxn', 'xyz', 'xyz_file', 'inchi', 'opsin'] +__all__ = ['smiles', 'smarts', 'mdl_mol', 'mdl_rxn', 'xyz', 'xyz_file', 'inchi', 'opsin', 'iupac'] __all__.extend(x for x in locals() if x.endswith(('Read', 'Write'))) diff --git a/chython/files/opsin.py b/chython/files/opsin.py index 2704270c..78e12e05 100644 --- a/chython/files/opsin.py +++ b/chython/files/opsin.py @@ -43,4 +43,7 @@ def opsin(string): return smiles(str(result.getSmiles())) -__all__ = ['opsin'] +iupac = opsin + + +__all__ = ['opsin', 'iupac']