# # Copyright (C) 2003-2025 Greg Landrum and other RDKit contributors # All Rights Reserved # """ This is a rough coverage test of the python wrapper it's intended to be shallow, but broad """ import doctest import gc import gzip import importlib.util import logging import os import pickle import sys import tempfile import unittest from contextlib import contextmanager from datetime import datetime, timedelta from io import BytesIO, StringIO import numpy as np import rdkit.Chem.rdDepictor from rdkit import Chem, DataStructs, RDConfig, __version__, rdBase from rdkit.Chem import AllChem, rdqueries from rdkit.Chem.Scaffolds import MurckoScaffold # Boost functions are NOT found by doctest, this "fixes" them # by adding the doctests to a fake module spec = importlib.util.spec_from_loader("TestReplaceCore", loader=None) TestReplaceCore = importlib.util.module_from_spec(spec) code = """ from rdkit.Chem import ReplaceCore def ReplaceCore(*a, **kw): '''%s ''' return Chem.ReplaceCore(*a, **kw) """ % "\n".join([x.lstrip() for x in Chem.ReplaceCore.__doc__.split("\n")]) exec(code, TestReplaceCore.__dict__) @contextmanager def log_to_python(level=None): """ Temporarily redirect logging to Python streams, optionally setting a specific log level. """ rdBase.LogToPythonLogger() pylog = logging.getLogger("rdkit") if level is not None: original_level = pylog.level pylog.setLevel(level) yield pylog if level is not None: pylog.setLevel(original_level) rdBase.LogToCppStreams() @contextmanager def capture_logging(level=None): """ Temporarily redirect logging to a Python StringIO, optionally setting a specific log level. """ log_stream = StringIO() stream_handler = logging.StreamHandler(stream=log_stream) with log_to_python(level) as pylog: pylog.addHandler(stream_handler) yield log_stream pylog.removeHandler(stream_handler) def load_tests(loader, tests, ignore): tests.addTests(doctest.DocTestSuite(TestReplaceCore)) return tests def feq(v1, v2, tol2=1e-4): return abs(v1 - v2) <= tol2 def getTotalFormalCharge(mol): totalFormalCharge = 0 for atom in mol.GetAtoms(): totalFormalCharge += atom.GetFormalCharge() return totalFormalCharge def cmpFormalChargeBondOrder(self, mol1, mol2): self.assertEqual(mol1.GetNumAtoms(), mol2.GetNumAtoms()) self.assertEqual(mol1.GetNumBonds(), mol2.GetNumBonds()) for i in range(mol1.GetNumAtoms()): self.assertEqual( mol1.GetAtomWithIdx(i).GetFormalCharge(), mol2.GetAtomWithIdx(i).GetFormalCharge()) for i in range(mol1.GetNumBonds()): self.assertEqual(mol1.GetBondWithIdx(i).GetBondType(), mol2.GetBondWithIdx(i).GetBondType()) def setResidueFormalCharge(mol, res, fc): for query in res: matches = mol.GetSubstructMatches(query) for match in matches: mol.GetAtomWithIdx(match[-1]).SetFormalCharge(fc) def getBtList2(resMolSuppl): btList2 = [] while (not resMolSuppl.atEnd()): resMol = next(resMolSuppl) bt = [] for bond in resMol.GetBonds(): bt.append(int(bond.GetBondTypeAsDouble())) btList2.append(bt) for i in range(len(btList2)): same = True for j in range(len(btList2[i])): if (not i): continue if (same): same = (btList2[i][j] == btList2[i - 1][j]) if (i and same): return None return btList2 class TestCase(unittest.TestCase): def test0Except(self): with self.assertRaises(IndexError): Chem.tossit() def test1Table(self): tbl = Chem.GetPeriodicTable() self.assertTrue(tbl) self.assertTrue(feq(tbl.GetAtomicWeight(6), 12.011)) self.assertTrue(feq(tbl.GetAtomicWeight("C"), 12.011)) self.assertTrue(tbl.GetAtomicNumber('C') == 6) self.assertTrue(feq(tbl.GetRvdw(6), 1.7)) self.assertTrue(feq(tbl.GetRvdw("C"), 1.7)) self.assertTrue(feq(tbl.GetRcovalent(6), 0.76)) self.assertTrue(feq(tbl.GetRcovalent("C"), 0.76)) self.assertTrue(tbl.GetDefaultValence(6) == 4) self.assertTrue(tbl.GetDefaultValence("C") == 4) self.assertTrue(tuple(tbl.GetValenceList(6)) == (4, )) self.assertTrue(tuple(tbl.GetValenceList("C")) == (4, )) self.assertTrue(tuple(tbl.GetValenceList(16)) == (2, 4, 6)) self.assertTrue(tuple(tbl.GetValenceList("S")) == (2, 4, 6)) self.assertTrue(tbl.GetNOuterElecs(6) == 4) self.assertTrue(tbl.GetNOuterElecs("C") == 4) self.assertTrue(tbl.GetMostCommonIsotope(6) == 12) self.assertTrue(tbl.GetMostCommonIsotope('C') == 12) self.assertTrue(tbl.GetMostCommonIsotopeMass(6) == 12.0) self.assertTrue(tbl.GetMostCommonIsotopeMass('C') == 12.0) self.assertTrue(tbl.GetAbundanceForIsotope(6, 12) == 98.93) self.assertTrue(tbl.GetAbundanceForIsotope('C', 12) == 98.93) self.assertTrue(feq(tbl.GetRb0(6), 0.77)) self.assertTrue(feq(tbl.GetRb0("C"), 0.77)) self.assertTrue(tbl.GetElementSymbol(6) == 'C') self.assertTrue(tbl.GetElementName(6) == 'Carbon') self.assertTrue(tbl.GetRow(6) == 2) self.assertTrue(tbl.GetRow("C") == 2) def test2Atom(self): atom = Chem.Atom(6) self.assertTrue(atom) self.assertTrue(atom.GetAtomicNum() == 6) atom.SetAtomicNum(8) self.assertTrue(atom.GetAtomicNum() == 8) atom = Chem.Atom("C") self.assertTrue(atom) self.assertTrue(atom.GetAtomicNum() == 6) def test3Bond(self): # No longer relevant, bonds are not constructible from Python pass def test4Mol(self): mol = Chem.Mol() self.assertTrue(mol) def test5Smiles(self): mol = Chem.MolFromSmiles('n1ccccc1') self.assertTrue(mol) self.assertTrue(mol.GetNumAtoms() == 6) self.assertTrue(mol.GetNumAtoms(1) == 6) self.assertTrue(mol.GetNumAtoms(0) == 11) at = mol.GetAtomWithIdx(2) self.assertTrue(at.GetAtomicNum() == 6) at = mol.GetAtomWithIdx(0) self.assertTrue(at.GetAtomicNum() == 7) def _test6Bookmarks(self): mol = Chem.MolFromSmiles('n1ccccc1') self.assertTrue(mol) self.assertTrue(not mol.HasAtomBookmark(0)) mol.SetAtomBookmark(mol.GetAtomWithIdx(0), 0) mol.SetAtomBookmark(mol.GetAtomWithIdx(1), 1) self.assertTrue(mol.HasAtomBookmark(0)) self.assertTrue(mol.HasAtomBookmark(1)) if 1: self.assertTrue(not mol.HasBondBookmark(0)) self.assertTrue(not mol.HasBondBookmark(1)) mol.SetBondBookmark(mol.GetBondWithIdx(0), 0) mol.SetBondBookmark(mol.GetBondWithIdx(1), 1) self.assertTrue(mol.HasBondBookmark(0)) self.assertTrue(mol.HasBondBookmark(1)) at = mol.GetAtomWithBookmark(0) self.assertTrue(at) self.assertTrue(at.GetAtomicNum() == 7) mol.ClearAtomBookmark(0) self.assertTrue(not mol.HasAtomBookmark(0)) self.assertTrue(mol.HasAtomBookmark(1)) mol.ClearAllAtomBookmarks() self.assertTrue(not mol.HasAtomBookmark(0)) self.assertTrue(not mol.HasAtomBookmark(1)) mol.SetAtomBookmark(mol.GetAtomWithIdx(1), 1) if 1: self.assertTrue(mol.HasBondBookmark(0)) self.assertTrue(mol.HasBondBookmark(1)) bond = mol.GetBondWithBookmark(0) self.assertTrue(bond) mol.ClearBondBookmark(0) self.assertTrue(not mol.HasBondBookmark(0)) self.assertTrue(mol.HasBondBookmark(1)) mol.ClearAllBondBookmarks() self.assertTrue(not mol.HasBondBookmark(0)) self.assertTrue(not mol.HasBondBookmark(1)) self.assertTrue(mol.HasAtomBookmark(1)) def test7Atom(self): mol = Chem.MolFromSmiles('n1ccccc1C[CH2-]') self.assertTrue(mol) Chem.SanitizeMol(mol) a0 = mol.GetAtomWithIdx(0) a1 = mol.GetAtomWithIdx(1) a6 = mol.GetAtomWithIdx(6) a7 = mol.GetAtomWithIdx(7) self.assertTrue(a0.GetAtomicNum() == 7) self.assertTrue(a0.GetSymbol() == 'N') self.assertTrue(a0.GetIdx() == 0) aList = [a0, a1, a6, a7] self.assertTrue(a0.GetDegree() == 2) self.assertTrue(a1.GetDegree() == 2) self.assertTrue(a6.GetDegree() == 2) self.assertTrue(a7.GetDegree() == 1) self.assertTrue([x.GetDegree() for x in aList] == [2, 2, 2, 1]) self.assertTrue([x.GetTotalNumHs() for x in aList] == [0, 1, 2, 2]) self.assertTrue([x.GetNumImplicitHs() for x in aList] == [0, 1, 2, 0]) self.assertTrue([x.GetValence(which=Chem.ValenceType.EXPLICIT) for x in aList] == [3, 3, 2, 3]) self.assertTrue([x.GetValence(which=Chem.ValenceType.IMPLICIT) for x in aList] == [0, 1, 2, 0]) self.assertTrue([x.GetValence(which=Chem.ValenceType.EXPLICIT) for x in aList] == [3, 3, 2, 3]) self.assertTrue([x.GetValence(which=Chem.ValenceType.IMPLICIT) for x in aList] == [0, 1, 2, 0]) self.assertTrue([x.GetFormalCharge() for x in aList] == [0, 0, 0, -1]) self.assertTrue([x.GetNoImplicit() for x in aList] == [0, 0, 0, 1]) self.assertTrue([x.GetNumExplicitHs() for x in aList] == [0, 0, 0, 2]) self.assertTrue([x.GetIsAromatic() for x in aList] == [1, 1, 0, 0]) self.assertTrue([x.GetHybridization() for x in aList] == [ Chem.HybridizationType.SP2, Chem.HybridizationType.SP2, Chem.HybridizationType.SP3, Chem.HybridizationType.SP3 ], [x.GetHybridization() for x in aList]) def test8Bond(self): mol = Chem.MolFromSmiles('n1ccccc1CC(=O)O') self.assertTrue(mol) Chem.SanitizeMol(mol) # note bond numbering is funny because of ring closure b0 = mol.GetBondWithIdx(0) b6 = mol.GetBondWithIdx(6) b7 = mol.GetBondWithIdx(7) b8 = mol.GetBondWithIdx(8) bList = [b0, b6, b7, b8] self.assertTrue( [x.GetBondType() for x in bList] == [Chem.BondType.AROMATIC, Chem.BondType.SINGLE, Chem.BondType.DOUBLE, Chem.BondType.SINGLE]) self.assertTrue([x.GetIsAromatic() for x in bList] == [1, 0, 0, 0]) self.assertEqual(bList[0].GetBondTypeAsDouble(), 1.5) self.assertEqual(bList[1].GetBondTypeAsDouble(), 1.0) self.assertEqual(bList[2].GetBondTypeAsDouble(), 2.0) self.assertTrue([x.GetIsConjugated() != 0 for x in bList] == [1, 0, 1, 1], [x.GetIsConjugated() != 0 for x in bList]) self.assertTrue([x.GetBeginAtomIdx() for x in bList] == [0, 6, 7, 7], [x.GetBeginAtomIdx() for x in bList]) self.assertTrue([x.GetBeginAtom().GetIdx() for x in bList] == [0, 6, 7, 7]) self.assertTrue([x.GetEndAtomIdx() for x in bList] == [1, 7, 8, 9]) self.assertTrue([x.GetEndAtom().GetIdx() for x in bList] == [1, 7, 8, 9]) def test9Smarts(self): query1 = Chem.MolFromSmarts('C(=O)O') self.assertTrue(query1) query2 = Chem.MolFromSmarts('C(=O)[O,N]') self.assertTrue(query2) query3 = Chem.MolFromSmarts('[$(C(=O)O)]') self.assertTrue(query3) mol = Chem.MolFromSmiles('CCC(=O)O') self.assertTrue(mol) self.assertTrue(mol.HasSubstructMatch(query1)) self.assertTrue(mol.HasSubstructMatch(query2)) self.assertTrue(mol.HasSubstructMatch(query3)) mol = Chem.MolFromSmiles('CCC(=O)N') self.assertTrue(mol) self.assertTrue(not mol.HasSubstructMatch(query1)) self.assertTrue(mol.HasSubstructMatch(query2)) self.assertTrue(not mol.HasSubstructMatch(query3)) def test10Iterators(self): mol = Chem.MolFromSmiles('CCOC') self.assertTrue(mol) for atom in mol.GetAtoms(): self.assertTrue(atom) ats = mol.GetAtoms() ats[1] with self.assertRaisesRegex(IndexError, ""): ats[12] for bond in mol.GetBonds(): self.assertTrue(bond) bonds = mol.GetBonds() bonds[1] with self.assertRaisesRegex(IndexError, ""): bonds[12] def test11MolOps(self): mol = Chem.MolFromSmiles('C1=CC=C(C=C1)P(C2=CC=CC=C2)C3=CC=CC=C3') self.assertTrue(mol) smi = Chem.MolToSmiles(mol) Chem.SanitizeMol(mol) nr = Chem.GetSymmSSSR(mol) self.assertTrue((len(nr) == 3)) nr = Chem.GetSSSR(mol) self.assertTrue((len(nr) == 3)) def test12Smarts(self): query1 = Chem.MolFromSmarts('C(=O)O') self.assertTrue(query1) query2 = Chem.MolFromSmarts('C(=O)[O,N]') self.assertTrue(query2) query3 = Chem.MolFromSmarts('[$(C(=O)O)]') self.assertTrue(query3) mol = Chem.MolFromSmiles('CCC(=O)O') self.assertTrue(mol) self.assertTrue(mol.HasSubstructMatch(query1)) self.assertTrue(mol.GetSubstructMatch(query1) == (2, 3, 4)) self.assertTrue(mol.HasSubstructMatch(query2)) self.assertTrue(mol.GetSubstructMatch(query2) == (2, 3, 4)) self.assertTrue(mol.HasSubstructMatch(query3)) self.assertTrue(mol.GetSubstructMatch(query3) == (2, )) mol = Chem.MolFromSmiles('CCC(=O)N') self.assertTrue(mol) self.assertTrue(not mol.HasSubstructMatch(query1)) self.assertTrue(not mol.GetSubstructMatch(query1)) self.assertTrue(mol.HasSubstructMatch(query2)) self.assertTrue(mol.GetSubstructMatch(query2) == (2, 3, 4)) self.assertTrue(not mol.HasSubstructMatch(query3)) mol = Chem.MolFromSmiles('OC(=O)CC(=O)O') self.assertTrue(mol) self.assertTrue(mol.HasSubstructMatch(query1)) self.assertTrue(mol.GetSubstructMatch(query1) == (1, 2, 0)) self.assertTrue(mol.GetSubstructMatches(query1) == ((1, 2, 0), (4, 5, 6))) self.assertTrue(mol.HasSubstructMatch(query2)) self.assertTrue(mol.GetSubstructMatch(query2) == (1, 2, 0)) self.assertTrue(mol.GetSubstructMatches(query2) == ((1, 2, 0), (4, 5, 6))) self.assertTrue(mol.HasSubstructMatch(query3)) self.assertTrue(mol.GetSubstructMatches(query3) == ((1, ), (4, ))) def test13Smarts(self): # previous smarts problems: query = Chem.MolFromSmarts('N(=,-C)') self.assertTrue(query) mol = Chem.MolFromSmiles('N#C') self.assertTrue(not mol.HasSubstructMatch(query)) mol = Chem.MolFromSmiles('N=C') self.assertTrue(mol.HasSubstructMatch(query)) mol = Chem.MolFromSmiles('NC') self.assertTrue(mol.HasSubstructMatch(query)) query = Chem.MolFromSmarts('[Cl,$(O)]') mol = Chem.MolFromSmiles('C(=O)O') self.assertTrue(len(mol.GetSubstructMatches(query)) == 2) mol = Chem.MolFromSmiles('C(=N)N') self.assertTrue(len(mol.GetSubstructMatches(query)) == 0) query = Chem.MolFromSmarts('[$([O,S]-[!$(*=O)])]') mol = Chem.MolFromSmiles('CC(S)C(=O)O') self.assertTrue(len(mol.GetSubstructMatches(query)) == 1) mol = Chem.MolFromSmiles('C(=O)O') self.assertTrue(len(mol.GetSubstructMatches(query)) == 0) def test14Hs(self): m = Chem.MolFromSmiles('CC(=O)[OH]') self.assertEqual(m.GetNumAtoms(), 4) m2 = Chem.AddHs(m) self.assertEqual(m2.GetNumAtoms(), 8) m2 = Chem.RemoveHs(m2) self.assertEqual(m2.GetNumAtoms(), 4) m = Chem.MolFromSmiles('CC[H]', False) self.assertEqual(m.GetNumAtoms(), 3) m2 = Chem.MergeQueryHs(m) self.assertEqual(m2.GetNumAtoms(), 2) self.assertTrue(m2.GetAtomWithIdx(1).HasQuery()) m = Chem.MolFromSmiles('CC[H]', False) self.assertEqual(m.GetNumAtoms(), 3) m1 = Chem.RemoveHs(m) self.assertEqual(m1.GetNumAtoms(), 2) self.assertEqual(m1.GetAtomWithIdx(1).GetNumExplicitHs(), 0) m1 = Chem.RemoveHs(m, updateExplicitCount=True) self.assertEqual(m1.GetNumAtoms(), 2) self.assertEqual(m1.GetAtomWithIdx(1).GetNumExplicitHs(), 1) # test merging of mapped hydrogens m = Chem.MolFromSmiles('CC[H]', False) m.GetAtomWithIdx(2).SetProp("molAtomMapNumber", "1") self.assertEqual(m.GetNumAtoms(), 3) m2 = Chem.MergeQueryHs(m, mergeUnmappedOnly=True) self.assertTrue(m2 is not None) self.assertEqual(m2.GetNumAtoms(), 3) self.assertFalse(m2.GetAtomWithIdx(1).HasQuery()) # here the hydrogen is unmapped # should be the same as merging all hydrogens m = Chem.MolFromSmiles('CC[H]', False) m.GetAtomWithIdx(1).SetProp("molAtomMapNumber", "1") self.assertEqual(m.GetNumAtoms(), 3) m2 = Chem.MergeQueryHs(m, mergeUnmappedOnly=True) self.assertTrue(m2 is not None) self.assertEqual(m2.GetNumAtoms(), 2) self.assertTrue(m2.GetAtomWithIdx(1).HasQuery()) # test merging of isotopes, by default deuterium will not be merged m = Chem.MolFromSmiles('CC[2H]', False) self.assertEqual(m.GetNumAtoms(), 3) m2 = Chem.MergeQueryHs(m) self.assertTrue(m2 is not None) self.assertEqual(m2.GetNumAtoms(), 3) self.assertFalse(m2.GetAtomWithIdx(1).HasQuery()) # here deuterium is merged # should be the same as merging all hydrogens m = Chem.MolFromSmiles('CC[2H]', False) self.assertEqual(m.GetNumAtoms(), 3) m2 = Chem.MergeQueryHs(m, mergeIsotopes=True) self.assertTrue(m2 is not None) self.assertEqual(m2.GetNumAtoms(), 2) self.assertTrue(m2.GetAtomWithIdx(1).HasQuery()) # test github758 m = Chem.MolFromSmiles('CCC') self.assertEqual(m.GetNumAtoms(), 3) m = Chem.AddHs(m, onlyOnAtoms=(0, 2)) self.assertEqual(m.GetNumAtoms(), 9) self.assertEqual(m.GetAtomWithIdx(0).GetDegree(), 4) self.assertEqual(m.GetAtomWithIdx(2).GetDegree(), 4) self.assertEqual(m.GetAtomWithIdx(1).GetDegree(), 2) # AddHsParameters params = Chem.AddHsParameters() params.skipQueries = True m = Chem.MolFromSmiles('CC') self.assertIsNotNone(m) nm = Chem.AddHs(m) self.assertEqual(nm.GetNumAtoms(), 8) nm = Chem.AddHs(m, params) self.assertEqual(nm.GetNumAtoms(), 8) m = Chem.MolFromSmarts('[CH3][CH3]') self.assertIsNotNone(m) nm = Chem.AddHs(m) self.assertEqual(nm.GetNumAtoms(), 8) nm = Chem.AddHs(m, params) self.assertEqual(nm.GetNumAtoms(), 2) def test15Neighbors(self): m = Chem.MolFromSmiles('CC(=O)[OH]') self.assertTrue(m.GetNumAtoms() == 4) a = m.GetAtomWithIdx(1) ns = a.GetNeighbors() self.assertTrue(len(ns) == 3) bs = a.GetBonds() self.assertTrue(len(bs) == 3) for b in bs: try: a2 = b.GetOtherAtom(a) except Exception: a2 = None self.assertTrue(a2) self.assertTrue(len(bs) == 3) def test16Pickle(self): import pickle m = Chem.MolFromSmiles('C1=CN=CC=C1') pkl = pickle.dumps(m) m2 = pickle.loads(pkl) self.assertTrue(type(m2) == Chem.Mol) smi1 = Chem.MolToSmiles(m) smi2 = Chem.MolToSmiles(m2) self.assertTrue(smi1 == smi2) pkl = pickle.dumps(Chem.RWMol(m)) m2 = pickle.loads(pkl) self.assertTrue(type(m2) == Chem.RWMol) smi1 = Chem.MolToSmiles(m) smi2 = Chem.MolToSmiles(m2) self.assertTrue(smi1 == smi2) def test16Props(self): m = Chem.MolFromSmiles('C1=CN=CC=C1') self.assertTrue(not m.HasProp('prop1')) self.assertTrue(not m.HasProp('prop2')) self.assertTrue(not m.HasProp('prop2')) m.SetProp('prop1', 'foob') self.assertTrue(not m.HasProp('prop2')) self.assertTrue(m.HasProp('prop1')) self.assertTrue(m.GetProp('prop1') == 'foob') self.assertTrue(not m.HasProp('propo')) try: m.GetProp('prop2') except KeyError: ok = 1 else: ok = 0 self.assertTrue(ok) # test computed properties m.SetProp('cprop1', 'foo', 1) m.SetProp('cprop2', 'foo2', 1) m.ClearComputedProps() self.assertTrue(not m.HasProp('cprop1')) self.assertTrue(not m.HasProp('cprop2')) m.SetDoubleProp("a", 2.0) self.assertTrue(m.GetDoubleProp("a") == 2.0) try: self.assertTrue(m.GetIntProp("a") == 2.0) raise Exception("Expected runtime exception") except ValueError: pass try: self.assertTrue(m.GetUnsignedProp("a") == 2.0) raise Exception("Expected runtime exception") except ValueError: pass m.SetDoubleProp("a", -2) self.assertTrue(m.GetDoubleProp("a") == -2.0) m.SetIntProp("a", -2) self.assertTrue(m.GetIntProp("a") == -2) try: m.SetUnsignedProp("a", -2) raise Exception("Expected failure with negative unsigned number") except OverflowError: pass m.SetBoolProp("a", False) self.assertFalse(m.GetBoolProp("a")) self.assertEqual(m.GetPropsAsDict(), {'a': False, 'prop1': 'foob'}) m.SetDoubleProp("b", 1000.0) m.SetUnsignedProp("c", 2000) m.SetIntProp("d", -2) m.SetUnsignedProp("e", 2, True) self.assertEqual(m.GetPropsAsDict(False, True), { 'a': False, 'c': 2000, 'b': 1000.0, 'e': 2, 'd': -2, 'prop1': 'foob' }) m = Chem.MolFromSmiles('C1=CN=CC=C1') m.SetProp("int", "1000") m.SetProp("double", "10000.123") m.SetProp("double spaces", " 10000.123 ") # Github #8890: test that string properties preserve spaces m.SetProp("string spaces", " foo ") m.SetProp("string whitespace", " \t") self.assertEqual( m.GetPropsAsDict(), { "int": 1000, "double": 10000.123, "double spaces": 10000.123, "string spaces": " foo ", "string whitespace": " \t" }) self.assertEqual( m.GetPropsAsDict(autoConvertStrings=False), { "int": "1000", "double": "10000.123", "double spaces": " 10000.123 ", "string spaces": " foo ", "string whitespace": " \t" }) self.assertEqual(type(m.GetPropsAsDict()['int']), int) self.assertEqual(type(m.GetPropsAsDict()['double']), float) def test17Kekulize(self): m = Chem.MolFromSmiles('c1ccccc1') smi = Chem.MolToSmiles(m) self.assertTrue(smi == 'c1ccccc1') Chem.Kekulize(m) smi = Chem.MolToSmiles(m) self.assertTrue(smi == 'c1ccccc1') m = Chem.MolFromSmiles('c1ccccc1') smi = Chem.MolToSmiles(m) self.assertTrue(smi == 'c1ccccc1') Chem.Kekulize(m, 1) smi = Chem.MolToSmiles(m) self.assertTrue(smi == 'C1=CC=CC=C1', smi) def test18Paths(self): m = Chem.MolFromSmiles("C1CC2C1CC2") #self.assertTrue(len(Chem.FindAllPathsOfLengthN(m,1,useBonds=1))==7) #print(Chem.FindAllPathsOfLengthN(m,3,useBonds=0)) self.assertTrue( len(Chem.FindAllPathsOfLengthN(m, 2, useBonds=1)) == 10, Chem.FindAllPathsOfLengthN(m, 2, useBonds=1)) self.assertTrue(len(Chem.FindAllPathsOfLengthN(m, 3, useBonds=1)) == 14) m = Chem.MolFromSmiles('C1CC1C') self.assertTrue(m) self.assertTrue(len(Chem.FindAllPathsOfLengthN(m, 1, useBonds=1)) == 4) self.assertTrue(len(Chem.FindAllPathsOfLengthN(m, 2, useBonds=1)) == 5) self.assertTrue( len(Chem.FindAllPathsOfLengthN(m, 3, useBonds=1)) == 3, Chem.FindAllPathsOfLengthN(m, 3, useBonds=1)) self.assertTrue( len(Chem.FindAllPathsOfLengthN(m, 4, useBonds=1)) == 1, Chem.FindAllPathsOfLengthN(m, 4, useBonds=1)) self.assertTrue( len(Chem.FindAllPathsOfLengthN(m, 5, useBonds=1)) == 0, Chem.FindAllPathsOfLengthN(m, 5, useBonds=1)) # # Hexane example from Hall-Kier Rev.Comp.Chem. paper # Rev. Comp. Chem. vol 2, 367-422, (1991) # m = Chem.MolFromSmiles("CCCCCC") self.assertTrue(len(Chem.FindAllPathsOfLengthN(m, 1, useBonds=1)) == 5) self.assertTrue(len(Chem.FindAllPathsOfLengthN(m, 2, useBonds=1)) == 4) self.assertTrue(len(Chem.FindAllPathsOfLengthN(m, 3, useBonds=1)) == 3) m = Chem.MolFromSmiles("CCC(C)CC") self.assertTrue(len(Chem.FindAllPathsOfLengthN(m, 1, useBonds=1)) == 5) self.assertTrue(len(Chem.FindAllPathsOfLengthN(m, 2, useBonds=1)) == 5) self.assertTrue( len(Chem.FindAllPathsOfLengthN(m, 3, useBonds=1)) == 4, Chem.FindAllPathsOfLengthN(m, 3, useBonds=1)) m = Chem.MolFromSmiles("CCCC(C)C") self.assertTrue(len(Chem.FindAllPathsOfLengthN(m, 1, useBonds=1)) == 5) self.assertTrue(len(Chem.FindAllPathsOfLengthN(m, 2, useBonds=1)) == 5) self.assertTrue(len(Chem.FindAllPathsOfLengthN(m, 3, useBonds=1)) == 3) m = Chem.MolFromSmiles("CC(C)C(C)C") self.assertTrue(len(Chem.FindAllPathsOfLengthN(m, 1, useBonds=1)) == 5) self.assertTrue(len(Chem.FindAllPathsOfLengthN(m, 2, useBonds=1)) == 6) self.assertTrue(len(Chem.FindAllPathsOfLengthN(m, 3, useBonds=1)) == 4) m = Chem.MolFromSmiles("CC(C)(C)CC") self.assertTrue(len(Chem.FindAllPathsOfLengthN(m, 1, useBonds=1)) == 5) self.assertTrue(len(Chem.FindAllPathsOfLengthN(m, 2, useBonds=1)) == 7) self.assertTrue( len(Chem.FindAllPathsOfLengthN(m, 3, useBonds=1)) == 3, Chem.FindAllPathsOfLengthN(m, 3, useBonds=1)) m = Chem.MolFromSmiles("C1CCCCC1") self.assertTrue(len(Chem.FindAllPathsOfLengthN(m, 1, useBonds=1)) == 6) self.assertTrue(len(Chem.FindAllPathsOfLengthN(m, 2, useBonds=1)) == 6) self.assertTrue(len(Chem.FindAllPathsOfLengthN(m, 3, useBonds=1)) == 6) m = Chem.MolFromSmiles("C1CC2C1CC2") self.assertTrue(len(Chem.FindAllPathsOfLengthN(m, 1, useBonds=1)) == 7) self.assertTrue( len(Chem.FindAllPathsOfLengthN(m, 2, useBonds=1)) == 10, Chem.FindAllPathsOfLengthN(m, 2, useBonds=1)) self.assertTrue(len(Chem.FindAllPathsOfLengthN(m, 3, useBonds=1)) == 14) m = Chem.MolFromSmiles("CC2C1CCC12") self.assertTrue(len(Chem.FindAllPathsOfLengthN(m, 1, useBonds=1)) == 7) self.assertTrue(len(Chem.FindAllPathsOfLengthN(m, 2, useBonds=1)) == 11) # FIX: this result disagrees with the paper (which says 13), # but it seems right self.assertTrue( len(Chem.FindAllPathsOfLengthN(m, 3, useBonds=1)) == 15, Chem.FindAllPathsOfLengthN(m, 3, useBonds=1)) def test19Subgraphs(self): m = Chem.MolFromSmiles('C1CC1C') self.assertTrue(m) self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 1, 0)) == 4) self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 2)) == 5) self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 3)) == 4) self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 4)) == 1) self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 5)) == 0) # # Hexane example from Hall-Kier Rev.Comp.Chem. paper # Rev. Comp. Chem. vol 2, 367-422, (1991) # m = Chem.MolFromSmiles("CCCCCC") self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 1)) == 5) self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 2)) == 4) self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 3)) == 3) l = Chem.FindAllSubgraphsOfLengthMToN(m, 1, 3) self.assertEqual(len(l), 3) self.assertEqual(len(l[0]), 5) self.assertEqual(len(l[1]), 4) self.assertEqual(len(l[2]), 3) self.assertRaises(ValueError, lambda: Chem.FindAllSubgraphsOfLengthMToN(m, 4, 3)) m = Chem.MolFromSmiles("CCC(C)CC") self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 1)) == 5) self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 2)) == 5) self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 3)) == 5) m = Chem.MolFromSmiles("CCCC(C)C") self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 1)) == 5) self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 2)) == 5) self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 3)) == 4) m = Chem.MolFromSmiles("CC(C)C(C)C") self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 1)) == 5) self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 2)) == 6) self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 3)) == 6) m = Chem.MolFromSmiles("CC(C)(C)CC") self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 1)) == 5) self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 2)) == 7) self.assertTrue( len(Chem.FindAllSubgraphsOfLengthN(m, 3)) == 7, Chem.FindAllSubgraphsOfLengthN(m, 3)) m = Chem.MolFromSmiles("C1CCCCC1") self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 1)) == 6) self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 2)) == 6) self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 3)) == 6) #self.assertTrue(len(Chem.FindUniqueSubgraphsOfLengthN(m,1))==1) self.assertTrue(len(Chem.FindUniqueSubgraphsOfLengthN(m, 2)) == 1) self.assertTrue(len(Chem.FindUniqueSubgraphsOfLengthN(m, 3)) == 1) m = Chem.MolFromSmiles("C1CC2C1CC2") self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 1)) == 7) self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 2)) == 10) self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 3)) == 16) m = Chem.MolFromSmiles("CC2C1CCC12") self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 1)) == 7) self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 2)) == 11) self.assertTrue( len(Chem.FindAllSubgraphsOfLengthN(m, 3)) == 18, len(Chem.FindAllSubgraphsOfLengthN(m, 3))) def test20IsInRing(self): m = Chem.MolFromSmiles('C1CCC1C') self.assertTrue(m) self.assertTrue(m.GetAtomWithIdx(0).IsInRingSize(4)) self.assertTrue(m.GetAtomWithIdx(1).IsInRingSize(4)) self.assertTrue(m.GetAtomWithIdx(2).IsInRingSize(4)) self.assertTrue(m.GetAtomWithIdx(3).IsInRingSize(4)) self.assertTrue(not m.GetAtomWithIdx(4).IsInRingSize(4)) self.assertTrue(not m.GetAtomWithIdx(0).IsInRingSize(3)) self.assertTrue(not m.GetAtomWithIdx(1).IsInRingSize(3)) self.assertTrue(not m.GetAtomWithIdx(2).IsInRingSize(3)) self.assertTrue(not m.GetAtomWithIdx(3).IsInRingSize(3)) self.assertTrue(not m.GetAtomWithIdx(4).IsInRingSize(3)) self.assertTrue(m.GetBondWithIdx(0).IsInRingSize(4)) self.assertTrue(not m.GetBondWithIdx(3).IsInRingSize(4)) self.assertTrue(not m.GetBondWithIdx(0).IsInRingSize(3)) self.assertTrue(not m.GetBondWithIdx(3).IsInRingSize(3)) def test21Robustification(self): ok = False # FIX: at the moment I can't figure out how to catch the # actual exception that BPL is throwing when it gets # invalid arguments (Boost.Python.ArgumentError) try: Chem.MolFromSmiles('C=O').HasSubstructMatch(Chem.MolFromSmarts('fiib')) #except ValueError: # ok=True except Exception: ok = True self.assertTrue(ok) def test22DeleteSubstruct(self): query = Chem.MolFromSmarts('C(=O)O') mol = Chem.MolFromSmiles('CCC(=O)O') nmol = Chem.DeleteSubstructs(mol, query) self.assertTrue(Chem.MolToSmiles(nmol) == 'CC') mol = Chem.MolFromSmiles('CCC(=O)O.O=CO') # now delete only fragments nmol = Chem.DeleteSubstructs(mol, query, 1) self.assertTrue(Chem.MolToSmiles(nmol) == 'CCC(=O)O', Chem.MolToSmiles(nmol)) mol = Chem.MolFromSmiles('CCC(=O)O.O=CO') nmol = Chem.DeleteSubstructs(mol, query, 0) self.assertTrue(Chem.MolToSmiles(nmol) == 'CC') mol = Chem.MolFromSmiles('CCCO') nmol = Chem.DeleteSubstructs(mol, query, 0) self.assertTrue(Chem.MolToSmiles(nmol) == 'CCCO') # Issue 96 prevented this from working: mol = Chem.MolFromSmiles('CCC(=O)O.O=CO') nmol = Chem.DeleteSubstructs(mol, query, 1) self.assertTrue(Chem.MolToSmiles(nmol) == 'CCC(=O)O') nmol = Chem.DeleteSubstructs(nmol, query, 1) self.assertTrue(Chem.MolToSmiles(nmol) == 'CCC(=O)O') nmol = Chem.DeleteSubstructs(nmol, query, 0) self.assertTrue(Chem.MolToSmiles(nmol) == 'CC') def test23MolFileParsing(self): fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'triazine.mol') with open(fileN, 'r') as inF: inD = inF.read() m1 = Chem.MolFromMolBlock(inD) self.assertTrue(m1 is not None) self.assertTrue(m1.GetNumAtoms() == 9) m1 = Chem.MolFromMolFile(fileN) self.assertTrue(m1 is not None) self.assertTrue(m1.GetNumAtoms() == 9) fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'triazine.mof') self.assertRaises(IOError, lambda: Chem.MolFromMolFile(fileN)) fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'list-query.mol') query = Chem.MolFromMolFile(fileN) smi = Chem.MolToSmiles(query) self.assertEqual(smi, '*1ccccc1') smi = Chem.MolToSmarts(query) self.assertEqual(smi, '[#6]1:[#6]:[#6]:[#6]:[#6]:[#6,#7,#15]:1') smi = Chem.MolToSmarts(query, rootedAtAtom=5) self.assertEqual(smi, '[#6,#7,#15]1:[#6]:[#6]:[#6]:[#6]:[#6]:1') query = Chem.MolFromMolFile(fileN, sanitize=False) smi = Chem.MolToSmiles(query) self.assertEqual(smi, '*1=CC=CC=C1') query.UpdatePropertyCache() smi = Chem.MolToSmarts(query) self.assertEqual(smi, '[#6]1=[#6]-[#6]=[#6]-[#6]=[#6,#7,#15]-1') smi = Chem.MolToSmarts(query, rootedAtAtom=3) self.assertEqual(smi, '[#6]1=[#6]-[#6]=[#6]-[#6,#7,#15]=[#6]-1') smi = "C1=CC=CC=C1" mol = Chem.MolFromSmiles(smi, 0) self.assertTrue(mol.HasSubstructMatch(query)) Chem.SanitizeMol(mol) self.assertTrue(not mol.HasSubstructMatch(query)) mol = Chem.MolFromSmiles('N1=CC=CC=C1', 0) self.assertTrue(mol.HasSubstructMatch(query)) mol = Chem.MolFromSmiles('S1=CC=CC=C1', 0) self.assertTrue(not mol.HasSubstructMatch(query)) mol = Chem.MolFromSmiles('P1=CC=CC=C1', 0) self.assertTrue(mol.HasSubstructMatch(query)) fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'issue123.mol') mol = Chem.MolFromMolFile(fileN) self.assertTrue(mol) self.assertEqual(mol.GetNumAtoms(), 23) mol = Chem.MolFromMolFile(fileN, removeHs=False) self.assertTrue(mol) self.assertEqual(mol.GetNumAtoms(), 39) # test23 was for Chem.DaylightFingerprint, which is deprecated def test24RDKFingerprint(self): from rdkit import DataStructs m1 = Chem.MolFromSmiles('C1=CC=CC=C1') fp1 = Chem.RDKFingerprint(m1) self.assertTrue(len(fp1) == 2048) m2 = Chem.MolFromSmiles('C1=CC=CC=C1') fp2 = Chem.RDKFingerprint(m2) tmp = DataStructs.TanimotoSimilarity(fp1, fp2) self.assertTrue(tmp == 1.0, tmp) m2 = Chem.MolFromSmiles('C1=CC=CC=N1') fp2 = Chem.RDKFingerprint(m2) self.assertTrue(len(fp2) == 2048) tmp = DataStructs.TanimotoSimilarity(fp1, fp2) self.assertTrue(tmp < 1.0, tmp) self.assertTrue(tmp > 0.0, tmp) fp3 = Chem.RDKFingerprint(m1, tgtDensity=0.3) self.assertTrue(len(fp3) < 2048) m1 = Chem.MolFromSmiles('C1=CC=CC=C1') fp1 = Chem.RDKFingerprint(m1) m2 = Chem.MolFromSmiles('C1=CC=CC=N1') fp2 = Chem.RDKFingerprint(m2) self.assertNotEqual(fp1, fp2) atomInvariants = [1] * 6 fp1 = Chem.RDKFingerprint(m1, atomInvariants=atomInvariants) fp2 = Chem.RDKFingerprint(m2, atomInvariants=atomInvariants) self.assertEqual(fp1, fp2) m2 = Chem.MolFromSmiles('C1CCCCN1') fp1 = Chem.RDKFingerprint(m1, atomInvariants=atomInvariants, useBondOrder=False) fp2 = Chem.RDKFingerprint(m2, atomInvariants=atomInvariants, useBondOrder=False) self.assertEqual(fp1, fp2) # rooted at atom m1 = Chem.MolFromSmiles('CCCCCO') fp1 = Chem.RDKFingerprint(m1, 1, 4, nBitsPerHash=1, fromAtoms=[0]) self.assertEqual(fp1.GetNumOnBits(), 4) m1 = Chem.MolFromSmiles('CCCCCO') fp1 = Chem.RDKFingerprint(m1, 1, 4, nBitsPerHash=1, fromAtoms=[0, 5]) self.assertEqual(fp1.GetNumOnBits(), 8) # test sf.net issue 270: fp1 = Chem.RDKFingerprint(m1, atomInvariants=[x.GetAtomicNum() + 10 for x in m1.GetAtoms()]) # atomBits m1 = Chem.MolFromSmiles('CCCO') l = [] fp1 = Chem.RDKFingerprint(m1, minPath=1, maxPath=2, nBitsPerHash=1, atomBits=l) self.assertEqual(fp1.GetNumOnBits(), 4) self.assertEqual(len(l), m1.GetNumAtoms()) self.assertEqual(len(l[0]), 2) self.assertEqual(len(l[1]), 3) self.assertEqual(len(l[2]), 4) self.assertEqual(len(l[3]), 2) def test25SDMolSupplier(self): fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'NCI_aids_few.sdf') #fileN = "../FileParsers/test_data/NCI_aids_few.sdf" sdSup = Chem.SDMolSupplier(fileN) molNames = [ "48", "78", "128", "163", "164", "170", "180", "186", "192", "203", "210", "211", "213", "220", "229", "256" ] chgs192 = {8: 1, 11: 1, 15: -1, 18: -1, 20: 1, 21: 1, 23: -1, 25: -1} i = 0 for mol in sdSup: self.assertTrue(mol) self.assertTrue(mol.GetProp("_Name") == molNames[i]) i += 1 if (mol.GetProp("_Name") == "192"): # test parsed charges on one of the molecules for id in chgs192.keys(): self.assertTrue(mol.GetAtomWithIdx(id).GetFormalCharge() == chgs192[id]) self.assertRaises(StopIteration, lambda: next(sdSup)) sdSup.reset() ns = [mol.GetProp("_Name") for mol in sdSup] self.assertTrue(ns == molNames) sdSup = Chem.SDMolSupplier(fileN, 0) for mol in sdSup: self.assertTrue(not mol.HasProp("numArom")) sdSup = Chem.SDMolSupplier(fileN) self.assertTrue(len(sdSup) == 16) mol = sdSup[5] self.assertTrue(mol.GetProp("_Name") == "170") # test handling of H removal: fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'withHs.sdf') sdSup = Chem.SDMolSupplier(fileN) m = next(sdSup) self.assertTrue(m) self.assertTrue(m.GetNumAtoms() == 23) m = next(sdSup) self.assertTrue(m) self.assertTrue(m.GetNumAtoms() == 28) sdSup = Chem.SDMolSupplier(fileN, removeHs=False) m = next(sdSup) self.assertTrue(m) self.assertTrue(m.GetNumAtoms() == 39) m = next(sdSup) self.assertTrue(m) self.assertTrue(m.GetNumAtoms() == 30) with open(fileN, 'rb') as dFile: d = dFile.read() sdSup.SetData(d) m = next(sdSup) self.assertTrue(m) self.assertTrue(m.GetNumAtoms() == 23) m = next(sdSup) self.assertTrue(m) self.assertTrue(m.GetNumAtoms() == 28) sdSup.SetData(d, removeHs=False) m = next(sdSup) self.assertTrue(m) self.assertTrue(m.GetNumAtoms() == 39) m = next(sdSup) self.assertTrue(m) self.assertTrue(m.GetNumAtoms() == 30) # test strictParsing1: fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'strictLax1.sdf') #strict from file sdSup = Chem.SDMolSupplier(fileN, strictParsing=True) i = 0 for mol in sdSup: self.assertTrue(mol.HasProp("_Name")) if (i == 0): self.assertTrue(not mol.HasProp("ID")) self.assertTrue(not mol.HasProp("ANOTHER_PROPERTY")) i += 1 self.assertTrue(i == 2) #lax from file sdSup = Chem.SDMolSupplier(fileN, strictParsing=False) i = 0 for mol in sdSup: self.assertTrue(mol.HasProp("_Name")) self.assertTrue(mol.HasProp("ID")) self.assertTrue(mol.HasProp("ANOTHER_PROPERTY")) i += 1 self.assertTrue(i == 2) #strict from text with open(fileN, 'rb') as dFile: d = dFile.read() sdSup = Chem.SDMolSupplier() sdSup.SetData(d, strictParsing=True) i = 0 for mol in sdSup: self.assertTrue(mol.HasProp("_Name")) if (i == 0): self.assertTrue(not mol.HasProp("ID")) self.assertTrue(not mol.HasProp("ANOTHER_PROPERTY")) i += 1 self.assertTrue(i == 2) #lax from text sdSup = Chem.SDMolSupplier() sdSup.SetData(d, strictParsing=False) i = 0 for mol in sdSup: self.assertTrue(mol.HasProp("_Name")) self.assertTrue(mol.HasProp("ID")) self.assertTrue(mol.HasProp("ANOTHER_PROPERTY")) i += 1 self.assertTrue(i == 2) # test strictParsing2: fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'strictLax2.sdf') #strict from file sdSup = Chem.SDMolSupplier(fileN, strictParsing=True) i = 0 for mol in sdSup: self.assertTrue(mol.HasProp("_Name")) self.assertTrue(mol.HasProp("ID")) self.assertTrue(mol.GetProp("ID") == "Lig1") self.assertTrue(mol.HasProp("ANOTHER_PROPERTY")) self.assertTrue( mol.GetProp("ANOTHER_PROPERTY") == "No blank line before dollars\n" "$$$$\n" "Structure1\n" "csChFnd70/05230312262D") i += 1 self.assertTrue(i == 1) #lax from file sdSup = Chem.SDMolSupplier(fileN, strictParsing=False) i = 0 for mol in sdSup: self.assertTrue(mol.HasProp("_Name")) self.assertTrue(mol.HasProp("ID")) self.assertTrue(mol.GetProp("ID") == "Lig2") self.assertTrue(mol.HasProp("ANOTHER_PROPERTY")) self.assertTrue(mol.GetProp("ANOTHER_PROPERTY") == "Value2") i += 1 self.assertTrue(i == 1) #strict from text with open(fileN, 'rb') as dFile: d = dFile.read() sdSup = Chem.SDMolSupplier() sdSup.SetData(d, strictParsing=True) i = 0 for mol in sdSup: self.assertTrue(mol.HasProp("_Name")) self.assertTrue(mol.HasProp("ID")) self.assertTrue(mol.GetProp("ID") == "Lig1") self.assertTrue(mol.HasProp("ANOTHER_PROPERTY")) self.assertTrue( mol.GetProp("ANOTHER_PROPERTY") == "No blank line before dollars\n" "$$$$\n" "Structure1\n" "csChFnd70/05230312262D") i += 1 self.assertTrue(i == 1) #lax from text sdSup = Chem.SDMolSupplier() sdSup.SetData(d, strictParsing=False) i = 0 for mol in sdSup: self.assertTrue(mol.HasProp("_Name")) self.assertTrue(mol.HasProp("ID")) self.assertTrue(mol.GetProp("ID") == "Lig2") self.assertTrue(mol.HasProp("ANOTHER_PROPERTY")) self.assertTrue(mol.GetProp("ANOTHER_PROPERTY") == "Value2") i += 1 self.assertTrue(i == 1) def test26SmiMolSupplier(self): fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'first_200.tpsa.csv') #fileN = "../FileParsers/test_data/first_200.tpsa.csv" smiSup = Chem.SmilesMolSupplier(fileN, ",", 0, -1) mol = smiSup[16] self.assertTrue(mol.GetProp("TPSA") == "46.25") mol = smiSup[8] self.assertTrue(mol.GetProp("TPSA") == "65.18") self.assertTrue(len(smiSup) == 200) fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'fewSmi.csv') #fileN = "../FileParsers/test_data/fewSmi.csv" smiSup = Chem.SmilesMolSupplier(fileN, delimiter=",", smilesColumn=1, nameColumn=0, titleLine=0) names = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"] i = 0 for mol in smiSup: self.assertTrue(mol.GetProp("_Name") == names[i]) i += 1 mol = smiSup[3] self.assertTrue(mol.GetProp("_Name") == "4") self.assertTrue(mol.GetProp("Column_2") == "82.78") # and test doing a supplier from text: with open(fileN, 'r') as inF: inD = inF.read() smiSup.SetData(inD, delimiter=",", smilesColumn=1, nameColumn=0, titleLine=0) names = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"] i = 0 # iteration interface: for mol in smiSup: self.assertTrue(mol.GetProp("_Name") == names[i]) i += 1 self.assertTrue(i == 10) # random access: mol = smiSup[3] self.assertTrue(len(smiSup) == 10) self.assertTrue(mol.GetProp("_Name") == "4") self.assertTrue(mol.GetProp("Column_2") == "82.78") # issue 113: smiSup.SetData(inD, delimiter=",", smilesColumn=1, nameColumn=0, titleLine=0) self.assertTrue(len(smiSup) == 10) # and test failure handling: inD = """mol-1,CCC mol-2,CCCC mol-3,fail mol-4,CCOC """ smiSup.SetData(inD, delimiter=",", smilesColumn=1, nameColumn=0, titleLine=0) # there are 4 entries in the supplier: self.assertTrue(len(smiSup) == 4) # but the 3rd is a None: self.assertTrue(smiSup[2] is None) text="Id SMILES Column_2\n"+\ "mol-1 C 1.0\n"+\ "mol-2 CC 4.0\n"+\ "mol-4 CCCC 16.0" smiSup.SetData(text, delimiter=" ", smilesColumn=1, nameColumn=0, titleLine=1) self.assertTrue(len(smiSup) == 3) self.assertTrue(smiSup[0]) self.assertTrue(smiSup[1]) self.assertTrue(smiSup[2]) m = [x for x in smiSup] self.assertTrue(smiSup[2]) self.assertTrue(len(m) == 3) self.assertTrue(m[0].GetProp("Column_2") == "1.0") # test simple parsing and Issue 114: smis = ['CC', 'CCC', 'CCOC', 'CCCOCC', 'CCCOCCC'] inD = '\n'.join(smis) smiSup.SetData(inD, delimiter=",", smilesColumn=0, nameColumn=-1, titleLine=0) self.assertTrue(len(smiSup) == 5) m = [x for x in smiSup] self.assertTrue(smiSup[4]) self.assertTrue(len(m) == 5) # order dependence: smiSup.SetData(inD, delimiter=",", smilesColumn=0, nameColumn=-1, titleLine=0) self.assertTrue(smiSup[4]) self.assertTrue(len(smiSup) == 5) # this was a nasty BC: # asking for a particular entry with a higher index than what we've # already seen resulted in a duplicate: smis = ['CC', 'CCC', 'CCOC', 'CCCCOC'] inD = '\n'.join(smis) smiSup.SetData(inD, delimiter=",", smilesColumn=0, nameColumn=-1, titleLine=0) m = next(smiSup) m = smiSup[3] self.assertTrue(len(smiSup) == 4) with self.assertRaisesRegex(Exception, ""): smiSup[4] smiSup.SetData(inD, delimiter=",", smilesColumn=0, nameColumn=-1, titleLine=0) with self.assertRaisesRegex(Exception, ""): smiSup[4] sys.stderr.write( '>>> This may result in an infinite loop. It should finish almost instantly\n') self.assertEqual(len(smiSup), 4) sys.stderr.write('<<< OK, it finished.\n') def test27SmilesWriter(self): fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'fewSmi.csv') #fileN = "../FileParsers/test_data/fewSmi.csv" smiSup = Chem.SmilesMolSupplier(fileN, delimiter=",", smilesColumn=1, nameColumn=0, titleLine=0) propNames = [] propNames.append("Column_2") ofile = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'Wrap', 'test_data', 'outSmiles.txt') writer = Chem.SmilesWriter(ofile) writer.SetProps(propNames) for mol in smiSup: writer.write(mol) writer.flush() def test28SmilesReverse(self): names = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"] props = [ "34.14", "25.78", "106.51", "82.78", "60.16", "87.74", "37.38", "77.28", "65.18", "0.00" ] ofile = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'Wrap', 'test_data', 'outSmiles.txt') #ofile = "test_data/outSmiles.csv" smiSup = Chem.SmilesMolSupplier(ofile) i = 0 for mol in smiSup: #print([repr(x) for x in mol.GetPropNames()]) self.assertTrue(mol.GetProp("_Name") == names[i]) self.assertTrue(mol.GetProp("Column_2") == props[i]) i += 1 def writerSDFile(self): fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'NCI_aids_few.sdf') #fileN = "../FileParsers/test_data/NCI_aids_few.sdf" ofile = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'Wrap', 'test_data', 'outNCI_few.sdf') writer = Chem.SDWriter(ofile) sdSup = Chem.SDMolSupplier(fileN) for mol in sdSup: writer.write(mol) writer.flush() def test29SDWriterLoop(self): self.writerSDFile() fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'Wrap', 'test_data', 'outNCI_few.sdf') sdSup = Chem.SDMolSupplier(fileN) molNames = [ "48", "78", "128", "163", "164", "170", "180", "186", "192", "203", "210", "211", "213", "220", "229", "256" ] chgs192 = {8: 1, 11: 1, 15: -1, 18: -1, 20: 1, 21: 1, 23: -1, 25: -1} i = 0 for mol in sdSup: #print('mol:',mol) #print('\t',molNames[i]) self.assertTrue(mol.GetProp("_Name") == molNames[i]) i += 1 if (mol.GetProp("_Name") == "192"): # test parsed charges on one of the molecules for id in chgs192.keys(): self.assertTrue(mol.GetAtomWithIdx(id).GetFormalCharge() == chgs192[id]) def test30Issues109and110(self): """ issues 110 and 109 were both related to handling of explicit Hs in SMILES input. """ m1 = Chem.MolFromSmiles('N12[CH](SC(C)(C)[CH]1C(O)=O)[CH](C2=O)NC(=O)[CH](N)c3ccccc3') self.assertTrue(m1.GetNumAtoms() == 24) m2 = Chem.MolFromSmiles( 'C1C=C([CH](N)C(=O)N[C]2([H])[C]3([H])SC(C)(C)[CH](C(=O)O)N3C(=O)2)C=CC=1') self.assertTrue(m2.GetNumAtoms() == 24) smi1 = Chem.MolToSmiles(m1) smi2 = Chem.MolToSmiles(m2) self.assertTrue(smi1 == smi2) m1 = Chem.MolFromSmiles('[H]CCl') self.assertTrue(m1.GetNumAtoms() == 2) self.assertTrue(m1.GetAtomWithIdx(0).GetNumExplicitHs() == 1) m1 = Chem.MolFromSmiles('[H][CH2]Cl') self.assertTrue(m1.GetNumAtoms() == 2) self.assertTrue(m1.GetAtomWithIdx(0).GetNumExplicitHs() == 3) m2 = Chem.AddHs(m1) self.assertTrue(m2.GetNumAtoms() == 5) m2 = Chem.RemoveHs(m2) self.assertTrue(m2.GetNumAtoms() == 2) def test31ChiralitySmiles(self): m1 = Chem.MolFromSmiles('F[C@](Br)(I)Cl') self.assertTrue(m1 is not None) self.assertTrue(m1.GetNumAtoms() == 5) self.assertTrue(Chem.MolToSmiles(m1, 1) == 'F[C@](Cl)(Br)I', Chem.MolToSmiles(m1, 1)) m1 = Chem.MolFromSmiles('CC1C[C@@]1(Cl)F') self.assertTrue(m1 is not None) self.assertTrue(m1.GetNumAtoms() == 6) self.assertTrue(Chem.MolToSmiles(m1, 1) == 'CC1C[C@]1(F)Cl', Chem.MolToSmiles(m1, 1)) m1 = Chem.MolFromSmiles('CC1C[C@]1(Cl)F') self.assertTrue(m1 is not None) self.assertTrue(m1.GetNumAtoms() == 6) self.assertTrue(Chem.MolToSmiles(m1, 1) == 'CC1C[C@@]1(F)Cl', Chem.MolToSmiles(m1, 1)) def test31aChiralitySubstructs(self): m1 = Chem.MolFromSmiles('CC1C[C@@]1(Cl)F') self.assertTrue(m1 is not None) self.assertTrue(m1.GetNumAtoms() == 6) self.assertTrue(Chem.MolToSmiles(m1, 1) == 'CC1C[C@]1(F)Cl', Chem.MolToSmiles(m1, 1)) m2 = Chem.MolFromSmiles('CC1C[C@]1(Cl)F') self.assertTrue(m2 is not None) self.assertTrue(m2.GetNumAtoms() == 6) self.assertTrue(Chem.MolToSmiles(m2, 1) == 'CC1C[C@@]1(F)Cl', Chem.MolToSmiles(m2, 1)) self.assertTrue(m1.HasSubstructMatch(m1)) self.assertTrue(m1.HasSubstructMatch(m2)) self.assertTrue(m1.HasSubstructMatch(m1, useChirality=True)) self.assertTrue(not m1.HasSubstructMatch(m2, useChirality=True)) def _test32MolFilesWithChirality(self): inD = """chiral1.mol ChemDraw10160313232D 5 4 0 0 0 0 0 0 0 0999 V2000 0.0553 0.6188 0.0000 F 0 0 0 0 0 0 0 0 0 0 0 0 0.0553 -0.2062 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.7697 -0.6188 0.0000 I 0 0 0 0 0 0 0 0 0 0 0 0 -0.6592 -0.6188 0.0000 Br 0 0 0 0 0 0 0 0 0 0 0 0 -0.7697 -0.2062 0.0000 Cl 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 2 3 1 0 2 4 1 1 2 5 1 0 M END """ m1 = Chem.MolFromMolBlock(inD) self.assertTrue(m1 is not None) self.assertTrue(m1.GetNumAtoms() == 5) smi = Chem.MolToSmiles(m1) self.assertTrue(smi == 'F[C@](Cl)(Br)I', smi) inD = """chiral2.cdxml ChemDraw10160314052D 5 4 0 0 0 0 0 0 0 0999 V2000 0.0553 0.6188 0.0000 F 0 0 0 0 0 0 0 0 0 0 0 0 0.0553 -0.2062 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.7697 -0.6188 0.0000 I 0 0 0 0 0 0 0 0 0 0 0 0 -0.6592 -0.6188 0.0000 Br 0 0 0 0 0 0 0 0 0 0 0 0 -0.7697 -0.2062 0.0000 Cl 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 2 3 1 0 2 4 1 6 2 5 1 0 M END """ m1 = Chem.MolFromMolBlock(inD) self.assertTrue(m1 is not None) self.assertTrue(m1.GetNumAtoms() == 5) self.assertTrue(Chem.MolToSmiles(m1, 1) == 'F[C@@](Cl)(Br)I') inD = """chiral1.mol ChemDraw10160313232D 5 4 0 0 0 0 0 0 0 0999 V2000 0.0553 0.6188 0.0000 F 0 0 0 0 0 0 0 0 0 0 0 0 0.0553 -0.2062 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.7697 -0.2062 0.0000 Cl 0 0 0 0 0 0 0 0 0 0 0 0 -0.6592 -0.6188 0.0000 Br 0 0 0 0 0 0 0 0 0 0 0 0 0.7697 -0.6188 0.0000 I 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 2 3 1 0 2 4 1 1 2 5 1 0 M END """ m1 = Chem.MolFromMolBlock(inD) self.assertTrue(m1 is not None) self.assertTrue(m1.GetNumAtoms() == 5) self.assertTrue(Chem.MolToSmiles(m1, 1) == 'F[C@](Cl)(Br)I') inD = """chiral1.mol ChemDraw10160313232D 5 4 0 0 0 0 0 0 0 0999 V2000 0.0553 -0.2062 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.7697 -0.2062 0.0000 Cl 0 0 0 0 0 0 0 0 0 0 0 0 -0.6592 -0.6188 0.0000 Br 0 0 0 0 0 0 0 0 0 0 0 0 0.7697 -0.6188 0.0000 I 0 0 0 0 0 0 0 0 0 0 0 0 0.0553 0.6188 0.0000 F 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 1 3 1 1 1 4 1 0 1 5 1 0 M END """ m1 = Chem.MolFromMolBlock(inD) self.assertTrue(m1 is not None) self.assertTrue(m1.GetNumAtoms() == 5) self.assertTrue(Chem.MolToSmiles(m1, 1) == 'F[C@](Cl)(Br)I') inD = """chiral3.mol ChemDraw10160314362D 4 3 0 0 0 0 0 0 0 0999 V2000 0.4125 0.6188 0.0000 F 0 0 0 0 0 0 0 0 0 0 0 0 0.4125 -0.2062 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.3020 -0.6188 0.0000 Br 0 0 0 0 0 0 0 0 0 0 0 0 -0.4125 -0.2062 0.0000 Cl 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 2 3 1 1 2 4 1 0 M END """ m1 = Chem.MolFromMolBlock(inD) self.assertTrue(m1 is not None) self.assertTrue(m1.GetNumAtoms() == 4) self.assertTrue(Chem.MolToSmiles(m1, 1) == 'F[C@H](Cl)Br') inD = """chiral4.mol ChemDraw10160314362D 4 3 0 0 0 0 0 0 0 0999 V2000 0.4125 0.6188 0.0000 F 0 0 0 0 0 0 0 0 0 0 0 0 0.4125 -0.2062 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 -0.3020 -0.6188 0.0000 Br 0 0 0 0 0 0 0 0 0 0 0 0 -0.4125 -0.2062 0.0000 Cl 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 2 3 1 1 2 4 1 0 M END """ m1 = Chem.MolFromMolBlock(inD) self.assertTrue(m1 is not None) self.assertTrue(m1.GetNumAtoms() == 4) self.assertTrue(Chem.MolToSmiles(m1, 1) == 'FN(Cl)Br') inD = """chiral5.mol ChemDraw10160314362D 4 3 0 0 0 0 0 0 0 0999 V2000 0.4125 0.6188 0.0000 F 0 0 0 0 0 0 0 0 0 0 0 0 0.4125 -0.2062 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 -0.3020 -0.6188 0.0000 Br 0 0 0 0 0 0 0 0 0 0 0 0 -0.4125 -0.2062 0.0000 Cl 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 2 3 1 1 2 4 1 0 M CHG 1 2 1 M END """ m1 = Chem.MolFromMolBlock(inD) self.assertTrue(m1 is not None) self.assertTrue(m1.GetNumAtoms() == 4) self.assertTrue(Chem.MolToSmiles(m1, 1) == 'F[N@H+](Cl)Br') inD = """Case 10-14-3 ChemDraw10140308512D 4 3 0 0 0 0 0 0 0 0999 V2000 -0.8250 -0.4125 0.0000 F 0 0 0 0 0 0 0 0 0 0 0 0 0.0000 -0.4125 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.8250 -0.4125 0.0000 Cl 0 0 0 0 0 0 0 0 0 0 0 0 0.0000 0.4125 0.0000 Br 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 2 3 1 0 2 4 1 1 M END """ m1 = Chem.MolFromMolBlock(inD) self.assertTrue(m1 is not None) self.assertTrue(m1.GetNumAtoms() == 4) self.assertTrue(Chem.MolToSmiles(m1, 1) == 'F[C@H](Cl)Br') inD = """Case 10-14-4 ChemDraw10140308512D 4 3 0 0 0 0 0 0 0 0999 V2000 -0.8250 -0.4125 0.0000 F 0 0 0 0 0 0 0 0 0 0 0 0 0.0000 -0.4125 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.8250 -0.4125 0.0000 Cl 0 0 0 0 0 0 0 0 0 0 0 0 0.0000 0.4125 0.0000 Br 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 2 3 1 1 2 4 1 0 M END """ m1 = Chem.MolFromMolBlock(inD) self.assertTrue(m1 is not None) self.assertTrue(m1.GetNumAtoms() == 4) self.assertTrue(Chem.MolToSmiles(m1, 1) == 'F[C@H](Cl)Br') inD = """chiral4.mol ChemDraw10160315172D 6 6 0 0 0 0 0 0 0 0999 V2000 -0.4422 0.1402 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.4422 -0.6848 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.2723 -0.2723 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.8547 0.8547 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.6848 0.4422 0.0000 F 0 0 0 0 0 0 0 0 0 0 0 0 0.8547 -0.8547 0.0000 Cl 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 2 3 1 0 3 1 1 0 1 4 1 0 3 5 1 1 3 6 1 0 M END """ m1 = Chem.MolFromMolBlock(inD) self.assertTrue(m1 is not None) self.assertTrue(m1.GetNumAtoms() == 6) self.assertTrue(Chem.MolToSmiles(m1, 1) == 'CC1C[C@@]1(F)Cl', Chem.MolToSmiles(m1, 1)) inD = """chiral4.mol ChemDraw10160315172D 6 6 0 0 0 0 0 0 0 0999 V2000 -0.4422 0.1402 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.4422 -0.6848 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.2723 -0.2723 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.8547 0.8547 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.6848 0.4422 0.0000 F 0 0 0 0 0 0 0 0 0 0 0 0 0.8547 -0.8547 0.0000 Cl 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 2 3 1 0 3 1 1 0 1 4 1 0 3 5 1 6 3 6 1 0 M END """ m1 = Chem.MolFromMolBlock(inD) self.assertTrue(m1 is not None) self.assertTrue(m1.GetNumAtoms() == 6) self.assertTrue(Chem.MolToSmiles(m1, 1) == 'CC1C[C@]1(F)Cl', Chem.MolToSmiles(m1, 1)) def test33Issue65(self): """ issue 65 relates to handling of [H] in SMARTS """ m1 = Chem.MolFromSmiles('OC(O)(O)O') m2 = Chem.MolFromSmiles('OC(O)O') m3 = Chem.MolFromSmiles('OCO') q1 = Chem.MolFromSmarts('OC[H]', 1) q2 = Chem.MolFromSmarts('O[C;H1]', 1) q3 = Chem.MolFromSmarts('O[C;H1][H]', 1) self.assertTrue(not m1.HasSubstructMatch(q1)) self.assertTrue(not m1.HasSubstructMatch(q2)) self.assertTrue(not m1.HasSubstructMatch(q3)) self.assertTrue(m2.HasSubstructMatch(q1)) self.assertTrue(m2.HasSubstructMatch(q2)) self.assertTrue(m2.HasSubstructMatch(q3)) self.assertTrue(m3.HasSubstructMatch(q1)) self.assertTrue(not m3.HasSubstructMatch(q2)) self.assertTrue(not m3.HasSubstructMatch(q3)) m1H = Chem.AddHs(m1) m2H = Chem.AddHs(m2) m3H = Chem.AddHs(m3) q1 = Chem.MolFromSmarts('OC[H]') q2 = Chem.MolFromSmarts('O[C;H1]') q3 = Chem.MolFromSmarts('O[C;H1][H]') self.assertTrue(not m1H.HasSubstructMatch(q1)) self.assertTrue(not m1H.HasSubstructMatch(q2)) self.assertTrue(not m1H.HasSubstructMatch(q3)) #m2H.Debug() self.assertTrue(m2H.HasSubstructMatch(q1)) self.assertTrue(m2H.HasSubstructMatch(q2)) self.assertTrue(m2H.HasSubstructMatch(q3)) self.assertTrue(m3H.HasSubstructMatch(q1)) self.assertTrue(not m3H.HasSubstructMatch(q2)) self.assertTrue(not m3H.HasSubstructMatch(q3)) def test34Issue124(self): """ issue 124 relates to calculation of the distance matrix """ m = Chem.MolFromSmiles('CC=C') d = Chem.GetDistanceMatrix(m, 0) self.assertTrue(feq(d[0, 1], 1.0)) self.assertTrue(feq(d[0, 2], 2.0)) # force an update: d = Chem.GetDistanceMatrix(m, 1, 0, 1) self.assertTrue(feq(d[0, 1], 1.0)) self.assertTrue(feq(d[0, 2], 1.5)) def test35ChiralityPerception(self): """ Test perception of chirality and CIP encoding """ m = Chem.MolFromSmiles('F[C@]([C@])(Cl)Br') Chem.AssignStereochemistry(m, 1) if Chem.GetUseLegacyStereoPerception(): self.assertTrue(m.GetAtomWithIdx(1).HasProp('_CIPCode')) self.assertFalse(m.GetAtomWithIdx(2).HasProp('_CIPCode')) Chem.RemoveStereochemistry(m) self.assertFalse(m.GetAtomWithIdx(1).HasProp('_CIPCode')) m = Chem.MolFromSmiles('F[C@H](C)C') Chem.AssignStereochemistry(m, 1) self.assertTrue(m.GetAtomWithIdx(1).GetChiralTag() == Chem.ChiralType.CHI_UNSPECIFIED) self.assertFalse(m.GetAtomWithIdx(1).HasProp('_CIPCode')) m = Chem.MolFromSmiles('F\\C=C/Cl') self.assertTrue(m.GetBondWithIdx(0).GetStereo() == Chem.BondStereo.STEREONONE) if Chem.GetUseLegacyStereoPerception(): self.assertTrue(m.GetBondWithIdx(1).GetStereo() == Chem.BondStereo.STEREOZ) else: self.assertTrue(m.GetBondWithIdx(1).GetStereo() == Chem.BondStereo.STEREOCIS) atoms = m.GetBondWithIdx(1).GetStereoAtoms() self.assertTrue(0 in atoms) self.assertTrue(3 in atoms) self.assertTrue(m.GetBondWithIdx(2).GetStereo() == Chem.BondStereo.STEREONONE) Chem.RemoveStereochemistry(m) self.assertTrue(m.GetBondWithIdx(1).GetStereo() == Chem.BondStereo.STEREONONE) m = Chem.MolFromSmiles('F\\C=CCl') self.assertTrue(m.GetBondWithIdx(1).GetStereo() == Chem.BondStereo.STEREONONE) def checkDefaultBondProperties(self, m): for bond in m.GetBonds(): self.assertIn(bond.GetBondType(), [Chem.BondType.SINGLE, Chem.BondType.DOUBLE]) self.assertEqual(bond.GetBondDir(), Chem.BondDir.NONE) self.assertEqual(list(bond.GetStereoAtoms()), []) self.assertEqual(bond.GetStereo(), Chem.BondStereo.STEREONONE) def assertHasDoubleBondStereo(self, smi): m = Chem.MolFromSmiles(smi) self.checkDefaultBondProperties(m) Chem.FindPotentialStereoBonds(m) for bond in m.GetBonds(): self.assertIn(bond.GetBondType(), [Chem.BondType.SINGLE, Chem.BondType.DOUBLE]) self.assertEqual(bond.GetBondDir(), Chem.BondDir.NONE) if bond.GetBondType() == Chem.BondType.DOUBLE: self.assertEqual(bond.GetStereo(), Chem.BondStereo.STEREOANY) self.assertEqual(len(list(bond.GetStereoAtoms())), 2) else: self.assertEqual(list(bond.GetStereoAtoms()), []) self.assertEqual(bond.GetStereo(), Chem.BondStereo.STEREONONE) def testFindPotentialStereoBonds(self): self.assertHasDoubleBondStereo("FC=CF") self.assertHasDoubleBondStereo("FC(Cl)=C(Br)I") self.assertHasDoubleBondStereo("FC=CC=CC=CCl") self.assertHasDoubleBondStereo("C1CCCCC1C=CC1CCCCC1") def assertDoesNotHaveDoubleBondStereo(self, smi): m = Chem.MolFromSmiles(smi) self.checkDefaultBondProperties(m) Chem.FindPotentialStereoBonds(m) self.checkDefaultBondProperties(m) def testFindPotentialStereoBondsShouldNotFindThisDoubleBondAsStereo(self): self.assertDoesNotHaveDoubleBondStereo("FC(F)=CF") self.assertDoesNotHaveDoubleBondStereo("C=C") self.assertDoesNotHaveDoubleBondStereo("C1CCCCC1C(C1CCCCC1)=CC1CCCCC1") def assertDoubleBondStereo(self, smi, stereo): mol = Chem.MolFromSmiles(smi) bond = mol.GetBondWithIdx(1) self.assertEqual(bond.GetBondType(), Chem.BondType.DOUBLE) self.assertEqual(bond.GetStereo(), stereo) self.assertEqual(list(bond.GetStereoAtoms()), [0, 3]) def allStereoBonds(self, bonds): for bond in bonds: self.assertEqual(len(list(bond.GetStereoAtoms())), 2) def testBondSetStereo(self): for testAssignStereo in [False, True]: mol = Chem.MolFromSmiles("FC=CF") Chem.FindPotentialStereoBonds(mol) for bond in mol.GetBonds(): if (bond.GetBondType() == Chem.BondType.DOUBLE and bond.GetStereo() == Chem.BondStereo.STEREOANY): break self.assertEqual(bond.GetBondType(), Chem.BondType.DOUBLE) self.assertEqual(bond.GetStereo(), Chem.BondStereo.STEREOANY) self.assertEqual(list(bond.GetStereoAtoms()), [0, 3]) bond.SetStereo(Chem.BondStereo.STEREOTRANS) self.assertEqual(bond.GetStereo(), Chem.BondStereo.STEREOTRANS) if testAssignStereo: # should be invariant of Chem.AssignStereochemistry being called Chem.AssignStereochemistry(mol, force=True) smi = Chem.MolToSmiles(mol, isomericSmiles=True) self.allStereoBonds([bond]) self.assertEqual(smi, "F/C=C/F") if Chem.GetUseLegacyStereoPerception(): self.assertDoubleBondStereo(smi, Chem.BondStereo.STEREOE) else: self.assertDoubleBondStereo(smi, Chem.BondStereo.STEREOTRANS) bond.SetStereo(Chem.BondStereo.STEREOCIS) self.assertEqual(bond.GetStereo(), Chem.BondStereo.STEREOCIS) if testAssignStereo: Chem.AssignStereochemistry(mol, force=True) smi = Chem.MolToSmiles(mol, isomericSmiles=True) self.allStereoBonds([bond]) self.assertEqual(smi, r"F/C=C\F") if Chem.GetUseLegacyStereoPerception(): self.assertDoubleBondStereo(smi, Chem.BondStereo.STEREOZ) else: self.assertDoubleBondStereo(smi, Chem.BondStereo.STEREOCIS) def recursive_enumerate_stereo_bonds(self, mol, done_bonds, bonds): if not bonds: yield done_bonds, Chem.Mol(mol) return bond = bonds[0] child_bonds = bonds[1:] self.assertEqual(len(list(bond.GetStereoAtoms())), 2) bond.SetStereo(Chem.BondStereo.STEREOTRANS) for isomer in self.recursive_enumerate_stereo_bonds(mol, done_bonds + [Chem.BondStereo.STEREOTRANS], child_bonds): yield isomer self.assertEqual(len(list(bond.GetStereoAtoms())), 2) bond.SetStereo(Chem.BondStereo.STEREOCIS) for isomer in self.recursive_enumerate_stereo_bonds(mol, done_bonds + [Chem.BondStereo.STEREOCIS], child_bonds): yield isomer def testBondSetStereoDifficultCase(self): origVal = Chem.GetUseLegacyStereoPerception() Chem.SetUseLegacyStereoPerception(False) unspec_smiles = "CCC=CC(CO)=C(C)CC" mol = Chem.MolFromSmiles(unspec_smiles) Chem.FindPotentialStereoBonds(mol) stereo_bonds = [] for bond in mol.GetBonds(): if bond.GetStereo() == Chem.BondStereo.STEREOANY: stereo_bonds.append(bond) isomers = set() for bond_stereo, isomer in self.recursive_enumerate_stereo_bonds(mol, [], stereo_bonds): self.allStereoBonds(stereo_bonds) isosmi = Chem.MolToSmiles(isomer, isomericSmiles=True) self.allStereoBonds(stereo_bonds) self.assertNotIn(isosmi, isomers) isomers.add(isosmi) isomol = Chem.MolFromSmiles(isosmi) round_trip_stereo = [ b.GetStereo() for b in isomol.GetBonds() if b.GetStereo() != Chem.BondStereo.STEREONONE ] self.assertEqual(bond_stereo, round_trip_stereo) self.assertEqual(len(isomers), 4) Chem.SetUseLegacyStereoPerception(origVal) def getNumUnspecifiedBondStereo(self, smi): mol = Chem.MolFromSmiles(smi) Chem.FindPotentialStereoBonds(mol) count = 0 for bond in mol.GetBonds(): if bond.GetStereo() != Chem.BondStereo.STEREONONE: print(bond.GetIdx(), bond.GetStereo()) if bond.GetStereo() == Chem.BondStereo.STEREOANY: count += 1 return count def testBondSetStereoReallyDifficultCase(self): # this one is much trickier because a double bond can gain and # lose it's stereochemistry based upon whether 2 other double # bonds have the same or different stereo chemistry. Chem.SetUseLegacyStereoPerception(True) unspec_smiles = "CCC=CC(C=CCC)=C(CO)CC" mol = Chem.MolFromSmiles(unspec_smiles) Chem.FindPotentialStereoBonds(mol) stereo_bonds = [] for bond in mol.GetBonds(): if bond.GetStereo() == Chem.BondStereo.STEREOANY: stereo_bonds.append(bond) self.assertEqual(len(stereo_bonds), 2) isomers = set() for bond_stereo, isomer in self.recursive_enumerate_stereo_bonds(mol, [], stereo_bonds): isosmi = Chem.MolToSmiles(isomer, isomericSmiles=True) isomers.add(isosmi) print(isosmi) self.assertEqual(len(isomers), 3) # one of these then gains a new stereo bond due to the # introduction of a new symmetry counts = {} for isosmi in isomers: print(isosmi) num_unspecified = self.getNumUnspecifiedBondStereo(isosmi) counts[num_unspecified] = counts.get(num_unspecified, 0) + 1 # 2 of the isomers don't have any unspecified bond stereo centers # left, 1 does self.assertEqual(counts, {0: 2, 1: 1}) def assertBondSetStereoIsAlwaysEquivalent(self, all_smiles, desired_stereo, bond_idx): refSmiles = None for smi in all_smiles: mol = Chem.MolFromSmiles(smi) doubleBond = None for bond in mol.GetBonds(): if bond.GetBondType() == Chem.BondType.DOUBLE: doubleBond = bond self.assertTrue(doubleBond is not None) Chem.FindPotentialStereoBonds(mol) doubleBond.SetStereo(desired_stereo) isosmi = Chem.MolToSmiles(mol, isomericSmiles=True) if refSmiles is None: refSmiles = isosmi self.assertEqual(refSmiles, isosmi) def testBondSetStereoAllHalogens(self): # can't get much more brutal than this test from itertools import combinations, permutations halogens = ['F', 'Cl', 'Br', 'I'] # binary double bond stereo for unique_set in combinations(halogens, 2): all_smiles = [] for fmt in ['%sC=C%s', 'C(%s)=C%s']: for ordering in permutations(unique_set): all_smiles.append(fmt % ordering) #print(fmt, all_smiles) for desired_stereo in [Chem.BondStereo.STEREOTRANS, Chem.BondStereo.STEREOCIS]: self.assertBondSetStereoIsAlwaysEquivalent(all_smiles, desired_stereo, 1) # tertiary double bond stereo for unique_set in combinations(halogens, 3): for mono_side in unique_set: halogens_left = list(unique_set) halogens_left.remove(mono_side) for binary_side in combinations(halogens_left, 2): all_smiles = [] for binary_side_permutation in permutations(binary_side): all_smiles.append('%sC=C(%s)%s' % ((mono_side, ) + binary_side_permutation)) all_smiles.append('C(%s)=C(%s)%s' % ((mono_side, ) + binary_side_permutation)) all_smiles.append('%sC(%s)=C%s' % (binary_side_permutation + (mono_side, ))) all_smiles.append('C(%s)(%s)=C%s' % (binary_side_permutation + (mono_side, ))) #print(all_smiles) for desired_stereo in [Chem.BondStereo.STEREOTRANS, Chem.BondStereo.STEREOCIS]: self.assertBondSetStereoIsAlwaysEquivalent(all_smiles, desired_stereo, 1) # quaternary double bond stereo for unique_ordering in permutations(halogens): left_side = unique_ordering[:2] rght_side = unique_ordering[2:] all_smiles = [] for left_side_permutation in permutations(left_side): for rght_side_permutation in permutations(rght_side): for smifmt in ['%sC(%s)=C(%s)%s', 'C(%s)(%s)=C(%s)%s']: all_smiles.append(smifmt % (left_side_permutation + rght_side_permutation)) #print(all_smiles) for desired_stereo in [Chem.BondStereo.STEREOTRANS, Chem.BondStereo.STEREOCIS]: self.assertBondSetStereoIsAlwaysEquivalent(all_smiles, desired_stereo, 1) def testBondSetStereoAtoms(self): # use this difficult molecule that only generates 4 isomers, but # assume all double bonds are stereo! unspec_smiles = "CCC=CC(C=CCC)=C(CO)CC" mol = Chem.MolFromSmiles(unspec_smiles) def getNbr(atom, exclude): for nbr in atom.GetNeighbors(): if nbr.GetIdx() not in exclude: return nbr raise ValueError("No neighbor found!") double_bonds = [] for bond in mol.GetBonds(): if bond.GetBondType() == 2: double_bonds.append(bond) exclude = {bond.GetBeginAtomIdx(), bond.GetEndAtomIdx()} bgnNbr = getNbr(bond.GetBeginAtom(), exclude) endNbr = getNbr(bond.GetEndAtom(), exclude) bond.SetStereoAtoms(bgnNbr.GetIdx(), endNbr.GetIdx()) self.assertEqual(len(double_bonds), 3) import itertools stereos = [Chem.BondStereo.STEREOE, Chem.BondStereo.STEREOZ] isomers = set() for stereo_config in itertools.product(stereos, repeat=len(double_bonds)): for bond, stereo in zip(double_bonds, stereo_config): bond.SetStereo(stereo) smi = Chem.MolToSmiles(mol, True) isomers.add(smi) # the dependent double bond stereo isn't picked up by this, should it? self.assertEqual(len(isomers), 6) # round tripping them through one more time does pick up the dependency, so meh? round_trip_isomers = set() for smi in isomers: isosmi = Chem.MolToSmiles(Chem.MolFromSmiles(smi), True) round_trip_isomers.add(isosmi) self.assertEqual(len(round_trip_isomers), 4) def test36SubstructMatchStr(self): """ test the _SubstructMatchStr function """ query = Chem.MolFromSmarts('[n,p]1ccccc1') self.assertTrue(query) mol = Chem.MolFromSmiles('N1=CC=CC=C1') self.assertTrue(mol.HasSubstructMatch(query)) self.assertTrue(Chem._HasSubstructMatchStr(mol.ToBinary(), query)) mol = Chem.MolFromSmiles('S1=CC=CC=C1') self.assertTrue(not Chem._HasSubstructMatchStr(mol.ToBinary(), query)) self.assertTrue(not mol.HasSubstructMatch(query)) mol = Chem.MolFromSmiles('P1=CC=CC=C1') self.assertTrue(mol.HasSubstructMatch(query)) self.assertTrue(Chem._HasSubstructMatchStr(mol.ToBinary(), query)) def test37SanitException(self): mol = Chem.MolFromSmiles('CC(C)(C)(C)C', 0) self.assertTrue(mol) self.assertRaises(ValueError, lambda: Chem.SanitizeMol(mol)) def test38TDTSuppliers(self): data = """$SMI CAS<17584-12-2> | $SMI CAS<~> | $SMI CAS<932-53-6> | $SMI CAS<~> |""" suppl = Chem.TDTMolSupplier() suppl.SetData(data, "CAS") i = 0 for mol in suppl: self.assertTrue(mol) self.assertTrue(mol.GetNumAtoms()) self.assertTrue(mol.HasProp("CAS")) self.assertTrue(mol.HasProp("_Name")) self.assertTrue(mol.GetProp("CAS") == mol.GetProp("_Name")) self.assertTrue(mol.GetNumConformers() == 0) i += 1 self.assertTrue(i == 4) self.assertTrue(len(suppl) == 4) def test38Issue266(self): """ test issue 266: generation of kekulized smiles""" mol = Chem.MolFromSmiles('c1ccccc1') Chem.Kekulize(mol) smi = Chem.MolToSmiles(mol) self.assertTrue(smi == 'c1ccccc1') smi = Chem.MolToSmiles(mol, kekuleSmiles=True) self.assertTrue(smi == 'C1=CC=CC=C1') def test39Issue273(self): """ test issue 273: MolFileComments and MolFileInfo props ending up in SD files """ fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'Wrap', 'test_data', 'outNCI_few.sdf') suppl = Chem.SDMolSupplier(fileN) ms = [x for x in suppl] for m in ms: self.assertTrue(m.HasProp('_MolFileInfo')) self.assertTrue(m.HasProp('_MolFileComments')) fName = tempfile.NamedTemporaryFile(suffix='.sdf', delete=False).name w = Chem.SDWriter(fName) w.SetProps(ms[0].GetPropNames()) for m in ms: w.write(m) w = None with open(fName, 'r') as txtFile: txt = txtFile.read() os.unlink(fName) self.assertTrue(txt.find('MolFileInfo') == -1) self.assertTrue(txt.find('MolFileComments') == -1) def test40SmilesRootedAtAtom(self): """ test the rootAtAtom functionality """ smi = 'CN(C)C' m = Chem.MolFromSmiles(smi) self.assertTrue(Chem.MolToSmiles(m) == 'CN(C)C') self.assertTrue(Chem.MolToSmiles(m, rootedAtAtom=1) == 'N(C)(C)C') def test41SetStreamIndices(self): fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'NCI_aids_few.sdf') allIndices = [] ifs = open(fileN, 'rb') addIndex = True line = True pos = 0 while (line): if (addIndex): pos = ifs.tell() line = ifs.readline().decode('utf-8') if (line): if (addIndex): allIndices.append(pos) addIndex = (line[:4] == '$$$$') ifs.close() indices = allIndices sdSup = Chem.SDMolSupplier(fileN) molNames = [ "48", "78", "128", "163", "164", "170", "180", "186", "192", "203", "210", "211", "213", "220", "229", "256" ] sdSup._SetStreamIndices(indices) self.assertTrue(len(sdSup) == 16) mol = sdSup[5] self.assertTrue(mol.GetProp("_Name") == "170") i = 0 for mol in sdSup: self.assertTrue(mol) self.assertTrue(mol.GetProp("_Name") == molNames[i]) i += 1 ns = [mol.GetProp("_Name") for mol in sdSup] self.assertTrue(ns == molNames) # this can also be used to skip molecules in the file: indices = [allIndices[0], allIndices[2], allIndices[5]] sdSup._SetStreamIndices(indices) self.assertTrue(len(sdSup) == 3) mol = sdSup[2] self.assertTrue(mol.GetProp("_Name") == "170") # or to reorder them: indices = [allIndices[0], allIndices[5], allIndices[2]] sdSup._SetStreamIndices(indices) self.assertTrue(len(sdSup) == 3) mol = sdSup[1] self.assertTrue(mol.GetProp("_Name") == "170") def test42LifeTheUniverseAndEverything(self): self.assertTrue(True) def test43TplFileParsing(self): fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'cmpd2.tpl') m1 = Chem.MolFromTPLFile(fileN) self.assertTrue(m1 is not None) self.assertTrue(m1.GetNumAtoms() == 12) self.assertTrue(m1.GetNumConformers() == 2) m1 = Chem.MolFromTPLFile(fileN, skipFirstConf=True) self.assertTrue(m1 is not None) self.assertTrue(m1.GetNumAtoms() == 12) self.assertTrue(m1.GetNumConformers() == 1) with open(fileN, 'r') as blockFile: block = blockFile.read() m1 = Chem.MolFromTPLBlock(block) self.assertTrue(m1 is not None) self.assertTrue(m1.GetNumAtoms() == 12) self.assertTrue(m1.GetNumConformers() == 2) def test44TplFileWriting(self): fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'cmpd2.tpl') m1 = Chem.MolFromTPLFile(fileN) self.assertTrue(m1 is not None) self.assertTrue(m1.GetNumAtoms() == 12) self.assertTrue(m1.GetNumConformers() == 2) block = Chem.MolToTPLBlock(m1) m1 = Chem.MolFromTPLBlock(block) self.assertTrue(m1 is not None) self.assertTrue(m1.GetNumAtoms() == 12) self.assertTrue(m1.GetNumConformers() == 2) def test45RingInfo(self): """ test the RingInfo class """ smi = 'CNC' m = Chem.MolFromSmiles(smi) ri = m.GetRingInfo() self.assertTrue(ri) self.assertTrue(ri.NumRings() == 0) self.assertFalse(ri.IsAtomInRingOfSize(0, 3)) self.assertFalse(ri.IsAtomInRingOfSize(1, 3)) self.assertFalse(ri.IsAtomInRingOfSize(2, 3)) self.assertFalse(ri.IsBondInRingOfSize(1, 3)) self.assertFalse(ri.IsBondInRingOfSize(2, 3)) if hasattr(Chem, 'FindRingFamilies'): self.assertEqual(ri.AtomRingFamilies(), ()) if hasattr(Chem, 'FindRingFamilies'): self.assertEqual(ri.BondRingFamilies(), ()) smi = 'C1CC2C1C2' m = Chem.MolFromSmiles(smi) ri = m.GetRingInfo() self.assertTrue(ri) self.assertEqual(ri.NumRings(), 2) self.assertFalse(ri.IsAtomInRingOfSize(0, 3)) self.assertTrue(ri.IsAtomInRingOfSize(0, 4)) self.assertFalse(ri.IsBondInRingOfSize(0, 3)) self.assertTrue(ri.IsBondInRingOfSize(0, 4)) self.assertTrue(ri.IsAtomInRingOfSize(2, 4)) self.assertTrue(ri.IsAtomInRingOfSize(2, 3)) self.assertTrue(ri.IsBondInRingOfSize(2, 3)) self.assertTrue(ri.IsBondInRingOfSize(2, 4)) self.assertEqual(ri.AtomRings(), ((0, 1, 2, 3), (2, 3, 4))) self.assertEqual(ri.BondRings(), ((0, 1, 2, 4), (2, 3, 5))) self.assertEqual(len(ri.AtomMembers(2)), 2) self.assertEqual(ri.AtomRingSizes(2), (4, 3)) self.assertEqual(ri.AtomRingSizes(99), ()) self.assertTrue(ri.AreAtomsInSameRing(2, 3)) self.assertFalse(ri.AreAtomsInSameRing(1, 4)) self.assertTrue(ri.AreAtomsInSameRingOfSize(2, 3, 3)) self.assertTrue(ri.AreAtomsInSameRingOfSize(2, 3, 4)) self.assertFalse(ri.AreAtomsInSameRingOfSize(2, 3, 5)) self.assertEqual(len(ri.BondMembers(2)), 2) self.assertEqual(len(ri.BondMembers(0)), 1) self.assertTrue(ri.IsRingFused(0)) self.assertTrue(ri.IsRingFused(1)) self.assertTrue(ri.AreRingsFused(0, 1)) self.assertTrue(ri.NumFusedBonds(0) == 1) self.assertTrue(ri.NumFusedBonds(1) == 1) self.assertEqual(ri.BondRingSizes(2), (4, 3)) self.assertEqual(ri.BondRingSizes(0), (4, )) self.assertEqual(ri.BondRingSizes(99), ()) self.assertTrue(ri.AreBondsInSameRing(1, 2)) self.assertTrue(ri.AreBondsInSameRing(2, 5)) self.assertFalse(ri.AreBondsInSameRing(1, 3)) self.assertTrue(ri.AreBondsInSameRingOfSize(1, 2, 4)) self.assertTrue(ri.AreBondsInSameRingOfSize(2, 5, 3)) self.assertFalse(ri.AreBondsInSameRingOfSize(1, 2, 3)) self.assertFalse(ri.AreBondsInSameRingOfSize(1, 3, 4)) if hasattr(Chem, 'FindRingFamilies'): ri = m.GetRingInfo() self.assertFalse(ri.AreRingFamiliesInitialized()) Chem.FindRingFamilies(m) ri = m.GetRingInfo() self.assertTrue(ri.AreRingFamiliesInitialized()) self.assertEqual(ri.NumRingFamilies(), 2) self.assertEqual(sorted(ri.AtomRingFamilies()), [(0, 1, 2, 3), (2, 3, 4)]) self.assertEqual(sorted(ri.BondRingFamilies()), [(0, 1, 2, 4), (2, 3, 5)]) def test46ReplaceCore(self): """ test the ReplaceCore functionality """ core = Chem.MolFromSmiles('C=O') smi = 'CCC=O' m = Chem.MolFromSmiles(smi) r = Chem.ReplaceCore(m, core) self.assertTrue(r) self.assertEqual(Chem.MolToSmiles(r, True), '[1*]CC') smi = 'C1CC(=O)CC1' m = Chem.MolFromSmiles(smi) r = Chem.ReplaceCore(m, core) self.assertTrue(r) self.assertEqual(Chem.MolToSmiles(r, True), '[1*]CCCC[2*]') smi = 'C1CC(=N)CC1' m = Chem.MolFromSmiles(smi) r = Chem.ReplaceCore(m, core) self.assertFalse(r) # smiles, smarts, replaceDummies, labelByIndex, useChirality expected = { ('C1O[C@@]1(OC)NC', 'C1O[C@]1(*)*', False, False, False): '[1*]OC.[2*]NC', ('C1O[C@@]1(OC)NC', 'C1O[C@]1(*)*', False, False, True): '[1*]NC.[2*]OC', ('C1O[C@@]1(OC)NC', 'C1O[C@]1(*)*', False, True, False): '[3*]OC.[4*]NC', ('C1O[C@@]1(OC)NC', 'C1O[C@]1(*)*', False, True, True): '[3*]NC.[4*]OC', ('C1O[C@@]1(OC)NC', 'C1O[C@]1(*)*', True, False, False): '[1*]C.[2*]C', ('C1O[C@@]1(OC)NC', 'C1O[C@]1(*)*', True, False, True): '[1*]C.[2*]C', ('C1O[C@@]1(OC)NC', 'C1O[C@]1(*)*', True, True, False): '[3*]C.[4*]C', ('C1O[C@@]1(OC)NC', 'C1O[C@]1(*)*', True, True, True): '[3*]C.[4*]C', ('C1O[C@]1(OC)NC', 'C1O[C@]1(*)*', False, False, False): '[1*]OC.[2*]NC', ('C1O[C@]1(OC)NC', 'C1O[C@]1(*)*', False, False, True): '[1*]OC.[2*]NC', ('C1O[C@]1(OC)NC', 'C1O[C@]1(*)*', False, True, False): '[3*]OC.[4*]NC', ('C1O[C@]1(OC)NC', 'C1O[C@]1(*)*', False, True, True): '[3*]OC.[4*]NC', ('C1O[C@]1(OC)NC', 'C1O[C@]1(*)*', True, False, False): '[1*]C.[2*]C', ('C1O[C@]1(OC)NC', 'C1O[C@]1(*)*', True, False, True): '[1*]C.[2*]C', ('C1O[C@]1(OC)NC', 'C1O[C@]1(*)*', True, True, False): '[3*]C.[4*]C', ('C1O[C@]1(OC)NC', 'C1O[C@]1(*)*', True, True, True): '[3*]C.[4*]C', } for (smiles, smarts, replaceDummies, labelByIndex, useChirality), expected_smiles in expected.items(): mol = Chem.MolFromSmiles(smiles) core = Chem.MolFromSmarts(smarts) nm = Chem.ReplaceCore(mol, core, replaceDummies=replaceDummies, labelByIndex=labelByIndex, useChirality=useChirality) if Chem.MolToSmiles(nm, True) != expected_smiles: print( "ReplaceCore(%r, %r, replaceDummies=%r, labelByIndex=%r, useChirality=%r" % (smiles, smarts, replaceDummies, labelByIndex, useChirality), file=sys.stderr) print("expected: %s\ngot: %s" % (expected_smiles, Chem.MolToSmiles(nm, True)), file=sys.stderr) self.assertEqual(expected_smiles, Chem.MolToSmiles(nm, True)) matchVect = mol.GetSubstructMatch(core, useChirality=useChirality) nm = Chem.ReplaceCore(mol, core, matchVect, replaceDummies=replaceDummies, labelByIndex=labelByIndex) if Chem.MolToSmiles(nm, True) != expected_smiles: print( "ReplaceCore(%r, %r, %r, replaceDummies=%r, labelByIndex=%rr" % (smiles, smarts, matchVect, replaceDummies, labelByIndex), file=sys.stderr) print("expected: %s\ngot: %s" % (expected_smiles, Chem.MolToSmiles(nm, True)), file=sys.stderr) self.assertEqual(expected_smiles, Chem.MolToSmiles(nm, True)) mol = Chem.MolFromSmiles("C") smarts = Chem.MolFromSmarts("C") try: Chem.ReplaceCore(mol, smarts, (3, )) self.asssertFalse(True) except Exception: pass mol = Chem.MolFromSmiles("C") smarts = Chem.MolFromSmarts("C") try: Chem.ReplaceCore(mol, smarts, (0, 0)) self.asssertFalse(True) except Exception: pass def test47RWMols(self): """ test the RWMol class """ mol = Chem.MolFromSmiles('C1CCC1') self.assertTrue(type(mol) == Chem.Mol) for rwmol in [Chem.EditableMol(mol), Chem.RWMol(mol)]: self.assertTrue(type(rwmol) in [Chem.EditableMol, Chem.RWMol]) newAt = Chem.Atom(8) rwmol.ReplaceAtom(0, newAt) self.assertTrue(Chem.MolToSmiles(rwmol.GetMol()) == 'C1COC1') rwmol.RemoveBond(0, 1) self.assertTrue(Chem.MolToSmiles(rwmol.GetMol()) == 'CCCO') a = Chem.Atom(7) idx = rwmol.AddAtom(a) self.assertEqual(rwmol.GetMol().GetNumAtoms(), 5) self.assertEqual(idx, 4) idx = rwmol.AddBond(0, 4, order=Chem.BondType.SINGLE) self.assertEqual(idx, 4) self.assertTrue(Chem.MolToSmiles(rwmol.GetMol()) == 'CCCON') rwmol.AddBond(4, 1, order=Chem.BondType.SINGLE) self.assertTrue(Chem.MolToSmiles(rwmol.GetMol()) == 'C1CNOC1') rwmol.RemoveAtom(3) self.assertTrue(Chem.MolToSmiles(rwmol.GetMol()) == 'CCNO') # practice shooting ourselves in the foot: m = Chem.MolFromSmiles('c1ccccc1') em = Chem.EditableMol(m) em.RemoveAtom(0) m2 = em.GetMol() self.assertRaises(ValueError, lambda: Chem.SanitizeMol(m2)) m = Chem.MolFromSmiles('c1ccccc1') em = Chem.EditableMol(m) em.RemoveBond(0, 1) m2 = em.GetMol() self.assertRaises(ValueError, lambda: Chem.SanitizeMol(m2)) # boundary cases: # removing non-existent bonds: m = Chem.MolFromSmiles('c1ccccc1') em = Chem.EditableMol(m) em.RemoveBond(0, 2) m2 = em.GetMol() Chem.SanitizeMol(m2) self.assertTrue(Chem.MolToSmiles(m2) == 'c1ccccc1') # removing non-existent atoms: m = Chem.MolFromSmiles('c1ccccc1') em = Chem.EditableMol(m) self.assertRaises(RuntimeError, lambda: em.RemoveAtom(12)) # confirm that an RWMol can be constructed without arguments m = Chem.RWMol() # test replaceAtom/Bond preserving properties mol = Chem.MolFromSmiles('C1CCC1') mol2 = Chem.MolFromSmiles('C1CCC1') mol.GetAtomWithIdx(0).SetProp("foo", "bar") mol.GetBondWithIdx(0).SetProp("foo", "bar") newBond = mol2.GetBondWithIdx(0) self.assertTrue(type(mol) == Chem.Mol) for rwmol in [Chem.EditableMol(mol), Chem.RWMol(mol)]: newAt = Chem.Atom(8) rwmol.ReplaceAtom(0, newAt) self.assertTrue(Chem.MolToSmiles(rwmol.GetMol()) == 'C1COC1') self.assertFalse(rwmol.GetMol().GetAtomWithIdx(0).HasProp("foo")) for rwmol in [Chem.EditableMol(mol), Chem.RWMol(mol)]: newAt = Chem.Atom(8) rwmol.ReplaceAtom(0, newAt, preserveProps=True) self.assertTrue(Chem.MolToSmiles(rwmol.GetMol()) == 'C1COC1') self.assertTrue(rwmol.GetMol().GetAtomWithIdx(0).HasProp("foo")) self.assertEqual(rwmol.GetMol().GetAtomWithIdx(0).GetProp("foo"), "bar") for rwmol in [Chem.EditableMol(mol), Chem.RWMol(mol)]: rwmol.ReplaceBond(0, newBond) self.assertTrue(Chem.MolToSmiles(rwmol.GetMol()) == 'C1CCC1') self.assertFalse(rwmol.GetMol().GetBondWithIdx(0).HasProp("foo")) for rwmol in [Chem.EditableMol(mol), Chem.RWMol(mol)]: rwmol.ReplaceBond(0, newBond, preserveProps=True) self.assertTrue(Chem.MolToSmiles(rwmol.GetMol()) == 'C1CCC1') self.assertTrue(rwmol.GetMol().GetBondWithIdx(0).HasProp("foo")) self.assertEqual(rwmol.GetMol().GetBondWithIdx(0).GetProp("foo"), "bar") def test47SmartsPieces(self): """ test the GetAtomSmarts and GetBondSmarts functions """ m = Chem.MolFromSmarts("[C,N]C") self.assertTrue(m.GetAtomWithIdx(0).GetSmarts() == '[C,N]') self.assertTrue(m.GetAtomWithIdx(1).GetSmarts() == 'C') self.assertEqual(m.GetBondBetweenAtoms(0, 1).GetSmarts(), '') m = Chem.MolFromSmarts("[$(C=O)]-O") self.assertTrue(m.GetAtomWithIdx(0).GetSmarts() == '[$(C=O)]') self.assertTrue(m.GetAtomWithIdx(1).GetSmarts() == 'O') self.assertTrue(m.GetBondBetweenAtoms(0, 1).GetSmarts() == '-') m = Chem.MolFromSmiles("CO") self.assertTrue(m.GetAtomWithIdx(0).GetSmarts() == 'C') self.assertTrue(m.GetAtomWithIdx(1).GetSmarts() == 'O') self.assertTrue(m.GetBondBetweenAtoms(0, 1).GetSmarts() == '') self.assertTrue(m.GetBondBetweenAtoms(0, 1).GetSmarts(allBondsExplicit=True) == '-') m = Chem.MolFromSmiles("C=O") self.assertTrue(m.GetAtomWithIdx(0).GetSmarts() == 'C') self.assertTrue(m.GetAtomWithIdx(1).GetSmarts() == 'O') self.assertTrue(m.GetBondBetweenAtoms(0, 1).GetSmarts() == '=') m = Chem.MolFromSmiles('C[C@H](F)[15NH3+]') self.assertEqual(m.GetAtomWithIdx(0).GetSmarts(), 'C') self.assertEqual(m.GetAtomWithIdx(0).GetSmarts(allHsExplicit=True), '[CH3]') self.assertEqual(m.GetAtomWithIdx(3).GetSmarts(), '[15NH3+]') self.assertEqual(m.GetAtomWithIdx(3).GetSmarts(allHsExplicit=True), '[15NH3+]') def test48Issue1928819(self): """ test a crash involving looping directly over mol suppliers """ fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'NCI_aids_few.sdf') ms = [x for x in Chem.SDMolSupplier(fileN)] self.assertEqual(len(ms), 16) count = 0 for m in Chem.SDMolSupplier(fileN): count += 1 self.assertEqual(count, 16) fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'fewSmi.csv') count = 0 for m in Chem.SmilesMolSupplier(fileN, titleLine=False, smilesColumn=1, delimiter=','): count += 1 self.assertEqual(count, 10) fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'acd_few.tdt') count = 0 for m in Chem.TDTMolSupplier(fileN): count += 1 self.assertEqual(count, 10) def test49Issue1932365(self): """ test aromatic Se and Te from smiles/smarts """ m = Chem.MolFromSmiles('c1ccc[se]1') self.assertTrue(m) self.assertTrue(m.GetAtomWithIdx(0).GetIsAromatic()) self.assertTrue(m.GetAtomWithIdx(4).GetIsAromatic()) m = Chem.MolFromSmiles('c1ccc[te]1') self.assertTrue(m) self.assertTrue(m.GetAtomWithIdx(0).GetIsAromatic()) self.assertTrue(m.GetAtomWithIdx(4).GetIsAromatic()) m = Chem.MolFromSmiles('C1=C[Se]C=C1') self.assertTrue(m) self.assertTrue(m.GetAtomWithIdx(0).GetIsAromatic()) self.assertTrue(m.GetAtomWithIdx(2).GetIsAromatic()) m = Chem.MolFromSmiles('C1=C[Te]C=C1') self.assertTrue(m) self.assertTrue(m.GetAtomWithIdx(0).GetIsAromatic()) self.assertTrue(m.GetAtomWithIdx(2).GetIsAromatic()) p = Chem.MolFromSmarts('[se]') self.assertTrue(Chem.MolFromSmiles('c1ccc[se]1').HasSubstructMatch(p)) self.assertFalse(Chem.MolFromSmiles('C1=CCC[Se]1').HasSubstructMatch(p)) p = Chem.MolFromSmarts('[te]') self.assertTrue(Chem.MolFromSmiles('c1ccc[te]1').HasSubstructMatch(p)) self.assertFalse(Chem.MolFromSmiles('C1=CCC[Te]1').HasSubstructMatch(p)) def test50Issue1968608(self): """ test sf.net issue 1968608 """ smarts = Chem.MolFromSmarts("[r5]") mol = Chem.MolFromSmiles("N12CCC36C1CC(C(C2)=CCOC4CC5=O)C4C3N5c7ccccc76") count = len(mol.GetSubstructMatches(smarts, uniquify=0)) self.assertTrue(count == 9) def test51RadicalHandling(self): """ test handling of atoms with radicals """ mol = Chem.MolFromSmiles("[C]C") self.assertTrue(mol) atom = mol.GetAtomWithIdx(0) self.assertTrue(atom.GetNumRadicalElectrons() == 3) self.assertTrue(atom.GetNoImplicit()) atom.SetNoImplicit(False) atom.SetNumRadicalElectrons(1) mol.UpdatePropertyCache() self.assertTrue(atom.GetNumRadicalElectrons() == 1) self.assertTrue(atom.GetNumImplicitHs() == 2) mol = Chem.MolFromSmiles("[c]1ccccc1") self.assertTrue(mol) atom = mol.GetAtomWithIdx(0) self.assertTrue(atom.GetNumRadicalElectrons() == 1) self.assertTrue(atom.GetNoImplicit()) mol = Chem.MolFromSmiles("[n]1ccccc1") self.assertTrue(mol) atom = mol.GetAtomWithIdx(0) self.assertTrue(atom.GetNumRadicalElectrons() == 0) self.assertTrue(atom.GetNoImplicit()) def test52MolFrags(self): """ test GetMolFrags functionality """ mol = Chem.MolFromSmiles("C.CC") self.assertTrue(mol) fs = Chem.GetMolFrags(mol) self.assertTrue(len(fs) == 2) self.assertTrue(len(fs[0]) == 1) self.assertTrue(tuple(fs[0]) == (0, )) self.assertTrue(len(fs[1]) == 2) self.assertTrue(tuple(fs[1]) == (1, 2)) fs = Chem.GetMolFrags(mol, True) self.assertTrue(len(fs) == 2) self.assertTrue(fs[0].GetNumAtoms() == 1) self.assertTrue(fs[1].GetNumAtoms() == 2) mol = Chem.MolFromSmiles("CCC") self.assertTrue(mol) fs = Chem.GetMolFrags(mol) self.assertTrue(len(fs) == 1) self.assertTrue(len(fs[0]) == 3) self.assertTrue(tuple(fs[0]) == (0, 1, 2)) fs = Chem.GetMolFrags(mol, True) self.assertTrue(len(fs) == 1) self.assertTrue(fs[0].GetNumAtoms() == 3) mol = Chem.MolFromSmiles("CO") em = Chem.EditableMol(mol) em.RemoveBond(0, 1) nm = em.GetMol() fs = Chem.GetMolFrags(nm, asMols=True) self.assertEqual([x.GetNumAtoms(onlyExplicit=False) for x in fs], [5, 3]) fs = Chem.GetMolFrags(nm, asMols=True, sanitizeFrags=False) self.assertEqual([x.GetNumAtoms(onlyExplicit=False) for x in fs], [4, 2]) mol = Chem.MolFromSmiles("CC.CCC") fs = Chem.GetMolFrags(mol, asMols=True) self.assertEqual([x.GetNumAtoms() for x in fs], [2, 3]) frags = [] fragsMolAtomMapping = [] fs = Chem.GetMolFrags(mol, asMols=True, frags=frags, fragsMolAtomMapping=fragsMolAtomMapping) self.assertEqual(mol.GetNumAtoms(onlyExplicit=True), len(frags)) fragsCheck = [] for i, f in enumerate(fs): fragsCheck.extend([i] * f.GetNumAtoms(onlyExplicit=True)) self.assertEqual(frags, fragsCheck) fragsMolAtomMappingCheck = [] i = 0 for f in fs: n = f.GetNumAtoms(onlyExplicit=True) fragsMolAtomMappingCheck.append(tuple(range(i, i + n))) i += n self.assertEqual(fragsMolAtomMapping, fragsMolAtomMappingCheck) def test53Matrices(self): """ test adjacency and distance matrices """ m = Chem.MolFromSmiles('CC=C') d = Chem.GetDistanceMatrix(m, 0) self.assertTrue(feq(d[0, 1], 1.0)) self.assertTrue(feq(d[0, 2], 2.0)) self.assertTrue(feq(d[1, 0], 1.0)) self.assertTrue(feq(d[2, 0], 2.0)) a = Chem.GetAdjacencyMatrix(m, 0) self.assertTrue(a[0, 1] == 1) self.assertTrue(a[0, 2] == 0) self.assertTrue(a[1, 2] == 1) self.assertTrue(a[1, 0] == 1) self.assertTrue(a[2, 0] == 0) m = Chem.MolFromSmiles('C1CC1') d = Chem.GetDistanceMatrix(m, 0) self.assertTrue(feq(d[0, 1], 1.0)) self.assertTrue(feq(d[0, 2], 1.0)) a = Chem.GetAdjacencyMatrix(m, 0) self.assertTrue(a[0, 1] == 1) self.assertTrue(a[0, 2] == 1) self.assertTrue(a[1, 2] == 1) m = Chem.MolFromSmiles('CC.C') d = Chem.GetDistanceMatrix(m, 0) self.assertTrue(feq(d[0, 1], 1.0)) self.assertTrue(d[0, 2] > 1000) self.assertTrue(d[1, 2] > 1000) a = Chem.GetAdjacencyMatrix(m, 0) self.assertTrue(a[0, 1] == 1) self.assertTrue(a[0, 2] == 0) self.assertTrue(a[1, 2] == 0) def test54Mol2Parser(self): """ test the mol2 parser """ fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'pyrazole_pyridine.mol2') m = Chem.MolFromMol2File(fileN) self.assertTrue(m.GetNumAtoms() == 5) self.assertTrue(Chem.MolToSmiles(m) == 'c1cn[nH]c1', Chem.MolToSmiles(m)) fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', '3505.mol2') m = Chem.MolFromMol2File(fileN) self.assertTrue(m.GetBondBetweenAtoms(3, 12) is not None) self.assertEqual(m.GetBondBetweenAtoms(3, 12).GetBondType(), Chem.BondType.SINGLE) self.assertEqual(m.GetAtomWithIdx(12).GetFormalCharge(), 0) m = Chem.MolFromMol2File(fileN, cleanupSubstructures=False) self.assertTrue(m.GetBondBetweenAtoms(3, 12) is not None) self.assertEqual(m.GetBondBetweenAtoms(3, 12).GetBondType(), Chem.BondType.DOUBLE) self.assertEqual(m.GetAtomWithIdx(12).GetFormalCharge(), 1) def test55LayeredFingerprint(self): m1 = Chem.MolFromSmiles('CC(C)C') fp1 = Chem.LayeredFingerprint(m1) self.assertEqual(len(fp1), 2048) atomCounts = [0] * m1.GetNumAtoms() fp2 = Chem.LayeredFingerprint(m1, atomCounts=atomCounts) self.assertEqual(fp1, fp2) self.assertEqual(atomCounts, [4, 7, 4, 4]) fp2 = Chem.LayeredFingerprint(m1, atomCounts=atomCounts) self.assertEqual(fp1, fp2) self.assertEqual(atomCounts, [8, 14, 8, 8]) pbv = DataStructs.ExplicitBitVect(2048) fp3 = Chem.LayeredFingerprint(m1, setOnlyBits=pbv) self.assertEqual(fp3.GetNumOnBits(), 0) fp3 = Chem.LayeredFingerprint(m1, setOnlyBits=fp2) self.assertEqual(fp3, fp2) m2 = Chem.MolFromSmiles('CC') fp4 = Chem.LayeredFingerprint(m2) atomCounts = [0] * m1.GetNumAtoms() fp3 = Chem.LayeredFingerprint(m1, setOnlyBits=fp4, atomCounts=atomCounts) self.assertEqual(atomCounts, [1, 3, 1, 1]) m2 = Chem.MolFromSmiles('CCC') fp4 = Chem.LayeredFingerprint(m2) atomCounts = [0] * m1.GetNumAtoms() fp3 = Chem.LayeredFingerprint(m1, setOnlyBits=fp4, atomCounts=atomCounts) self.assertEqual(atomCounts, [3, 6, 3, 3]) def test56LazySDMolSupplier(self): if not hasattr(Chem, 'CompressedSDMolSupplier'): return self.assertRaises(ValueError, lambda: Chem.CompressedSDMolSupplier('nosuchfile.sdf.gz')) fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'NCI_aids_few.sdf.gz') sdSup = Chem.CompressedSDMolSupplier(fileN) molNames = [ "48", "78", "128", "163", "164", "170", "180", "186", "192", "203", "210", "211", "213", "220", "229", "256" ] chgs192 = {8: 1, 11: 1, 15: -1, 18: -1, 20: 1, 21: 1, 23: -1, 25: -1} i = 0 for mol in sdSup: self.assertTrue(mol) self.assertTrue(mol.GetProp("_Name") == molNames[i]) i += 1 if (mol.GetProp("_Name") == "192"): # test parsed charges on one of the molecules for id in chgs192.keys(): self.assertTrue(mol.GetAtomWithIdx(id).GetFormalCharge() == chgs192[id]) self.assertEqual(i, 16) sdSup = Chem.CompressedSDMolSupplier(fileN) ns = [mol.GetProp("_Name") for mol in sdSup] self.assertTrue(ns == molNames) sdSup = Chem.CompressedSDMolSupplier(fileN, 0) for mol in sdSup: self.assertTrue(not mol.HasProp("numArom")) def test57AddRecursiveQuery(self): q1 = Chem.MolFromSmiles('CC') q2 = Chem.MolFromSmiles('CO') Chem.AddRecursiveQuery(q1, q2, 1) m1 = Chem.MolFromSmiles('OCC') self.assertTrue(m1.HasSubstructMatch(q2)) self.assertTrue(m1.HasSubstructMatch(q1)) self.assertTrue(m1.HasSubstructMatch(q1)) self.assertTrue(m1.GetSubstructMatch(q1) == (2, 1)) q3 = Chem.MolFromSmiles('CS') Chem.AddRecursiveQuery(q1, q3, 1) self.assertFalse(m1.HasSubstructMatch(q3)) self.assertFalse(m1.HasSubstructMatch(q1)) m2 = Chem.MolFromSmiles('OC(S)C') self.assertTrue(m2.HasSubstructMatch(q1)) self.assertTrue(m2.GetSubstructMatch(q1) == (3, 1)) m3 = Chem.MolFromSmiles('SCC') self.assertTrue(m3.HasSubstructMatch(q3)) self.assertFalse(m3.HasSubstructMatch(q1)) q1 = Chem.MolFromSmiles('CC') Chem.AddRecursiveQuery(q1, q2, 1) Chem.AddRecursiveQuery(q1, q3, 1, False) self.assertTrue(m3.HasSubstructMatch(q1)) self.assertTrue(m3.GetSubstructMatch(q1) == (2, 1)) def test58Issue2983794(self): fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'Wrap', 'test_data', 'issue2983794.sdf') m1 = Chem.MolFromMolFile(fileN) self.assertTrue(m1) em = Chem.EditableMol(m1) em.RemoveAtom(0) m2 = em.GetMol() Chem.Kekulize(m2) def test59Issue3007178(self): m = Chem.MolFromSmiles('CCC') a = m.GetAtomWithIdx(0) m = None self.assertEqual(Chem.MolToSmiles(a.GetOwningMol()), 'CCC') a = None m = Chem.MolFromSmiles('CCC') b = m.GetBondWithIdx(0) m = None self.assertEqual(Chem.MolToSmiles(b.GetOwningMol()), 'CCC') def test60SmilesWriterClose(self): fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'fewSmi.csv') smiSup = Chem.SmilesMolSupplier(fileN, delimiter=",", smilesColumn=1, nameColumn=0, titleLine=0) ms = [x for x in smiSup] ofile = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'Wrap', 'test_data', 'outSmiles.txt') writer = Chem.SmilesWriter(ofile) for mol in ms: writer.write(mol) writer.close() newsup = Chem.SmilesMolSupplier(ofile) newms = [x for x in newsup] self.assertEqual(len(ms), len(newms)) def test61PathToSubmol(self): m = Chem.MolFromSmiles('CCCCCC1C(O)CC(O)N1C=CCO') env = Chem.FindAtomEnvironmentOfRadiusN(m, 2, 11) self.assertEqual(len(env), 8) amap = {} submol = Chem.PathToSubmol(m, env, atomMap=amap) self.assertEqual(submol.GetNumAtoms(), len(amap.keys())) self.assertEqual(submol.GetNumAtoms(), 9) smi = Chem.MolToSmiles(submol, rootedAtAtom=amap[11]) self.assertEqual(smi[0], 'N') refsmi = Chem.MolToSmiles(Chem.MolFromSmiles('N(C=C)(C(C)C)C(O)C')) csmi = Chem.MolToSmiles(Chem.MolFromSmiles(smi)) self.assertEqual(refsmi, csmi) def test62SmilesAndSmartsReplacements(self): mol = Chem.MolFromSmiles('C{branch}C', replacements={'{branch}': 'C1(CC1)'}) self.assertEqual(mol.GetNumAtoms(), 5) mol = Chem.MolFromSmarts('C{branch}C', replacements={'{branch}': 'C1(CC1)'}) self.assertEqual(mol.GetNumAtoms(), 5) mol = Chem.MolFromSmiles('C{branch}C{acid}', replacements={ '{branch}': 'C1(CC1)', '{acid}': "C(=O)O" }) self.assertEqual(mol.GetNumAtoms(), 8) def test63Issue3313539(self): fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'rgroups1.mol') m = Chem.MolFromMolFile(fileN) self.assertTrue(m is not None) at = m.GetAtomWithIdx(3) self.assertTrue(at is not None) self.assertTrue(at.HasProp('_MolFileRLabel')) p = at.GetProp('_MolFileRLabel') self.assertEqual(p, '2') self.assertEqual(Chem.GetAtomRLabel(at), 2) at = m.GetAtomWithIdx(4) self.assertTrue(at is not None) self.assertTrue(at.HasProp('_MolFileRLabel')) p = at.GetProp('_MolFileRLabel') self.assertEqual(p, '1') self.assertEqual(Chem.GetAtomRLabel(at), 1) def test64MoleculeCleanup(self): m = Chem.MolFromSmiles('CN(=O)=O', False) self.assertTrue(m) self.assertTrue( m.GetAtomWithIdx(1).GetFormalCharge() == 0 and m.GetAtomWithIdx(2).GetFormalCharge() == 0 and m.GetAtomWithIdx(3).GetFormalCharge() == 0) self.assertTrue( m.GetBondBetweenAtoms(1, 3).GetBondType() == Chem.BondType.DOUBLE and m.GetBondBetweenAtoms(1, 2).GetBondType() == Chem.BondType.DOUBLE) Chem.Cleanup(m) m.UpdatePropertyCache() self.assertTrue( m.GetAtomWithIdx(1).GetFormalCharge() == 1 and (m.GetAtomWithIdx(2).GetFormalCharge() == -1 or m.GetAtomWithIdx(3).GetFormalCharge() == -1)) self.assertTrue( m.GetBondBetweenAtoms(1, 3).GetBondType() == Chem.BondType.SINGLE or m.GetBondBetweenAtoms(1, 2).GetBondType() == Chem.BondType.SINGLE) def test65StreamSupplier(self): fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'NCI_aids_few.sdf.gz') molNames = [ "48", "78", "128", "163", "164", "170", "180", "186", "192", "203", "210", "211", "213", "220", "229", "256" ] inf = gzip.open(fileN) if 0: sb = Chem.streambuf(inf) suppl = Chem.ForwardSDMolSupplier(sb) else: suppl = Chem.ForwardSDMolSupplier(inf) i = 0 while not suppl.atEnd(): mol = next(suppl) self.assertTrue(mol) self.assertTrue(mol.GetProp("_Name") == molNames[i]) i += 1 self.assertEqual(i, 16) # make sure we have object ownership preserved inf = gzip.open(fileN) suppl = Chem.ForwardSDMolSupplier(inf) inf = None i = 0 while not suppl.atEnd(): mol = next(suppl) self.assertTrue(mol) self.assertTrue(mol.GetProp("_Name") == molNames[i]) i += 1 self.assertEqual(i, 16) @unittest.skipIf(not hasattr(Chem, 'MaeMolSupplier'), "not built with MAEParser support") def testMaeStreamSupplier(self): fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'NCI_aids_few.maegz') molNames = [ "48", "78", "128", "163", "164", "170", "180", "186", "192", "203", "210", "211", "213", "220", "229", "256" ] inf = gzip.open(fileN) suppl = Chem.MaeMolSupplier(inf) i = 0 while not suppl.atEnd(): mol = next(suppl) self.assertTrue(mol) self.assertTrue(mol.GetProp("_Name") == molNames[i]) i += 1 self.assertEqual(i, 16) # make sure we have object ownership preserved inf = gzip.open(fileN) suppl = Chem.MaeMolSupplier(inf) inf = None i = 0 while not suppl.atEnd(): mol = next(suppl) self.assertTrue(mol) self.assertTrue(mol.GetProp("_Name") == molNames[i]) i += 1 self.assertEqual(i, 16) @unittest.skipIf(not hasattr(Chem, 'MaeMolSupplier'), "not built with MAEParser support") def testMaeFileSupplier(self): fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'NCI_aids_few.mae') molNames = [ "48", "78", "128", "163", "164", "170", "180", "186", "192", "203", "210", "211", "213", "220", "229", "256" ] suppl = Chem.MaeMolSupplier(fileN) i = 0 while not suppl.atEnd(): mol = next(suppl) self.assertTrue(mol) self.assertTrue(mol.GetProp("_Name") == molNames[i]) i += 1 self.assertEqual(i, 16) @unittest.skipIf(not hasattr(Chem, 'MaeMolSupplier'), "not built with MAEParser support") def testMaeFileSupplierException(self): fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'bad_ppty.mae') err_msg_substr = "Bad format for property" ok = False suppl = Chem.MaeMolSupplier(fileN) for i in range(5): try: mol = next(suppl) except RuntimeError as e: self.assertEqual(i, 1) self.assertTrue(err_msg_substr in str(e)) ok = True break else: self.assertTrue(mol) self.assertTrue(mol.HasProp("_Name")) self.assertTrue(mol.GetNumAtoms() == 1) self.assertFalse(suppl.atEnd()) self.assertTrue(ok) @unittest.skipIf(not hasattr(Chem, 'MaeMolSupplier'), "not built with MAEParser support") def testMaeFileSupplierSetData(self): fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'NCI_aids_few.mae') molNames = [ "48", "78", "128", "163", "164", "170", "180", "186", "192", "203", "210", "211", "213", "220", "229", "256" ] suppl = Chem.MaeMolSupplier() with open(fileN) as f: data = f.read() suppl.SetData(data) self.assertEqual(len(suppl), 16) for i, mol in enumerate(suppl): self.assertTrue(mol) self.assertEqual(mol.GetProp("_Name"), molNames[i]) self.assertEqual(i, 15) # Do it again, to check the reset() method suppl.reset() self.assertEqual(len(suppl), 16) for i, mol in enumerate(suppl): self.assertTrue(mol) self.assertEqual(mol.GetProp("_Name"), molNames[i]) self.assertEqual(i, 15) @unittest.skipIf(not hasattr(Chem, 'MaeMolSupplier'), "not built with MAEParser support") def testMaeFileSupplierGetItem(self): fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'NCI_aids_few.mae') molNames = [ "48", "78", "128", "163", "164", "170", "180", "186", "192", "203", "210", "211", "213", "220", "229", "256" ] with Chem.MaeMolSupplier(fileN) as suppl: num_mols = len(suppl) self.assertEqual(num_mols, 16) for i in range(num_mols): self.assertEqual(suppl[i].GetProp("_Name"), molNames[i]) j = -(i + 1) self.assertEqual(suppl[j].GetProp("_Name"), molNames[j]) for i in (num_mols, 2 * num_mols, -(num_mols + 1), -2 * (num_mols + 1)): self.assertRaises(IndexError, lambda: suppl[i]) @unittest.skipIf(not hasattr(Chem, 'MaeMolSupplier'), "not built with MAEParser support") def testMaeFileSupplierExceptionMsgs(self): maeBlock = "f_m_ct {\n s_m_title\n :::\n " "\n }\n}" with Chem.MaeMolSupplier() as suppl: suppl.SetData(maeBlock) self.assertRaisesRegex(RuntimeError, r'File parsing error: Indexed block not found: m_atom', lambda: next(suppl)) def test66StreamSupplierIter(self): fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'NCI_aids_few.sdf.gz') inf = gzip.open(fileN) if 0: sb = Chem.streambuf(inf) suppl = Chem.ForwardSDMolSupplier(sb) else: suppl = Chem.ForwardSDMolSupplier(inf) molNames = [ "48", "78", "128", "163", "164", "170", "180", "186", "192", "203", "210", "211", "213", "220", "229", "256" ] i = 0 for mol in suppl: self.assertTrue(mol) self.assertTrue(mol.GetProp("_Name") == molNames[i]) i += 1 self.assertEqual(i, 16) def test67StreamSupplierStringIO(self): fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'NCI_aids_few.sdf.gz') sio = BytesIO(gzip.open(fileN).read()) suppl = Chem.ForwardSDMolSupplier(sio) molNames = [ "48", "78", "128", "163", "164", "170", "180", "186", "192", "203", "210", "211", "213", "220", "229", "256" ] i = 0 for mol in suppl: self.assertTrue(mol) self.assertTrue(mol.GetProp("_Name") == molNames[i]) i += 1 self.assertEqual(i, 16) def test68ForwardSupplierUsingFilename(self): fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'NCI_aids_few.sdf') suppl = Chem.ForwardSDMolSupplier(fileN) molNames = [ "48", "78", "128", "163", "164", "170", "180", "186", "192", "203", "210", "211", "213", "220", "229", "256" ] i = 0 for mol in suppl: self.assertTrue(mol) self.assertTrue(mol.GetProp("_Name") == molNames[i]) i += 1 self.assertEqual(i, 16) self.assertRaises(IOError, lambda: Chem.ForwardSDMolSupplier('nosuchfile.sdf')) def test69StreamSupplierStreambuf(self): fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'NCI_aids_few.sdf.gz') sb = rdBase.streambuf(gzip.open(fileN)) suppl = Chem.ForwardSDMolSupplier(sb) molNames = [ "48", "78", "128", "163", "164", "170", "180", "186", "192", "203", "210", "211", "213", "220", "229", "256" ] i = 0 for mol in suppl: self.assertTrue(mol) self.assertTrue(mol.GetProp("_Name") == molNames[i]) i += 1 self.assertEqual(i, 16) def test70StreamSDWriter(self): fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'NCI_aids_few.sdf.gz') inf = gzip.open(fileN) suppl = Chem.ForwardSDMolSupplier(inf) osio = StringIO() w = Chem.SDWriter(osio) molNames = [ "48", "78", "128", "163", "164", "170", "180", "186", "192", "203", "210", "211", "213", "220", "229", "256" ] i = 0 for mol in suppl: self.assertTrue(mol) self.assertTrue(mol.GetProp("_Name") == molNames[i]) w.write(mol) i += 1 self.assertEqual(i, 16) w.flush() w = None txt = osio.getvalue().encode() isio = BytesIO(txt) suppl = Chem.ForwardSDMolSupplier(isio) i = 0 for mol in suppl: self.assertTrue(mol) self.assertTrue(mol.GetProp("_Name") == molNames[i]) i += 1 self.assertEqual(i, 16) def test71StreamSmilesWriter(self): fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'esters.sdf') suppl = Chem.ForwardSDMolSupplier(fileN) osio = StringIO() w = Chem.SmilesWriter(osio) ms = [x for x in suppl] w.SetProps(ms[0].GetPropNames()) i = 0 for mol in ms: self.assertTrue(mol) w.write(mol) i += 1 self.assertEqual(i, 6) w.flush() w = None txt = osio.getvalue() self.assertEqual(txt.count('ID'), 1) self.assertEqual(txt.count('\n'), 7) def test72StreamTDTWriter(self): fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'esters.sdf') suppl = Chem.ForwardSDMolSupplier(fileN) osio = StringIO() w = Chem.TDTWriter(osio) ms = [x for x in suppl] w.SetProps(ms[0].GetPropNames()) i = 0 for mol in ms: self.assertTrue(mol) w.write(mol) i += 1 self.assertEqual(i, 6) w.flush() w = None txt = osio.getvalue() self.assertEqual(txt.count('ID'), 6) self.assertEqual(txt.count('NAME'), 6) def test73SanitizationOptions(self): m = Chem.MolFromSmiles('c1ccccc1', sanitize=False) res = Chem.SanitizeMol(m, catchErrors=True) self.assertEqual(res, 0) m = Chem.MolFromSmiles('c1cccc1', sanitize=False) res = Chem.SanitizeMol(m, catchErrors=True) self.assertEqual(res, Chem.SanitizeFlags.SANITIZE_KEKULIZE) m = Chem.MolFromSmiles('CC(C)(C)(C)C', sanitize=False) res = Chem.SanitizeMol(m, catchErrors=True) self.assertEqual(res, Chem.SanitizeFlags.SANITIZE_PROPERTIES) m = Chem.MolFromSmiles('c1cccc1', sanitize=False) res = Chem.SanitizeMol( m, sanitizeOps=Chem.SanitizeFlags.SANITIZE_ALL ^ Chem.SanitizeFlags.SANITIZE_KEKULIZE, catchErrors=True) self.assertEqual(res, Chem.SanitizeFlags.SANITIZE_NONE) def test74Issue3510149(self): mol = Chem.MolFromSmiles("CCC1CNCC1CC") atoms = mol.GetAtoms() mol = None for atom in atoms: idx = atom.GetIdx() p = atom.GetOwningMol().GetNumAtoms() mol = Chem.MolFromSmiles("CCC1CNCC1CC") bonds = mol.GetBonds() mol = None for bond in bonds: idx = bond.GetIdx() p = atom.GetOwningMol().GetNumAtoms() mol = Chem.MolFromSmiles("CCC1CNCC1CC") bond = mol.GetBondBetweenAtoms(0, 1) mol = None idx = bond.GetBeginAtomIdx() p = bond.GetOwningMol().GetNumAtoms() fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'NCI_aids_few.sdf') sdSup = Chem.SDMolSupplier(fileN) mol = next(sdSup) nats = mol.GetNumAtoms() conf = mol.GetConformer() mol = None self.assertEqual(nats, conf.GetNumAtoms()) conf.GetOwningMol().GetProp("_Name") def test75AllBondsExplicit(self): m = Chem.MolFromSmiles("CCC") smi = Chem.MolToSmiles(m) self.assertEqual(smi, "CCC") smi = Chem.MolToSmiles(m, allBondsExplicit=True) self.assertEqual(smi, "C-C-C") m = Chem.MolFromSmiles("c1ccccc1") smi = Chem.MolToSmiles(m) self.assertEqual(smi, "c1ccccc1") smi = Chem.MolToSmiles(m, allBondsExplicit=True) self.assertEqual(smi, "c1:c:c:c:c:c:1") def test76VeryLargeMolecule(self): # this is sf.net issue 3524984 smi = '[C@H](F)(Cl)' + 'c1cc[nH]c1' * 500 + '[C@H](F)(Cl)' m = Chem.MolFromSmiles(smi) self.assertTrue(m) self.assertEqual(m.GetNumAtoms(), 2506) scs = Chem.FindMolChiralCenters(m) self.assertEqual(len(scs), 2) def test77MolFragmentToSmiles(self): smi = "OC1CC1CC" m = Chem.MolFromSmiles(smi) fsmi = Chem.MolFragmentToSmiles(m, [1, 2, 3]) self.assertEqual(fsmi, "C1CC1") fsmi = Chem.MolFragmentToSmiles(m, [1, 2, 3], bondsToUse=[1, 2, 5]) self.assertEqual(fsmi, "C1CC1") fsmi = Chem.MolFragmentToSmiles(m, [1, 2, 3], bondsToUse=[1, 2]) self.assertEqual(fsmi, "CCC") fsmi = Chem.MolFragmentToSmiles(m, [1, 2, 3], atomSymbols=["", "[A]", "[C]", "[B]", "", ""]) self.assertEqual(fsmi, "[A]1[B][C]1") fsmi = Chem.MolFragmentToSmiles(m, [1, 2, 3], bondSymbols=["", "%", "%", "", "", "%"]) self.assertEqual(fsmi, "C1%C%C%1") smi = "c1ccccc1C" m = Chem.MolFromSmiles(smi) fsmi = Chem.MolFragmentToSmiles(m, range(6)) self.assertEqual(fsmi, "c1ccccc1") Chem.Kekulize(m) fsmi = Chem.MolFragmentToSmiles(m, range(6), kekuleSmiles=True) self.assertEqual(fsmi, "C1=CC=CC=C1") fsmi = Chem.MolFragmentToSmiles(m, range(6), atomSymbols=["[C]"] * 7, kekuleSmiles=True) self.assertEqual(fsmi, "[C]1=[C][C]=[C][C]=[C]1") self.assertRaises(ValueError, lambda: Chem.MolFragmentToSmiles(m, [])) def test78AtomAndBondProps(self): m = Chem.MolFromSmiles('c1ccccc1') at = m.GetAtomWithIdx(0) self.assertFalse(at.HasProp('foo')) at.SetProp('foo', 'bar') self.assertTrue(at.HasProp('foo')) self.assertEqual(at.GetProp('foo'), 'bar') bond = m.GetBondWithIdx(0) self.assertFalse(bond.HasProp('foo')) bond.SetProp('foo', 'bar') self.assertTrue(bond.HasProp('foo')) self.assertEqual(bond.GetProp('foo'), 'bar') def test79AddRecursiveStructureQueries(self): qs = {'carbonyl': Chem.MolFromSmiles('CO'), 'amine': Chem.MolFromSmiles('CN')} q = Chem.MolFromSmiles('CCC') q.GetAtomWithIdx(0).SetProp('query', 'carbonyl,amine') Chem.MolAddRecursiveQueries(q, qs, 'query') m = Chem.MolFromSmiles('CCCO') self.assertTrue(m.HasSubstructMatch(q)) m = Chem.MolFromSmiles('CCCN') self.assertTrue(m.HasSubstructMatch(q)) m = Chem.MolFromSmiles('CCCC') self.assertFalse(m.HasSubstructMatch(q)) def test80ParseMolQueryDefFile(self): fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'ChemTransforms', 'testData', 'query_file1.txt') d = Chem.ParseMolQueryDefFile(fileN, standardize=False) self.assertTrue('CarboxylicAcid' in d) m = Chem.MolFromSmiles('CC(=O)O') self.assertTrue(m.HasSubstructMatch(d['CarboxylicAcid'])) self.assertFalse(m.HasSubstructMatch(d['CarboxylicAcid.Aromatic'])) d = Chem.ParseMolQueryDefFile(fileN) self.assertTrue('carboxylicacid' in d) self.assertFalse('CarboxylicAcid' in d) def test81Issue275(self): smi = Chem.MolToSmiles(Chem.MurckoDecompose( Chem.MolFromSmiles('CCCCC[C@H]1CC[C@H](C(=O)O)CC1'))) self.assertEqual(smi, 'C1CCCCC1') def test82Issue288(self): m = Chem.MolFromSmiles('CC*') m.GetAtomWithIdx(2).SetProp('molAtomMapNumber', '30') smi = Chem.MolToSmiles(m) self.assertEqual(smi, 'CC[*:30]') # try newer api m = Chem.MolFromSmiles('CC*') m.GetAtomWithIdx(2).SetAtomMapNum(30) smi = Chem.MolToSmiles(m) self.assertEqual(smi, 'CC[*:30]') def test83GitHubIssue19(self): fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'empty2.sdf') with self.assertRaises(OSError): sdSup = Chem.SDMolSupplier(fileN) sdSup = Chem.SDMolSupplier() sdSup.SetData('') self.assertTrue(sdSup.atEnd()) self.assertRaises(IndexError, lambda: sdSup[0]) sdSup.SetData('') self.assertRaises(IndexError, lambda: sdSup[0]) self.assertEqual(len(sdSup), 0) def test84PDBBasics(self): fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', '1CRN.pdb') m = Chem.MolFromPDBFile(fileN, proximityBonding=False) self.assertEqual(m.GetNumAtoms(), 327) self.assertEqual(m.GetNumBonds(), 3) m = Chem.MolFromPDBFile(fileN) self.assertTrue(m is not None) self.assertEqual(m.GetNumAtoms(), 327) self.assertEqual(m.GetNumBonds(), 337) self.assertTrue(m.GetAtomWithIdx(0).GetPDBResidueInfo()) self.assertEqual(m.GetAtomWithIdx(0).GetPDBResidueInfo().GetName(), " N ") self.assertEqual(m.GetAtomWithIdx(0).GetPDBResidueInfo().GetResidueName(), "THR") self.assertAlmostEqual(m.GetAtomWithIdx(0).GetPDBResidueInfo().GetTempFactor(), 13.79, 2) m = Chem.MolFromPDBBlock(Chem.MolToPDBBlock(m)) self.assertEqual(m.GetNumAtoms(), 327) self.assertEqual(m.GetNumBonds(), 337) self.assertTrue(m.GetAtomWithIdx(0).GetPDBResidueInfo()) self.assertEqual(m.GetAtomWithIdx(0).GetPDBResidueInfo().GetName(), " N ") self.assertEqual(m.GetAtomWithIdx(0).GetPDBResidueInfo().GetResidueName(), "THR") self.assertAlmostEqual(m.GetAtomWithIdx(0).GetPDBResidueInfo().GetTempFactor(), 13.79, 2) # test multivalent Hs fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', '2c92_hypervalentH.pdb') mol = Chem.MolFromPDBFile(fileN, sanitize=False, removeHs=False) atom = mol.GetAtomWithIdx(84) self.assertEqual(atom.GetAtomicNum(), 1) # is it H self.assertEqual(atom.GetDegree(), 1) # H should have 1 bond for n in atom.GetNeighbors(): # Check if neighbor is from the same residue self.assertEqual(atom.GetPDBResidueInfo().GetResidueName(), n.GetPDBResidueInfo().GetResidueName()) # test unbinding metals (ZN) fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', '1ps3_zn.pdb') mol = Chem.MolFromPDBFile(fileN, sanitize=False, removeHs=False) atom = mol.GetAtomWithIdx(40) self.assertEqual(atom.GetAtomicNum(), 30) # is it Zn self.assertEqual(atom.GetDegree(), 4) # Zn should have 4 zero-order bonds self.assertEqual(atom.GetValence(which=Chem.ValenceType.EXPLICIT), 0) bonds_order = [bond.GetBondType() for bond in atom.GetBonds()] self.assertEqual(bonds_order, [Chem.BondType.ZERO] * atom.GetDegree()) # test metal bonds without proximity bonding mol = Chem.MolFromPDBFile(fileN, sanitize=False, removeHs=False, proximityBonding=False) atom = mol.GetAtomWithIdx(40) self.assertEqual(atom.GetAtomicNum(), 30) # is it Zn self.assertEqual(atom.GetDegree(), 4) # Zn should have 4 zero-order bonds self.assertEqual(atom.GetValence(which=Chem.ValenceType.EXPLICIT), 0) bonds_order = [bond.GetBondType() for bond in atom.GetBonds()] self.assertEqual(bonds_order, [Chem.BondType.ZERO] * atom.GetDegree()) # test unbinding HOHs fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', '2vnf_bindedHOH.pdb') mol = Chem.MolFromPDBFile(fileN, sanitize=False, removeHs=False) atom = mol.GetAtomWithIdx(10) self.assertEqual(atom.GetPDBResidueInfo().GetResidueName(), 'HOH') self.assertEqual(atom.GetDegree(), 0) # HOH should have no bonds # test metal bonding in ligand fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', '2dej_APW.pdb') mol = Chem.MolFromPDBFile(fileN, sanitize=False, removeHs=False) atom = mol.GetAtomWithIdx(6) self.assertEqual(atom.GetAtomicNum(), 12) self.assertEqual(atom.GetDegree(), 2) atom = mol.GetAtomWithIdx(35) self.assertEqual(atom.GetPDBResidueInfo().GetResidueName(), 'HOH') self.assertEqual(atom.GetDegree(), 0) def testAtomMonomerInfo(self): m = Chem.MolFromSmiles('CCO') info = Chem.AtomMonomerInfo() info.SetChainId('A') info.SetResidueName('FOO') info.SetResidueNumber(12) info.SetMonomerClass('LINK') m.GetAtomWithIdx(1).SetMonomerInfo(info) info2 = m.GetAtomWithIdx(1).GetMonomerInfo() self.assertEqual(info2.GetChainId(), 'A') self.assertEqual(info2.GetResidueName(), 'FOO') self.assertEqual(info2.GetResidueNumber(), 12) self.assertEqual(info2.GetMonomerClass(), 'LINK') # test that this can be cast into the PDBResidueInfo subclass info.SetMonomerType(Chem.AtomMonomerType.PDBRESIDUE) m.GetAtomWithIdx(1).SetMonomerInfo(info) pdb_res_info = m.GetAtomWithIdx(1).GetPDBResidueInfo() self.assertIsNotNone(pdb_res_info) self.assertEqual(pdb_res_info.GetChainId(), 'A') self.assertEqual(pdb_res_info.GetResidueName(), 'FOO') self.assertEqual(pdb_res_info.GetResidueNumber(), 12) self.assertEqual(pdb_res_info.GetMonomerClass(), 'LINK') def test85AtomCopying(self): """Can a copied atom be added to a molecule?""" import copy m = Chem.MolFromSmiles('C1CC1') a = m.GetAtomWithIdx(0) a_copy1 = copy.copy(a) a_copy2 = Chem.Atom(a) m = None a = None def assert_is_valid_atom(a): new_m = Chem.RWMol() new_m.AddAtom(a) # This will not match if the owning mol is unset for a_copy, # or if there has been a clean up. self.assertEqual(new_m.GetAtomWithIdx(0).GetIdx(), 0) assert_is_valid_atom(a_copy1) assert_is_valid_atom(a_copy2) def test85MolCopying(self): m = Chem.MolFromSmiles('C1CC1[C@H](F)Cl') m.SetProp('foo', 'bar') m2 = Chem.Mol(m) self.assertEqual(Chem.MolToSmiles(m, True), Chem.MolToSmiles(m2, True)) self.assertTrue(m2.HasProp('foo')) self.assertEqual(m2.GetProp('foo'), 'bar') ri = m2.GetRingInfo() self.assertTrue(ri) self.assertEqual(ri.NumRings(), 1) def test85MolCopying2(self): import copy m1 = Chem.MolFromSmiles('CC') m1.SetProp('Foo', 'bar') m1.foo = [1] m2 = copy.copy(m1) m3 = copy.copy(m2) m4 = copy.deepcopy(m1) m5 = copy.deepcopy(m2) m6 = copy.deepcopy(m4) self.assertEqual(m1.GetProp('Foo'), 'bar') self.assertEqual(m2.GetProp('Foo'), 'bar') self.assertEqual(m3.GetProp('Foo'), 'bar') self.assertEqual(m4.GetProp('Foo'), 'bar') self.assertEqual(m5.GetProp('Foo'), 'bar') self.assertEqual(m6.GetProp('Foo'), 'bar') m2.foo.append(4) self.assertEqual(m1.foo, [1, 4]) self.assertEqual(m2.foo, [1, 4]) self.assertEqual(m3.foo, [1, 4]) self.assertEqual(m4.foo, [1]) self.assertEqual(m5.foo, [1]) self.assertEqual(m6.foo, [1]) m7 = Chem.RWMol(m1) self.assertFalse(hasattr(m7, 'foo')) m7.foo = [1] m8 = copy.copy(m7) m9 = copy.deepcopy(m7) m8.foo.append(4) self.assertEqual(m7.GetProp('Foo'), 'bar') self.assertEqual(m8.GetProp('Foo'), 'bar') self.assertEqual(m9.GetProp('Foo'), 'bar') self.assertEqual(m8.foo, [1, 4]) self.assertEqual(m9.foo, [1]) def test86MolRenumbering(self): import random m = Chem.MolFromSmiles('C[C@H]1CC[C@H](C/C=C/[C@H](F)Cl)CC1') cSmi = Chem.MolToSmiles(m, True) for i in range(m.GetNumAtoms()): ans = list(range(m.GetNumAtoms())) random.shuffle(ans) m2 = Chem.RenumberAtoms(m, ans) nSmi = Chem.MolToSmiles(m2, True) self.assertEqual(cSmi, nSmi) def test87FragmentOnBonds(self): m = Chem.MolFromSmiles('CC1CC(O)C1CCC1CC1') bis = m.GetSubstructMatches(Chem.MolFromSmarts('[!R][R]')) bs = [] labels = [] for bi in bis: b = m.GetBondBetweenAtoms(bi[0], bi[1]) if b.GetBeginAtomIdx() == bi[0]: labels.append((10, 1)) else: labels.append((1, 10)) bs.append(b.GetIdx()) nm = Chem.FragmentOnBonds(m, bs) frags = Chem.GetMolFrags(nm) self.assertEqual(len(frags), 5) self.assertEqual(frags, ((0, 12), (1, 2, 3, 5, 11, 14, 16), (4, 13), (6, 7, 15, 18), (8, 9, 10, 17))) smi = Chem.MolToSmiles(nm, True) self.assertEqual(smi, '*C1CC([4*])C1[6*].[1*]C.[3*]O.[5*]CC[8*].[7*]C1CC1') nm = Chem.FragmentOnBonds(m, bs, dummyLabels=labels) frags = Chem.GetMolFrags(nm) self.assertEqual(len(frags), 5) self.assertEqual(frags, ((0, 12), (1, 2, 3, 5, 11, 14, 16), (4, 13), (6, 7, 15, 18), (8, 9, 10, 17))) smi = Chem.MolToSmiles(nm, True) self.assertEqual(smi, '[1*]C.[1*]CC[1*].[1*]O.[10*]C1CC([10*])C1[10*].[10*]C1CC1') m = Chem.MolFromSmiles('CCC(=O)CC(=O)C') bis = m.GetSubstructMatches(Chem.MolFromSmarts('C=O')) bs = [] for bi in bis: b = m.GetBondBetweenAtoms(bi[0], bi[1]) bs.append(b.GetIdx()) bts = [Chem.BondType.DOUBLE] * len(bs) nm = Chem.FragmentOnBonds(m, bs, bondTypes=bts) frags = Chem.GetMolFrags(nm) self.assertEqual(len(frags), 3) smi = Chem.MolToSmiles(nm, True) self.assertEqual(smi, '[2*]=O.[3*]=C(CC)CC(=[6*])C.[5*]=O') # github issue 430: m = Chem.MolFromSmiles('OCCCCN') self.assertRaises(ValueError, lambda: Chem.FragmentOnBonds(m, ())) def test88QueryAtoms(self): from rdkit.Chem import rdqueries m = Chem.MolFromSmiles('c1nc(C)n(CC)c1') qa = rdqueries.ExplicitDegreeEqualsQueryAtom(3) l = tuple([x.GetIdx() for x in m.GetAtomsMatchingQuery(qa)]) self.assertEqual(l, (2, 4)) qa.ExpandQuery(rdqueries.AtomNumEqualsQueryAtom(6, negate=True)) l = tuple([x.GetIdx() for x in m.GetAtomsMatchingQuery(qa)]) self.assertEqual(l, (4, )) qa = rdqueries.ExplicitDegreeEqualsQueryAtom(3) qa.ExpandQuery(rdqueries.AtomNumEqualsQueryAtom(6, negate=True), how=Chem.CompositeQueryType.COMPOSITE_OR) l = tuple([x.GetIdx() for x in m.GetAtomsMatchingQuery(qa)]) self.assertEqual(l, (1, 2, 4)) qa = rdqueries.ExplicitDegreeEqualsQueryAtom(3) qa.ExpandQuery(rdqueries.AtomNumEqualsQueryAtom(6, negate=True), how=Chem.CompositeQueryType.COMPOSITE_XOR) l = tuple([x.GetIdx() for x in m.GetAtomsMatchingQuery(qa)]) self.assertEqual(l, (1, 2)) qa = rdqueries.ExplicitDegreeGreaterQueryAtom(2) l = tuple([x.GetIdx() for x in m.GetAtomsMatchingQuery(qa)]) self.assertEqual(l, (2, 4)) qa = rdqueries.ExplicitDegreeLessQueryAtom(2) l = tuple([x.GetIdx() for x in m.GetAtomsMatchingQuery(qa)]) self.assertEqual(l, (3, 6)) m = Chem.MolFromSmiles('N[CH][CH]') qa = rdqueries.NumRadicalElectronsGreaterQueryAtom(0) l = tuple([x.GetIdx() for x in m.GetAtomsMatchingQuery(qa)]) self.assertEqual(l, (1, 2)) qa = rdqueries.NumRadicalElectronsGreaterQueryAtom(1) l = tuple([x.GetIdx() for x in m.GetAtomsMatchingQuery(qa)]) self.assertEqual(l, (2, )) m = Chem.MolFromSmiles('F[C@H](Cl)C') qa = rdqueries.HasChiralTagQueryAtom() l = tuple([x.GetIdx() for x in m.GetAtomsMatchingQuery(qa)]) self.assertEqual(l, (1, )) qa = rdqueries.MissingChiralTagQueryAtom() l = tuple([x.GetIdx() for x in m.GetAtomsMatchingQuery(qa)]) self.assertEqual(l, ()) m = Chem.MolFromSmiles('F[CH](Cl)C') qa = rdqueries.HasChiralTagQueryAtom() l = tuple([x.GetIdx() for x in m.GetAtomsMatchingQuery(qa)]) self.assertEqual(l, ()) qa = rdqueries.MissingChiralTagQueryAtom() l = tuple([x.GetIdx() for x in m.GetAtomsMatchingQuery(qa)]) self.assertEqual(l, (1, )) m = Chem.MolFromSmiles('CNCON') qa = rdqueries.NumHeteroatomNeighborsEqualsQueryAtom(2) l = tuple([x.GetIdx() for x in m.GetAtomsMatchingQuery(qa)]) self.assertEqual(l, (2, )) qa = rdqueries.NumHeteroatomNeighborsGreaterQueryAtom(0) l = tuple([x.GetIdx() for x in m.GetAtomsMatchingQuery(qa)]) self.assertEqual(l, (0, 2, 3, 4)) m = Chem.MolFromSmiles('CC12CCN(CC1)C2') qa = rdqueries.IsBridgeheadQueryAtom() l = tuple([x.GetIdx() for x in m.GetAtomsMatchingQuery(qa)]) self.assertEqual(l, (1, 4)) m = Chem.MolFromSmiles('OCCOC') qa = rdqueries.NonHydrogenDegreeEqualsQueryAtom(2) l = tuple([x.GetIdx() for x in m.GetAtomsMatchingQuery(qa)]) self.assertEqual(l, (1, 2, 3)) def test89UnicodeInput(self): m = Chem.MolFromSmiles(u'c1ccccc1') self.assertTrue(m is not None) self.assertEqual(m.GetNumAtoms(), 6) m = Chem.MolFromSmarts(u'c1ccccc1') self.assertTrue(m is not None) self.assertEqual(m.GetNumAtoms(), 6) def test90FragmentOnSomeBonds(self): m = Chem.MolFromSmiles('OCCCCN') pieces = Chem.FragmentOnSomeBonds(m, (0, 2, 4), 2) self.assertEqual(len(pieces), 3) frags = Chem.GetMolFrags(pieces[0]) self.assertEqual(len(frags), 3) self.assertEqual(len(frags[0]), 2) self.assertEqual(len(frags[1]), 4) self.assertEqual(len(frags[2]), 4) frags = Chem.GetMolFrags(pieces[1]) self.assertEqual(len(frags), 3) self.assertEqual(len(frags[0]), 2) self.assertEqual(len(frags[1]), 6) self.assertEqual(len(frags[2]), 2) frags = Chem.GetMolFrags(pieces[2]) self.assertEqual(len(frags), 3) self.assertEqual(len(frags[0]), 4) self.assertEqual(len(frags[1]), 4) self.assertEqual(len(frags[2]), 2) pieces, cpa = Chem.FragmentOnSomeBonds(m, (0, 2, 4), 2, returnCutsPerAtom=True) self.assertEqual(len(pieces), 3) self.assertEqual(len(cpa), 3) self.assertEqual(len(cpa[0]), m.GetNumAtoms()) # github issue 430: m = Chem.MolFromSmiles('OCCCCN') self.assertRaises(ValueError, lambda: Chem.FragmentOnSomeBonds(m, ())) pieces = Chem.FragmentOnSomeBonds(m, (0, 2, 4), 0) self.assertEqual(len(pieces), 0) def test91RankAtoms(self): m = Chem.MolFromSmiles('ONCS.ONCS') ranks = Chem.CanonicalRankAtoms(m, breakTies=False) self.assertEqual(list(ranks[0:4]), list(ranks[4:])) m = Chem.MolFromSmiles("c1ccccc1") ranks = Chem.CanonicalRankAtoms(m, breakTies=False) for x in ranks: self.assertEqual(x, 0) m = Chem.MolFromSmiles("C1NCN1") ranks = Chem.CanonicalRankAtoms(m, breakTies=False) self.assertEqual(ranks[0], ranks[2]) self.assertEqual(ranks[1], ranks[3]) def test92RankAtomsInFragment(self): m = Chem.MolFromSmiles('ONCS.ONCS') ranks = Chem.CanonicalRankAtomsInFragment(m, [0, 1, 2, 3], [0, 1, 2]) ranks2 = Chem.CanonicalRankAtomsInFragment(m, [4, 5, 6, 7], [3, 4, 5]) self.assertEqual(list(ranks[0:4]), list(ranks2[4:])) self.assertEqual(list(ranks[4:]), [-1] * 4) self.assertEqual(list(ranks2[0:4]), [-1] * 4) # doc tests mol = Chem.MolFromSmiles('C1NCN1.C1NCN1') self.assertEqual( list(Chem.CanonicalRankAtomsInFragment(mol, atomsToUse=range(0, 4), breakTies=False)), [4, 6, 4, 6, -1, -1, -1, -1]) self.assertNotEqual( list(Chem.CanonicalRankAtomsInFragment(mol, atomsToUse=range(0, 4), breakTies=True)), [4, 6, 4, 6, -1, -1, -1, -1]) self.assertEqual( list(Chem.CanonicalRankAtomsInFragment(mol, atomsToUse=range(4, 8), breakTies=False)), [-1, -1, -1, -1, 4, 6, 4, 6]) self.assertNotEqual( list(Chem.CanonicalRankAtomsInFragment(mol, atomsToUse=range(4, 8), breakTies=True)), [-1, -1, -1, -1, 4, 6, 4, 6]) def test93RWMolsAsROMol(self): """ test the RWMol class as a proper ROMol """ mol = Chem.MolFromSmiles('C1CCC1') self.assertTrue(type(mol) == Chem.Mol) rwmol = Chem.RWMol(mol) self.assertEqual(Chem.MolToSmiles(rwmol, True), Chem.MolToSmiles(rwmol.GetMol())) newAt = Chem.Atom(8) rwmol.ReplaceAtom(0, newAt) self.assertEqual(Chem.MolToSmiles(rwmol, True), Chem.MolToSmiles(rwmol.GetMol())) def test94CopyWithConfs(self): """ test copying Mols with some conformers """ fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'cmpd2.tpl') m1 = Chem.MolFromTPLFile(fileN) self.assertTrue(m1 is not None) self.assertEqual(m1.GetNumAtoms(), 12) self.assertEqual(m1.GetNumConformers(), 2) self.assertEqual(m1.GetConformer(0).GetNumAtoms(), 12) self.assertEqual(m1.GetConformer(1).GetNumAtoms(), 12) m2 = Chem.Mol(m1) self.assertEqual(m2.GetNumAtoms(), 12) self.assertEqual(m2.GetNumConformers(), 2) self.assertEqual(m2.GetConformer(0).GetNumAtoms(), 12) self.assertEqual(m2.GetConformer(1).GetNumAtoms(), 12) m2 = Chem.Mol(m1, False, 0) self.assertEqual(m2.GetNumAtoms(), 12) self.assertEqual(m2.GetNumConformers(), 1) self.assertEqual(m2.GetConformer(0).GetNumAtoms(), 12) m2 = Chem.Mol(m1, False, 1) self.assertEqual(m2.GetNumAtoms(), 12) self.assertEqual(m2.GetNumConformers(), 1) self.assertEqual(m2.GetConformer(1).GetNumAtoms(), 12) m2 = Chem.Mol(m1, True) self.assertTrue(m2.GetNumAtoms() == 12) self.assertTrue(m2.GetNumConformers() == 0) m2 = Chem.RWMol(m1) self.assertEqual(m2.GetNumAtoms(), 12) self.assertEqual(m2.GetNumConformers(), 2) self.assertEqual(m2.GetConformer(0).GetNumAtoms(), 12) self.assertEqual(m2.GetConformer(1).GetNumAtoms(), 12) m2 = Chem.RWMol(m1, False, 0) self.assertEqual(m2.GetNumAtoms(), 12) self.assertEqual(m2.GetNumConformers(), 1) self.assertEqual(m2.GetConformer(0).GetNumAtoms(), 12) m2 = Chem.RWMol(m1, False, 1) self.assertEqual(m2.GetNumAtoms(), 12) self.assertEqual(m2.GetNumConformers(), 1) self.assertEqual(m2.GetConformer(1).GetNumAtoms(), 12) m2 = Chem.RWMol(m1, True) self.assertTrue(m2.GetNumAtoms() == 12) self.assertTrue(m2.GetNumConformers() == 0) def testAtomPropQueries(self): """ test the property queries """ from rdkit.Chem import rdqueries m = Chem.MolFromSmiles("C" * 14) atoms = m.GetAtoms() atoms[0].SetProp("hah", "hah") atoms[1].SetIntProp("bar", 1) atoms[2].SetIntProp("bar", 2) atoms[3].SetBoolProp("baz", True) atoms[4].SetBoolProp("baz", False) atoms[5].SetProp("boo", "hoo") atoms[6].SetProp("boo", "-urns") atoms[7].SetDoubleProp("boot", 1.0) atoms[8].SetDoubleProp("boot", 4.0) atoms[9].SetDoubleProp("number", 4.0) atoms[10].SetIntProp("number", 4) tests = ((rdqueries.HasIntPropWithValueQueryAtom, "bar", { 1: [1], 2: [2] }), (rdqueries.HasBoolPropWithValueQueryAtom, "baz", { True: [3], False: [4] }), (rdqueries.HasStringPropWithValueQueryAtom, "boo", { "hoo": [5], "-urns": [6] }), (rdqueries.HasDoublePropWithValueQueryAtom, "boot", { 1.0: [7], 4.0: [8] })) for query, name, lookups in tests: for t, v in lookups.items(): q = query(name, t) self.assertEqual(v, [x.GetIdx() for x in m.GetAtomsMatchingQuery(q)]) q = query(name, t, negate=True) self.assertEqual(sorted(set(range(14)) - set(v)), [x.GetIdx() for x in m.GetAtomsMatchingQuery(q)]) # check tolerances self.assertEqual([ x.GetIdx() for x in m.GetAtomsMatchingQuery( rdqueries.HasDoublePropWithValueQueryAtom("boot", 1.0, tolerance=3.)) ], [7, 8]) # numbers are numbers?, i.e. int!=double self.assertEqual([ x.GetIdx() for x in m.GetAtomsMatchingQuery(rdqueries.HasIntPropWithValueQueryAtom("number", 4)) ], [10]) def testBondPropQueries(self): """ test the property queries """ from rdkit.Chem import rdqueries m = Chem.MolFromSmiles("C" * 14) bonds = m.GetBonds() bonds[0].SetProp("hah", "hah") bonds[1].SetIntProp("bar", 1) bonds[2].SetIntProp("bar", 2) bonds[3].SetBoolProp("baz", True) bonds[4].SetBoolProp("baz", False) bonds[5].SetProp("boo", "hoo") bonds[6].SetProp("boo", "-urns") bonds[7].SetDoubleProp("boot", 1.0) bonds[8].SetDoubleProp("boot", 4.0) bonds[9].SetDoubleProp("number", 4.0) bonds[10].SetIntProp("number", 4) tests = ((rdqueries.HasIntPropWithValueQueryBond, "bar", { 1: [1], 2: [2] }), (rdqueries.HasBoolPropWithValueQueryBond, "baz", { True: [3], False: [4] }), (rdqueries.HasStringPropWithValueQueryBond, "boo", { "hoo": [5], "-urns": [6] }), (rdqueries.HasDoublePropWithValueQueryBond, "boot", { 1.0: [7], 4.0: [8] })) for query, name, lookups in tests: for t, v in lookups.items(): q = query(name, t) self.assertEqual(v, [x.GetIdx() for x in m.GetBonds() if q.Match(x)]) q = query(name, t, negate=True) self.assertEqual(sorted(set(range(13)) - set(v)), [x.GetIdx() for x in m.GetBonds() if q.Match(x)]) # check tolerances q = rdqueries.HasDoublePropWithValueQueryBond("boot", 1.0, tolerance=3.) self.assertEqual([x.GetIdx() for x in m.GetBonds() if q.Match(x)], [7, 8]) # numbers are numbers?, i.e. int!=double q = rdqueries.HasIntPropWithValueQueryBond("number", 4) self.assertEqual([x.GetIdx() for x in m.GetBonds() if q.Match(x)], [10]) def testGetShortestPath(self): """ test the GetShortestPath() wrapper """ smi = "CC(OC1C(CCCC3)C3C(CCCC2)C2C1OC(C)=O)=O" m = Chem.MolFromSmiles(smi) path = Chem.GetShortestPath(m, 1, 20) self.assertEqual(path, (1, 2, 3, 16, 17, 18, 20)) def testGithub497(self): with tempfile.TemporaryFile() as tmp, gzip.open(tmp) as outf: with self.assertRaises(ValueError): w = Chem.SDWriter(outf) def testGithub498(self): if (sys.version_info < (3, 0)): mode = 'w+' else: mode = 'wt+' m = Chem.MolFromSmiles('C') with tempfile.NamedTemporaryFile() as tmp, gzip.open(tmp, mode) as outf: w = Chem.SDWriter(outf) w.write(m) w.close() def testReplaceBond(self): origmol = Chem.RWMol(Chem.MolFromSmiles("CC")) bonds = list(origmol.GetBonds()) self.assertEqual(len(bonds), 1) singlebond = bonds[0] self.assertEqual(singlebond.GetBondType(), Chem.BondType.SINGLE) # this is the only way we create a bond, is take it from another molecule doublebonded = Chem.MolFromSmiles("C=C") doublebond = list(doublebonded.GetBonds())[0] # make sure replacing the bond changes the smiles self.assertEqual(Chem.MolToSmiles(origmol), "CC") origmol.ReplaceBond(singlebond.GetIdx(), doublebond) Chem.SanitizeMol(origmol) self.assertEqual(Chem.MolToSmiles(origmol), "C=C") def testAdjustQueryProperties(self): m = Chem.MolFromSmarts('C1CCC1*') am = Chem.AdjustQueryProperties(m) self.assertTrue(Chem.MolFromSmiles('C1CCC1C').HasSubstructMatch(m)) self.assertTrue(Chem.MolFromSmiles('C1CCC1C').HasSubstructMatch(am)) self.assertTrue(Chem.MolFromSmiles('C1CC(C)C1C').HasSubstructMatch(m)) self.assertFalse(Chem.MolFromSmiles('C1CC(C)C1C').HasSubstructMatch(am)) m = Chem.MolFromSmiles('C1CCC1*') am = Chem.AdjustQueryProperties(m) self.assertFalse(Chem.MolFromSmiles('C1CCC1C').HasSubstructMatch(m)) self.assertTrue(Chem.MolFromSmiles('C1CCC1C').HasSubstructMatch(am)) qps = Chem.AdjustQueryParameters() qps.makeDummiesQueries = False am = Chem.AdjustQueryProperties(m, qps) self.assertFalse(Chem.MolFromSmiles('C1CCC1C').HasSubstructMatch(am)) m = Chem.MolFromSmiles('C1=CC=CC=C1', sanitize=False) am = Chem.AdjustQueryProperties(m) self.assertTrue(Chem.MolFromSmiles('c1ccccc1').HasSubstructMatch(am)) qp = Chem.AdjustQueryParameters() qp.aromatizeIfPossible = False am = Chem.AdjustQueryProperties(m, qp) self.assertFalse(Chem.MolFromSmiles('c1ccccc1').HasSubstructMatch(am)) m = Chem.MolFromSmiles('C1CCC1OC') qps = Chem.AdjustQueryParameters() qps.makeAtomsGeneric = True am = Chem.AdjustQueryProperties(m, qps) self.assertEqual(Chem.MolToSmarts(am), '[D2]1-[D2]-[D2]-[D3]-1-*-*') qps.makeAtomsGenericFlags = Chem.ADJUST_IGNORERINGS am = Chem.AdjustQueryProperties(m, qps) self.assertEqual(Chem.MolToSmarts(am), '[#6&D2]1-[#6&D2]-[#6&D2]-[#6&D3]-1-*-*') qps = Chem.AdjustQueryParameters() qps.makeBondsGeneric = True am = Chem.AdjustQueryProperties(m, qps) self.assertEqual(Chem.MolToSmarts(am), '[#6&D2]1~[#6&D2]~[#6&D2]~[#6&D3]~1~[#8]~[#6]') qps.makeBondsGenericFlags = Chem.ADJUST_IGNORERINGS am = Chem.AdjustQueryProperties(m, qps) self.assertEqual(Chem.MolToSmarts(am), '[#6&D2]1-[#6&D2]-[#6&D2]-[#6&D3]-1~[#8]~[#6]') def testMolFragmentSmarts(self): m = Chem.MolFromSmiles('C1CCC1OC') self.assertEqual(Chem.MolFragmentToSmarts(m, [0, 1, 2]), '[#6]-[#6]-[#6]') # if bondsToUse is honored, the ring won't show up self.assertEqual(Chem.MolFragmentToSmarts(m, [0, 1, 2, 3], bondsToUse=[0, 1, 2, 3]), '[#6]-[#6]-[#6]-[#6]') # Does MolFragmentToSmarts accept output of AdjustQueryProperties? qps = Chem.AdjustQueryParameters() qps.makeAtomsGeneric = True am = Chem.AdjustQueryProperties(m, qps) self.assertEqual(Chem.MolFragmentToSmarts(am, [3, 4, 5]), '[D3]-*-*') def testAdjustQueryPropertiesgithubIssue1474(self): core = Chem.MolFromSmiles('[*:1]C1N([*:2])C([*:3])O1') core.GetAtomWithIdx(0).SetProp('foo', 'bar') core.GetAtomWithIdx(1).SetProp('foo', 'bar') ap = Chem.AdjustQueryProperties(core) self.assertEqual(ap.GetAtomWithIdx(0).GetPropsAsDict()["foo"], "bar") self.assertEqual(ap.GetAtomWithIdx(1).GetPropsAsDict()["foo"], "bar") def testReplaceAtomWithQueryAtom(self): mol = Chem.MolFromSmiles("CC(C)C") qmol = Chem.MolFromSmiles("C") matches = mol.GetSubstructMatches(qmol) self.assertEqual(((0, ), (1, ), (2, ), (3, )), matches) atom = qmol.GetAtomWithIdx(0) natom = rdqueries.ReplaceAtomWithQueryAtom(qmol, atom) qa = rdqueries.ExplicitDegreeEqualsQueryAtom(3) natom.ExpandQuery(qa, Chem.CompositeQueryType.COMPOSITE_AND) matches = mol.GetSubstructMatches(qmol) self.assertEqual(((1, ), ), matches) def testGithubIssue579(self): fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'NCI_aids_few.sdf.gz') inf = gzip.open(fileN) suppl = Chem.ForwardSDMolSupplier(inf) m0 = next(suppl) self.assertIsNot(m0, None) inf.close() del suppl def testSequenceBasics(self): " very basic round-tripping of the sequence reader/writer support " helm = 'PEPTIDE1{C.Y.I.Q.N.C.P.L.G}$$$$' seq = 'CYIQNCPLG' fasta = '>\nCYIQNCPLG\n' smi = 'CC[C@H](C)[C@H](NC(=O)[C@H](Cc1ccc(O)cc1)NC(=O)[C@@H](N)CS)C(=O)N[C@@H](CCC(N)=O)C(=O)N[C@@H](CC(N)=O)C(=O)N[C@@H](CS)C(=O)N1CCC[C@H]1C(=O)N[C@@H](CC(C)C)C(=O)NCC(=O)O' m = Chem.MolFromSequence(seq) self.assertTrue(m is not None) self.assertEqual(Chem.MolToSequence(m), seq) self.assertEqual(Chem.MolToHELM(m), helm) self.assertEqual(Chem.MolToFASTA(m), fasta) self.assertEqual(Chem.MolToSmiles(m, isomericSmiles=True), smi) m = Chem.MolFromHELM(helm) self.assertTrue(m is not None) self.assertEqual(Chem.MolToSequence(m), seq) self.assertEqual(Chem.MolToHELM(m), helm) self.assertEqual(Chem.MolToFASTA(m), fasta) self.assertEqual(Chem.MolToSmiles(m, isomericSmiles=True), smi) m = Chem.MolFromFASTA(fasta) self.assertTrue(m is not None) self.assertEqual(Chem.MolToSequence(m), seq) self.assertEqual(Chem.MolToHELM(m), helm) self.assertEqual(Chem.MolToFASTA(m), fasta) self.assertEqual(Chem.MolToSmiles(m, isomericSmiles=True), smi) seq = "CGCGAATTACCGCG" m = Chem.MolFromSequence(seq, flavor=6) # DNA self.assertEqual(Chem.MolToSequence(m), 'CGCGAATTACCGCG') self.assertEqual( Chem.MolToHELM(m), 'RNA1{[dR](C)P.[dR](G)P.[dR](C)P.[dR](G)P.[dR](A)P.[dR](A)P.[dR](T)P.[dR](T)P.[dR](A)P.[dR](C)P.[dR](C)P.[dR](G)P.[dR](C)P.[dR](G)}$$$$' ) seq = "CGCGAAUUACCGCG" m = Chem.MolFromSequence(seq, flavor=2) # RNA self.assertEqual(Chem.MolToSequence(m), 'CGCGAAUUACCGCG') self.assertEqual( Chem.MolToHELM(m), 'RNA1{R(C)P.R(G)P.R(C)P.R(G)P.R(A)P.R(A)P.R(U)P.R(U)P.R(A)P.R(C)P.R(C)P.R(G)P.R(C)P.R(G)}$$$$' ) m = Chem.MolFromSequence(seq, flavor=3) # RNA - 5' cap self.assertEqual(Chem.MolToSequence(m), 'CGCGAAUUACCGCG') self.assertEqual( Chem.MolToHELM(m), 'RNA1{P.R(C)P.R(G)P.R(C)P.R(G)P.R(A)P.R(A)P.R(U)P.R(U)P.R(A)P.R(C)P.R(C)P.R(G)P.R(C)P.R(G)}$$$$' ) def testResMolSupplier(self): mol = Chem.MolFromSmiles('CC') resMolSuppl = Chem.ResonanceMolSupplier(mol) del resMolSuppl resMolSuppl = Chem.ResonanceMolSupplier(mol) self.assertEqual(resMolSuppl.GetNumConjGrps(), 0) self.assertEqual(len(resMolSuppl), 1) self.assertEqual(resMolSuppl.GetNumConjGrps(), 0) mol = Chem.MolFromSmiles('NC(=[NH2+])c1ccc(cc1)C(=O)[O-]') totalFormalCharge = getTotalFormalCharge(mol) resMolSuppl = Chem.ResonanceMolSupplier(mol) self.assertFalse(resMolSuppl.GetIsEnumerated()) self.assertEqual(len(resMolSuppl), 4) self.assertTrue(resMolSuppl.GetIsEnumerated()) resMolSuppl = Chem.ResonanceMolSupplier(mol) self.assertFalse(resMolSuppl.GetIsEnumerated()) resMolSuppl.Enumerate() self.assertTrue(resMolSuppl.GetIsEnumerated()) self.assertTrue( (resMolSuppl[0].GetBondBetweenAtoms(0, 1).GetBondType() != resMolSuppl[1].GetBondBetweenAtoms( 0, 1).GetBondType()) or (resMolSuppl[0].GetBondBetweenAtoms(9, 10).GetBondType() != resMolSuppl[1].GetBondBetweenAtoms(9, 10).GetBondType())) resMolSuppl = Chem.ResonanceMolSupplier(mol, Chem.KEKULE_ALL) self.assertEqual(len(resMolSuppl), 8) bondTypeSet = set() # check that we actually have two alternate Kekule structures bondTypeSet.add(resMolSuppl[0].GetBondBetweenAtoms(3, 4).GetBondType()) bondTypeSet.add(resMolSuppl[1].GetBondBetweenAtoms(3, 4).GetBondType()) self.assertEqual(len(bondTypeSet), 2) bondTypeDict = {} resMolSuppl = Chem.ResonanceMolSupplier( mol, Chem.ALLOW_INCOMPLETE_OCTETS | Chem.UNCONSTRAINED_CATIONS | Chem.UNCONSTRAINED_ANIONS) self.assertEqual(len(resMolSuppl), 32) for i in range(len(resMolSuppl)): resMol = resMolSuppl[i] self.assertEqual(getTotalFormalCharge(resMol), totalFormalCharge) while (not resMolSuppl.atEnd()): resMol = next(resMolSuppl) self.assertEqual(getTotalFormalCharge(resMol), totalFormalCharge) resMolSuppl.reset() cmpFormalChargeBondOrder(self, resMolSuppl[0], next(resMolSuppl)) resMolSuppl = Chem.ResonanceMolSupplier( mol, Chem.ALLOW_INCOMPLETE_OCTETS | Chem.UNCONSTRAINED_CATIONS | Chem.UNCONSTRAINED_ANIONS, 10) self.assertEqual(len(resMolSuppl), 10) crambinPdb = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', '1CRN.pdb') mol = Chem.MolFromPDBFile(crambinPdb) resMolSuppl = Chem.ResonanceMolSupplier(mol) self.assertEqual(len(resMolSuppl), 1) resMolSuppl = Chem.ResonanceMolSupplier(mol, Chem.KEKULE_ALL) self.assertEqual(len(resMolSuppl), 8) def testGithub4884(self): # test that we don't hang mol = Chem.MolFromSmiles('O=[N+][O-]') supp = Chem.ResonanceMolSupplier(mol) supp.atEnd() def testSubstructMatchAcetate(self): mol = Chem.MolFromSmiles('CC(=O)[O-]') query = Chem.MolFromSmarts('C(=O)[O-]') resMolSuppl = Chem.ResonanceMolSupplier(mol) matches = mol.GetSubstructMatches(query) self.assertEqual(len(matches), 1) self.assertEqual(matches, ((1, 2, 3), )) matches = mol.GetSubstructMatches(query, uniquify=True) self.assertEqual(len(matches), 1) self.assertEqual(matches, ((1, 2, 3), )) matches = mol.GetSubstructMatches(query, uniquify=False) self.assertEqual(len(matches), 1) self.assertEqual(matches, ((1, 2, 3), )) matches = resMolSuppl.GetSubstructMatches(query) self.assertEqual(len(matches), 2) self.assertEqual(matches, ((1, 2, 3), (1, 3, 2))) matches = resMolSuppl.GetSubstructMatches(query, uniquify=True) self.assertEqual(len(matches), 1) self.assertEqual(matches, ((1, 2, 3), )) matches = resMolSuppl.GetSubstructMatches(query, uniquify=False) self.assertEqual(len(matches), 2) self.assertEqual(matches, ((1, 2, 3), (1, 3, 2))) query = Chem.MolFromSmarts('C(~O)~O') matches = mol.GetSubstructMatches(query, uniquify=False) self.assertEqual(len(matches), 2) self.assertEqual(matches, ((1, 2, 3), (1, 3, 2))) matches = mol.GetSubstructMatches(query, uniquify=True) self.assertEqual(len(matches), 1) self.assertEqual(matches, ((1, 2, 3), )) matches = resMolSuppl.GetSubstructMatches(query, uniquify=False) self.assertEqual(len(matches), 2) self.assertEqual(matches, ((1, 2, 3), (1, 3, 2))) matches = resMolSuppl.GetSubstructMatches(query, uniquify=True) self.assertEqual(len(matches), 1) self.assertEqual(matches, ((1, 2, 3), )) def testSubstructMatchDMAP(self): mol = Chem.MolFromSmiles('C(C)Nc1cc[nH+]cc1') query = Chem.MolFromSmarts('[#7+]') resMolSuppl = Chem.ResonanceMolSupplier(mol) matches = mol.GetSubstructMatches(query, False, False, False) self.assertEqual(len(matches), 1) p = matches[0] self.assertEqual(p[0], 6) matches = resMolSuppl.GetSubstructMatches(query, False, False, False) self.assertEqual(len(matches), 2) v = [] p = matches[0] v.append(p[0]) p = matches[1] v.append(p[0]) v.sort() self.assertEqual(v[0], 2) self.assertEqual(v[1], 6) def testCrambin(self): crambinPdb = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', '1CRN.pdb') crambin = Chem.MolFromPDBFile(crambinPdb) res = [] # protonate NH2 res.append(Chem.MolFromSmarts('[Nh2][Ch;Ch2]')) # protonate Arg res.append(Chem.MolFromSmarts('[Nh][C]([Nh2])=[Nh]')) setResidueFormalCharge(crambin, res, 1) res = [] # deprotonate COOH res.append(Chem.MolFromSmarts('C(=O)[Oh]')) setResidueFormalCharge(crambin, res, -1) res = [] resMolSupplST = Chem.ResonanceMolSupplier(crambin) # crambin has 2 Arg (3 resonance structures each); 1 Asp, 1 Glu # and 1 terminal COO- (2 resonance structures each) # so possible resonance structures are 3^2 * 2^3 = 72 self.assertEqual(len(resMolSupplST), 72) self.assertEqual(resMolSupplST.GetNumConjGrps(), 56) carboxylateQuery = Chem.MolFromSmarts('C(=O)[O-]') guanidiniumQuery = Chem.MolFromSmarts('NC(=[NH2+])N') matches = crambin.GetSubstructMatches(carboxylateQuery) self.assertEqual(len(matches), 3) matches = crambin.GetSubstructMatches(carboxylateQuery, uniquify=False) self.assertEqual(len(matches), 3) matches = crambin.GetSubstructMatches(guanidiniumQuery) self.assertEqual(len(matches), 0) matches = crambin.GetSubstructMatches(guanidiniumQuery, uniquify=False) self.assertEqual(len(matches), 0) matches = resMolSupplST.GetSubstructMatches(carboxylateQuery) self.assertEqual(len(matches), 6) self.assertEqual(matches, ((166, 167, 168), (166, 168, 167), (298, 299, 300), (298, 300, 299), (320, 321, 326), (320, 326, 321))) matches = resMolSupplST.GetSubstructMatches(carboxylateQuery, uniquify=True) self.assertEqual(len(matches), 3) self.assertEqual(matches, ((166, 167, 168), (298, 299, 300), (320, 321, 326))) matches = resMolSupplST.GetSubstructMatches(guanidiniumQuery) self.assertEqual(len(matches), 8) self.assertEqual(matches, ((66, 67, 68, 69), (66, 67, 69, 68), (68, 67, 69, 66), (69, 67, 68, 66), (123, 124, 125, 126), (123, 124, 126, 125), (125, 124, 126, 123), (126, 124, 125, 123))) matches = resMolSupplST.GetSubstructMatches(guanidiniumQuery, uniquify=True) self.assertEqual(len(matches), 2) self.assertEqual(matches, ((66, 67, 68, 69), (123, 124, 125, 126))) btList2ST = getBtList2(resMolSupplST) self.assertTrue(btList2ST) resMolSupplMT = Chem.ResonanceMolSupplier(crambin) resMolSupplMT.SetNumThreads(0) self.assertEqual(len(resMolSupplST), len(resMolSupplMT)) btList2MT = getBtList2(resMolSupplMT) self.assertTrue(btList2MT) self.assertEqual(len(btList2ST), len(btList2MT)) for i in range(len(btList2ST)): for j in range(len(btList2ST)): self.assertEqual(btList2ST[i][j], btList2MT[i][j]) for suppl in [resMolSupplST, resMolSupplMT]: matches = suppl.GetSubstructMatches(carboxylateQuery, numThreads=0) self.assertEqual(len(matches), 6) self.assertEqual(matches, ((166, 167, 168), (166, 168, 167), (298, 299, 300), (298, 300, 299), (320, 321, 326), (320, 326, 321))) matches = suppl.GetSubstructMatches(carboxylateQuery, uniquify=True, numThreads=0) self.assertEqual(len(matches), 3) self.assertEqual(matches, ((166, 167, 168), (298, 299, 300), (320, 321, 326))) matches = suppl.GetSubstructMatches(guanidiniumQuery, numThreads=0) self.assertEqual(len(matches), 8) self.assertEqual(matches, ((66, 67, 68, 69), (66, 67, 69, 68), (68, 67, 69, 66), (69, 67, 68, 66), (123, 124, 125, 126), (123, 124, 126, 125), (125, 124, 126, 123), (126, 124, 125, 123))) matches = suppl.GetSubstructMatches(guanidiniumQuery, uniquify=True, numThreads=0) self.assertEqual(len(matches), 2) self.assertEqual(matches, ((66, 67, 68, 69), (123, 124, 125, 126))) def testGitHub1166(self): mol = Chem.MolFromSmiles('NC(=[NH2+])c1ccc(cc1)C(=O)[O-]') resMolSuppl = Chem.ResonanceMolSupplier(mol, Chem.KEKULE_ALL) self.assertEqual(len(resMolSuppl), 8) # check that formal charges on odd indices are in the same position # as on even indices for i in range(0, len(resMolSuppl), 2): self.assertEqual(resMolSuppl[i].GetNumAtoms(), resMolSuppl[i + 1].GetNumAtoms()) for atomIdx in range(resMolSuppl[i].GetNumAtoms()): self.assertEqual(resMolSuppl[i].GetAtomWithIdx(atomIdx).GetFormalCharge(), resMolSuppl[i + 1].GetAtomWithIdx(atomIdx).GetFormalCharge()) # check that bond orders are alternate on aromatic bonds between # structures on odd indices and structures on even indices self.assertEqual(resMolSuppl[i].GetNumBonds(), resMolSuppl[i + 1].GetNumBonds()) for bondIdx in range(resMolSuppl[i].GetNumBonds()): self.assertTrue( ((not resMolSuppl[i].GetBondWithIdx(bondIdx).GetIsAromatic()) and (not resMolSuppl[i + 1].GetBondWithIdx(bondIdx).GetIsAromatic()) and (resMolSuppl[i].GetBondWithIdx(bondIdx).GetBondType() == resMolSuppl[i + 1].GetBondWithIdx(bondIdx).GetBondType())) or (resMolSuppl[i].GetBondWithIdx(bondIdx).GetIsAromatic() and resMolSuppl[i + 1].GetBondWithIdx(bondIdx).GetIsAromatic() and (int( round(resMolSuppl[i].GetBondWithIdx(bondIdx).GetBondTypeAsDouble() + resMolSuppl[i + 1].GetBondWithIdx(bondIdx).GetBondTypeAsDouble())) == 3))) def testConjGrpPerception(self): mol1 = Chem.MolFromMolBlock("""\ RDKit 2D 14 15 0 0 0 0 0 0 0 0999 V2000 3.7539 -1.2744 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.4317 -0.5660 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.1571 -1.3568 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.1651 -0.6484 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.4397 -1.4393 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.3921 -2.9385 0.0000 F 0 0 0 0 0 0 0 0 0 0 0 0 -2.7619 -0.7309 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.8095 0.7684 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -4.1316 1.4768 0.0000 F 0 0 0 0 0 0 0 0 0 0 0 0 -1.5349 1.5592 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.2127 0.8508 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.0619 1.6417 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 2.3841 0.9333 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.6587 1.7241 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 2 3 4 0 3 4 4 0 4 5 4 0 5 6 1 0 5 7 4 0 7 8 4 0 8 9 1 0 8 10 4 0 10 11 4 0 11 12 4 0 12 13 4 0 13 14 1 0 13 2 4 0 11 4 4 0 M END $$$$ """) mol2 = Chem.MolFromMolBlock("""\ RDKit 2D 14 15 0 0 0 0 0 0 0 0999 V2000 1.0619 -1.6417 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 -0.2127 -0.8508 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.5349 -1.5592 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.8095 -0.7684 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.7619 0.7309 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.4397 1.4393 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.1651 0.6484 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.1571 1.3568 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.4317 0.5660 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.7539 1.2744 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.3841 -0.9333 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.6587 -1.7241 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -4.1316 -1.4768 0.0000 F 0 0 0 0 0 0 0 0 0 0 0 0 -1.3921 2.9385 0.0000 F 0 0 0 0 0 0 0 0 0 0 0 0 1 2 4 0 3 4 4 0 4 5 4 0 5 6 4 0 2 3 4 0 2 7 4 0 7 8 4 0 8 9 4 0 9 10 1 0 9 11 4 0 11 12 1 0 11 1 4 0 6 7 4 0 4 13 1 0 6 14 1 0 M END $$$$ """) resMolSuppl1 = Chem.ResonanceMolSupplier(mol1, Chem.KEKULE_ALL) self.assertEqual(len(resMolSuppl1), 3) resMolSuppl2 = Chem.ResonanceMolSupplier(mol2, Chem.KEKULE_ALL) self.assertEqual(len(resMolSuppl2), 3) def testGitHub2597(self): class MyBrokenCallBack(Chem.ResonanceMolSupplier): def __call__(self): return True class MyBrokenCallBack2(Chem.ResonanceMolSupplierCallback): pass class ExceedNumStructures(Chem.ResonanceMolSupplierCallback): def __init__(self, parent): super().__init__() self._parent = parent def __call__(self): self._parent.assertEqual(self.GetNumConjGrps(), 1) return (self.GetNumStructures(0) < 12) class ExceedNumDiverseStructures(Chem.ResonanceMolSupplierCallback): def __init__(self, parent): super().__init__() self._parent = parent def __call__(self): self._parent.assertEqual(self.GetNumConjGrps(), 1) return (self.GetNumDiverseStructures(0) < 8) class ExceedTimeout(Chem.ResonanceMolSupplierCallback): def __init__(self, parent): super().__init__() self.start_time = None self.timeout = timedelta(seconds=3) self._parent = parent def __call__(self): if (self.start_time is None): self.start_time = datetime.now() return (datetime.now() - self.start_time < self.timeout) mol = Chem.MolFromSmiles( "ClC1=NC(NC2=CC=CC3=C2C(=O)C2=CC=CC=C2C3=O)=NC(NC2=CC=CC3=C2C(=O)C2=CC=CC=C2C3=O)=N1") resMolSuppl = Chem.ResonanceMolSupplier(mol) self.assertEqual(len(resMolSuppl), 1) resMolSuppl = Chem.ResonanceMolSupplier(mol, Chem.KEKULE_ALL) self.assertEqual(len(resMolSuppl), 32) resMolSuppl = Chem.ResonanceMolSupplier(mol, Chem.ALLOW_CHARGE_SEPARATION, 10) self.assertEqual(len(resMolSuppl), 10) self.assertFalse(resMolSuppl.WasCanceled()) resMolSuppl = Chem.ResonanceMolSupplier(mol, Chem.ALLOW_CHARGE_SEPARATION) callback = resMolSuppl.GetProgressCallback() self.assertIsNone(callback) resMolSuppl.SetProgressCallback(ExceedNumStructures(self)) callback = resMolSuppl.GetProgressCallback() self.assertTrue(isinstance(callback, ExceedNumStructures)) resMolSuppl.SetProgressCallback(None) callback = resMolSuppl.GetProgressCallback() self.assertIsNone(callback) resMolSuppl.SetProgressCallback(ExceedNumStructures(self)) self.assertEqual(len(resMolSuppl), 12) self.assertTrue(resMolSuppl.WasCanceled()) resMolSuppl = Chem.ResonanceMolSupplier(mol, Chem.ALLOW_CHARGE_SEPARATION) with self.assertRaises(TypeError): resMolSuppl.SetProgressCallback(MyBrokenCallBack()) with self.assertRaises(AttributeError): resMolSuppl.SetProgressCallback(MyBrokenCallBack2()) resMolSuppl.SetProgressCallback(ExceedNumDiverseStructures(self)) self.assertEqual(len(resMolSuppl), 9) self.assertTrue(resMolSuppl.WasCanceled()) resMolSuppl = Chem.ResonanceMolSupplier( mol, Chem.UNCONSTRAINED_CATIONS | Chem.UNCONSTRAINED_ANIONS | Chem.KEKULE_ALL) resMolSuppl.SetProgressCallback(ExceedTimeout(self)) resMolSuppl.Enumerate() print(len(resMolSuppl)) self.assertTrue(resMolSuppl.WasCanceled()) def testAtomBondProps(self): origVal = Chem.GetUseLegacyStereoPerception() Chem.SetUseLegacyStereoPerception(True) m = Chem.MolFromSmiles('c1ccccc1C(C)C') for atom in m.GetAtoms(): d = atom.GetPropsAsDict() self.assertEqual(set(d.keys()), set(['_CIPRank', '__computedProps'])) self.assertEqual(type(d['_CIPRank']), int) self.assertEqual(list(d['__computedProps']), ['_CIPRank']) m = Chem.MolFromSmiles('c1ccccc1') self.assertEqual(Chem.ComputeAtomCIPRanks(m), (0, 0, 0, 0, 0, 0)) for atom in m.GetAtoms(): d = atom.GetPropsAsDict() self.assertEqual(set(d.keys()), set(['_CIPRank', '__computedProps'])) self.assertEqual(d['_CIPRank'], 0) self.assertEqual(list(d['__computedProps']), ['_CIPRank']) Chem.SetUseLegacyStereoPerception(origVal) for bond in m.GetBonds(): self.assertEqual(bond.GetPropsAsDict(), {}) def testSDProps(self): fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'NCI_aids_few.sdf') #fileN = "../FileParsers/test_data/NCI_aids_few.sdf" sddata = [ { '_MolFileInfo': 'BBtclserve11129916382D 0 0.00000 0.00000 48', 'NSC': 48, 'NCI_AIDS_Antiviral_Screen_IC50': '2.00E-04\tM\t=\t2.46E-05\t3', '_Name': 48, 'CAS_RN': '15716-70-8', '_MolFileChiralFlag': 0, '_MolFileComments': '15716-70-8', 'NCI_AIDS_Antiviral_Screen_EC50': '2.00E-04\tM\t>\t2.00E-04\t3', 'NCI_AIDS_Antiviral_Screen_Conclusion': 'CI' }, { '_MolFileInfo': 'BBtclserve11129916382D 0 0.00000 0.00000 78', 'NSC': 78, 'NCI_AIDS_Antiviral_Screen_IC50': '2.00E-04\tM\t=\t9.80E-05\t3', '_Name': 78, 'CAS_RN': '6290-84-2', '_MolFileChiralFlag': 0, '_MolFileComments': '6290-84-2', 'NCI_AIDS_Antiviral_Screen_EC50': '2.00E-04\tM\t>\t2.00E-04\t3', 'NCI_AIDS_Antiviral_Screen_Conclusion': 'CI' }, { '_MolFileInfo': 'BBtclserve11129916382D 0 0.00000 0.00000 128', 'NSC': 128, 'NCI_AIDS_Antiviral_Screen_IC50': '2.00E-04\tM\t=\t4.60E-05\t4', '_Name': 128, 'CAS_RN': '5395-10-8', '_MolFileChiralFlag': 0, '_MolFileComments': '5395-10-8', 'NCI_AIDS_Antiviral_Screen_EC50': '2.00E-04\tM\t>\t2.00E-04\t4', 'NCI_AIDS_Antiviral_Screen_Conclusion': 'CI' }, { '_MolFileInfo': 'BBtclserve11129916382D 0 0.00000 0.00000 163', 'NSC': 163, 'NCI_AIDS_Antiviral_Screen_IC50': '6.75E-04\tM\t>\t6.75E-04\t2', '_Name': 163, 'CAS_RN': '81-11-8', '_MolFileChiralFlag': 0, '_MolFileComments': '81-11-8', 'NCI_AIDS_Antiviral_Screen_EC50': '6.75E-04\tM\t>\t6.75E-04\t2', 'NCI_AIDS_Antiviral_Screen_Conclusion': 'CI' }, { '_MolFileInfo': 'BBtclserve11129916382D 0 0.00000 0.00000 164', 'NSC': 164, 'NCI_AIDS_Antiviral_Screen_IC50': '2.00E-04\tM\t>\t2.00E-04\t2', '_Name': 164, 'CAS_RN': '5325-43-9', '_MolFileChiralFlag': 0, '_MolFileComments': '5325-43-9', 'NCI_AIDS_Antiviral_Screen_EC50': '2.00E-04\tM\t>\t2.00E-04\t2', 'NCI_AIDS_Antiviral_Screen_Conclusion': 'CI' }, { '_MolFileInfo': 'BBtclserve11129916382D 0 0.00000 0.00000 170', 'NSC': 170, '_Name': 170, 'CAS_RN': '999-99-9', '_MolFileChiralFlag': 0, '_MolFileComments': '999-99-9', 'NCI_AIDS_Antiviral_Screen_EC50': '9.47E-04\tM\t>\t9.47E-04\t1', 'NCI_AIDS_Antiviral_Screen_Conclusion': 'CI' }, { '_MolFileInfo': 'BBtclserve11129916382D 0 0.00000 0.00000 180', 'NSC': 180, 'NCI_AIDS_Antiviral_Screen_IC50': '6.46E-04\tM\t=\t5.80E-04\t2\n1.81E-03\tM\t=\t6.90E-04\t2', '_Name': 180, 'CAS_RN': '69-72-7', '_MolFileChiralFlag': 0, '_MolFileComments': '69-72-7', 'NCI_AIDS_Antiviral_Screen_EC50': '6.46E-04\tM\t>\t6.46E-04\t2\n1.81E-03\tM\t>\t1.81E-03\t2', 'NCI_AIDS_Antiviral_Screen_Conclusion': 'CI' }, { '_MolFileInfo': 'BBtclserve11129916382D 0 0.00000 0.00000 186', 'NSC': 186, 'NCI_AIDS_Antiviral_Screen_IC50': '1.44E-04\tM\t=\t2.49E-05\t2', '_Name': 186, 'CAS_RN': '518-75-2', '_MolFileChiralFlag': 0, '_MolFileComments': '518-75-2', 'NCI_AIDS_Antiviral_Screen_EC50': '1.44E-04\tM\t>\t1.44E-04\t2', 'NCI_AIDS_Antiviral_Screen_Conclusion': 'CI' }, { '_MolFileInfo': 'BBtclserve11129916382D 0 0.00000 0.00000 192', 'NSC': 192, 'NCI_AIDS_Antiviral_Screen_IC50': '2.00E-04\tM\t=\t3.38E-06\t2', '_Name': 192, 'CAS_RN': '2217-55-2', '_MolFileChiralFlag': 0, '_MolFileComments': '2217-55-2', 'NCI_AIDS_Antiviral_Screen_EC50': '2.00E-04\tM\t>\t2.00E-04\t2', 'NCI_AIDS_Antiviral_Screen_Conclusion': 'CI' }, { '_MolFileInfo': 'BBtclserve11129916382D 0 0.00000 0.00000 203', 'NSC': 203, '_Name': 203, 'CAS_RN': '1155-00-6', '_MolFileChiralFlag': 0, '_MolFileComments': '1155-00-6', 'NCI_AIDS_Antiviral_Screen_Conclusion': 'CI' }, { '_MolFileInfo': 'BBtclserve11129916382D 0 0.00000 0.00000 210', 'NSC': 210, 'NCI_AIDS_Antiviral_Screen_IC50': '1.33E-03\tM\t>\t1.33E-03\t2', '_Name': 210, 'CAS_RN': '5325-75-7', '_MolFileChiralFlag': 0, '_MolFileComments': '5325-75-7', 'NCI_AIDS_Antiviral_Screen_EC50': '1.33E-03\tM\t>\t1.33E-03\t2', 'NCI_AIDS_Antiviral_Screen_Conclusion': 'CI' }, { '_MolFileInfo': 'BBtclserve11129916382D 0 0.00000 0.00000 211', 'NSC': 211, 'NCI_AIDS_Antiviral_Screen_IC50': '2.00E-04\tM\t>\t2.00E-04\t8\n2.00E-03\tM\t=\t1.12E-03\t2', '_Name': 211, 'CAS_RN': '5325-76-8', '_MolFileChiralFlag': 0, '_MolFileComments': '5325-76-8', 'NCI_AIDS_Antiviral_Screen_EC50': '2.00E-04\tM\t>\t7.42E-05\t8\n2.00E-03\tM\t=\t6.35E-05\t2', 'NCI_AIDS_Antiviral_Screen_Conclusion': 'CM' }, { '_MolFileInfo': 'BBtclserve11129916382D 0 0.00000 0.00000 213', 'NSC': 213, 'NCI_AIDS_Antiviral_Screen_IC50': '2.00E-04\tM\t>\t2.00E-04\t4', '_Name': 213, 'CAS_RN': '119-80-2', '_MolFileChiralFlag': 0, '_MolFileComments': '119-80-2', 'NCI_AIDS_Antiviral_Screen_EC50': '2.00E-04\tM\t>\t2.00E-04\t4', 'NCI_AIDS_Antiviral_Screen_Conclusion': 'CI' }, { '_MolFileInfo': 'BBtclserve11129916382D 0 0.00000 0.00000 220', 'NSC': 220, 'NCI_AIDS_Antiviral_Screen_IC50': '2.00E-04\tM\t>\t2.00E-04\t4', '_Name': 220, 'CAS_RN': '5325-83-7', '_MolFileChiralFlag': 0, '_MolFileComments': '5325-83-7', 'NCI_AIDS_Antiviral_Screen_EC50': '2.00E-04\tM\t>\t2.00E-04\t4', 'NCI_AIDS_Antiviral_Screen_Conclusion': 'CI' }, { '_MolFileInfo': 'BBtclserve11129916382D 0 0.00000 0.00000 229', 'NSC': 229, 'NCI_AIDS_Antiviral_Screen_IC50': '2.00E-04\tM\t>\t2.00E-04\t2', '_Name': 229, 'CAS_RN': '5325-88-2', '_MolFileChiralFlag': 0, '_MolFileComments': '5325-88-2', 'NCI_AIDS_Antiviral_Screen_EC50': '2.00E-04\tM\t>\t2.00E-04\t2', 'NCI_AIDS_Antiviral_Screen_Conclusion': 'CI' }, { '_MolFileInfo': 'BBtclserve11129916382D 0 0.00000 0.00000 256', 'NSC': 256, 'NCI_AIDS_Antiviral_Screen_IC50': '2.00E-04\tM\t>\t2.00E-04\t4', '_Name': 256, 'CAS_RN': '5326-06-7', '_MolFileChiralFlag': 0, '_MolFileComments': '5326-06-7', 'NCI_AIDS_Antiviral_Screen_EC50': '2.00E-04\tM\t>\t2.00E-04\t4', 'NCI_AIDS_Antiviral_Screen_Conclusion': 'CI' }, ] sdSup = Chem.SDMolSupplier(fileN) for i, mol in enumerate(sdSup): self.assertEqual(mol.GetPropsAsDict(includePrivate=True), sddata[i]) def testGetSetProps(self): m = Chem.MolFromSmiles("CC") errors = { "int": r"key `foo` exists but does not result in an integer value reason: [B,b]ad any[\ ,_]cast", "uint overflow": "key `foo` exists but does not result in an unsigned integer value reason: bad numeric conversion: negative overflow", "int overflow": "key `foo` exists but does not result in an integer value reason: bad numeric conversion: positive overflow", "double": r"key `foo` exists but does not result in a double value reason: [B,b]ad any[\ ,_]cast", "bool": r"key `foo` exists but does not result in a True or False value reason: [B,b]ad any[\ ,_]cast" } for ob in [m, list(m.GetAtoms())[0], list(m.GetBonds())[0]]: ob.SetDoubleProp("foo", 2.0) with self.assertRaises(ValueError) as e: ob.GetBoolProp("foo") self.assertRegex(str(e.exception), errors["bool"]) with self.assertRaises(ValueError) as e: ob.GetIntProp("foo") self.assertRegex(str(e.exception), errors["int"]) ob.SetBoolProp("foo", True) with self.assertRaises(ValueError) as e: ob.GetDoubleProp("foo") self.assertRegex(str(e.exception), errors["double"]) with self.assertRaises(ValueError) as e: ob.GetIntProp("foo") self.assertRegex(str(e.exception), errors["int"]) ob.SetIntProp("foo", -1) with self.assertRaises(ValueError) as e: ob.GetUnsignedProp("foo") self.assertEqual(str(e.exception), errors["uint overflow"]) ob.SetUnsignedProp("foo", 4294967295) self.assertEqual(ob.GetUnsignedProp("foo"), 4294967295) with self.assertRaises(ValueError) as e: ob.GetIntProp("foo") self.assertEqual(str(e.exception), errors["int overflow"]) def testInvariantException(self): m = Chem.MolFromSmiles("C") try: m.GetAtomWithIdx(3) except RuntimeError as e: import platform details = str(e) if platform.system() == 'Windows': details = details.replace('\\', '/') self.assertTrue("Code/GraphMol/ROMol.cpp".lower() in details.lower()) self.assertTrue("Failed Expression: 3 < 1" in details) self.assertTrue("RDKIT:" in details) self.assertTrue(__version__ in details) def testGetSDText(self): fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'NCI_aids_few.sdf') #fileN = "../FileParsers/test_data/NCI_aids_few.sdf" sdSup = Chem.SDMolSupplier(fileN) for m in sdSup: sdt = Chem.SDWriter.GetText(m) ts = Chem.SDMolSupplier() ts.SetData(sdt) nm = next(ts) self.assertEqual(Chem.MolToSmiles(m, True), Chem.MolToSmiles(nm, True)) for pn in m.GetPropNames(): self.assertTrue(nm.HasProp(pn)) self.assertEqual(m.GetProp(pn), nm.GetProp(pn)) def testUnfoldedRDKFingerprint(self): from rdkit.Chem import AllChem m = Chem.MolFromSmiles('c1ccccc1N') fp = AllChem.UnfoldedRDKFingerprintCountBased(m) fpDict = fp.GetNonzeroElements() self.assertEqual(len(fpDict.items()), 19) self.assertTrue(374073638 in fpDict) self.assertEqual(fpDict[374073638], 6) self.assertTrue(464351883 in fpDict) self.assertEqual(fpDict[464351883], 2) self.assertTrue(1949583554 in fpDict) self.assertEqual(fpDict[1949583554], 6) self.assertTrue(4105342207 in fpDict) self.assertEqual(fpDict[4105342207], 1) self.assertTrue(794080973 in fpDict) self.assertEqual(fpDict[794080973], 1) self.assertTrue(3826517238 in fpDict) self.assertEqual(fpDict[3826517238], 2) m = Chem.MolFromSmiles('Cl') fp = AllChem.UnfoldedRDKFingerprintCountBased(m) fpDict = fp.GetNonzeroElements() self.assertEqual(len(fpDict.items()), 0) m = Chem.MolFromSmiles('CCCO') aBits = {} fp = AllChem.UnfoldedRDKFingerprintCountBased(m, bitInfo=aBits) fpDict = fp.GetNonzeroElements() self.assertEqual(len(fpDict.items()), 5) self.assertTrue(1524090560 in fpDict) self.assertEqual(fpDict[1524090560], 1) self.assertTrue(1940446997 in fpDict) self.assertEqual(fpDict[1940446997], 1) self.assertTrue(3977409745 in fpDict) self.assertEqual(fpDict[3977409745], 1) self.assertTrue(4274652475 in fpDict) self.assertEqual(fpDict[4274652475], 1) self.assertTrue(4275705116 in fpDict) self.assertEqual(fpDict[4275705116], 2) self.assertTrue(1524090560 in aBits) self.assertEqual(aBits[1524090560], [[1, 2]]) self.assertTrue(1940446997 in aBits) self.assertEqual(aBits[1940446997], [[0, 1]]) self.assertTrue(3977409745 in aBits) self.assertEqual(aBits[3977409745], [[0, 1, 2]]) self.assertTrue(4274652475 in aBits) self.assertEqual(aBits[4274652475], [[2]]) self.assertTrue(4275705116 in aBits) self.assertEqual(aBits[4275705116], [[0], [1]]) def testRDKFingerprintBitInfo(self): m = Chem.MolFromSmiles('CCCO') aBits = {} fp1 = Chem.RDKFingerprint(m, bitInfo=aBits) self.assertTrue(1183 in aBits) self.assertEqual(aBits[1183], [[1, 2]]) self.assertTrue(709 in aBits) self.assertEqual(aBits[709], [[0, 1]]) self.assertTrue(1118 in aBits) self.assertEqual(aBits[1118], [[0, 1, 2]]) self.assertTrue(562 in aBits) self.assertEqual(aBits[562], [[2]]) self.assertTrue(1772 in aBits) self.assertEqual(aBits[1772], [[0], [1]]) def testSimpleAromaticity(self): m = Chem.MolFromSmiles('c1ccccc1') self.assertTrue(m.GetBondWithIdx(0).GetIsAromatic()) self.assertTrue(m.GetAtomWithIdx(0).GetIsAromatic()) Chem.Kekulize(m, True) self.assertFalse(m.GetBondWithIdx(0).GetIsAromatic()) self.assertFalse(m.GetAtomWithIdx(0).GetIsAromatic()) Chem.SetAromaticity(m, Chem.AROMATICITY_SIMPLE) self.assertTrue(m.GetBondWithIdx(0).GetIsAromatic()) self.assertTrue(m.GetAtomWithIdx(0).GetIsAromatic()) m = Chem.MolFromSmiles('c1c[nH]cc1') self.assertTrue(m.GetBondWithIdx(0).GetIsAromatic()) self.assertTrue(m.GetAtomWithIdx(0).GetIsAromatic()) Chem.Kekulize(m, True) self.assertFalse(m.GetBondWithIdx(0).GetIsAromatic()) self.assertFalse(m.GetAtomWithIdx(0).GetIsAromatic()) Chem.SetAromaticity(m, Chem.AROMATICITY_SIMPLE) self.assertTrue(m.GetBondWithIdx(0).GetIsAromatic()) self.assertTrue(m.GetAtomWithIdx(0).GetIsAromatic()) m = Chem.MolFromSmiles('c1cccoocc1') self.assertTrue(m.GetBondWithIdx(0).GetIsAromatic()) self.assertTrue(m.GetAtomWithIdx(0).GetIsAromatic()) Chem.Kekulize(m, True) self.assertFalse(m.GetBondWithIdx(0).GetIsAromatic()) self.assertFalse(m.GetAtomWithIdx(0).GetIsAromatic()) Chem.SetAromaticity(m, Chem.AROMATICITY_SIMPLE) self.assertFalse(m.GetBondWithIdx(0).GetIsAromatic()) self.assertFalse(m.GetAtomWithIdx(0).GetIsAromatic()) m = Chem.MolFromSmiles('c1ooc1') self.assertTrue(m.GetBondWithIdx(0).GetIsAromatic()) self.assertTrue(m.GetAtomWithIdx(0).GetIsAromatic()) Chem.Kekulize(m, True) self.assertFalse(m.GetBondWithIdx(0).GetIsAromatic()) self.assertFalse(m.GetAtomWithIdx(0).GetIsAromatic()) Chem.SetAromaticity(m, Chem.AROMATICITY_SIMPLE) self.assertFalse(m.GetBondWithIdx(0).GetIsAromatic()) self.assertFalse(m.GetAtomWithIdx(0).GetIsAromatic()) m = Chem.MolFromSmiles('C1=CC2=CC=CC=CC2=C1') self.assertTrue(m.GetBondWithIdx(0).GetIsAromatic()) self.assertTrue(m.GetAtomWithIdx(0).GetIsAromatic()) Chem.Kekulize(m, True) self.assertFalse(m.GetBondWithIdx(0).GetIsAromatic()) self.assertFalse(m.GetAtomWithIdx(0).GetIsAromatic()) Chem.SetAromaticity(m, Chem.AROMATICITY_SIMPLE) self.assertFalse(m.GetBondWithIdx(0).GetIsAromatic()) self.assertFalse(m.GetAtomWithIdx(0).GetIsAromatic()) def testGithub955(self): m = Chem.MolFromSmiles("CCC") m.GetAtomWithIdx(0).SetProp("foo", "1") self.assertEqual(list(m.GetAtomWithIdx(0).GetPropNames()), ["foo"]) m.GetBondWithIdx(0).SetProp("foo", "1") self.assertEqual(list(m.GetBondWithIdx(0).GetPropNames()), ["foo"]) def testMDLProps(self): m = Chem.MolFromSmiles("CCC") m.GetAtomWithIdx(0).SetAtomMapNum(1) Chem.SetAtomAlias(m.GetAtomWithIdx(1), "foo") Chem.SetAtomValue(m.GetAtomWithIdx(1), "bar") m = Chem.MolFromMolBlock(Chem.MolToMolBlock(m)) self.assertEqual(m.GetAtomWithIdx(0).GetAtomMapNum(), 1) self.assertEqual(Chem.GetAtomAlias(m.GetAtomWithIdx(1)), "foo") self.assertEqual(Chem.GetAtomValue(m.GetAtomWithIdx(1)), "bar") def testSmilesProps(self): m = Chem.MolFromSmiles("C") Chem.SetSupplementalSmilesLabel(m.GetAtomWithIdx(0), 'xxx') self.assertEqual(Chem.MolToSmiles(m), "Cxxx") def testGithub1051(self): # just need to test that this exists: self.assertTrue(Chem.BondDir.EITHERDOUBLE) def testGithub1041(self): a = Chem.Atom(6) self.assertRaises(RuntimeError, lambda: a.GetOwningMol()) self.assertRaises(RuntimeError, lambda: a.GetNeighbors()) self.assertRaises(RuntimeError, lambda: a.GetBonds()) self.assertRaises(RuntimeError, lambda: a.IsInRing()) self.assertRaises(RuntimeError, lambda: a.IsInRingSize(4)) def testSmilesParseParams(self): smi = "CCC |$foo;;bar$| ourname" m = Chem.MolFromSmiles(smi) self.assertTrue(m is not None) ps = Chem.SmilesParserParams() ps.allowCXSMILES = False ps.parseName = False m = Chem.MolFromSmiles(smi, ps) self.assertTrue(m is None) ps.allowCXSMILES = True ps.parseName = True m = Chem.MolFromSmiles(smi, ps) self.assertTrue(m is not None) self.assertTrue(m.GetAtomWithIdx(0).HasProp('atomLabel')) self.assertEqual(m.GetAtomWithIdx(0).GetProp('atomLabel'), "foo") self.assertTrue(m.HasProp('_Name')) self.assertEqual(m.GetProp('_Name'), "ourname") self.assertEqual(m.GetProp("_CXSMILES_Data"), "|$foo;;bar$|") def testWriteCXSmiles(self): smi = "CCC |$foo;;bar$|" ps = Chem.SmilesParserParams() ps.allowCXSMILES = True m = Chem.MolFromSmiles(smi, ps) self.assertTrue(m is not None) self.assertTrue(m.GetAtomWithIdx(0).HasProp('atomLabel')) self.assertEqual(m.GetAtomWithIdx(0).GetProp('atomLabel'), "foo") self.assertEqual(Chem.MolToCXSmiles(m), 'CCC |$foo;;bar$|') smi = "Cl.CCC |$;foo;;bar$|" m = Chem.MolFromSmiles(smi, ps) self.assertTrue(m is not None) self.assertTrue(m.GetAtomWithIdx(1).HasProp('atomLabel')) self.assertEqual(m.GetAtomWithIdx(1).GetProp('atomLabel'), "foo") self.assertEqual(Chem.MolFragmentToCXSmiles(m, atomsToUse=(1, 2, 3)), 'CCC |$foo;;bar$|') def testPickleProps(self): import pickle m = Chem.MolFromSmiles('C1=CN=CC=C1') m.SetProp("_Name", "Name") for atom in m.GetAtoms(): atom.SetProp("_foo", "bar" + str(atom.GetIdx())) atom.SetProp("foo", "baz" + str(atom.GetIdx())) Chem.SetDefaultPickleProperties(Chem.PropertyPickleOptions.AllProps) pkl = pickle.dumps(m) m2 = pickle.loads(pkl) smi1 = Chem.MolToSmiles(m) smi2 = Chem.MolToSmiles(m2) self.assertTrue(smi1 == smi2) self.assertEqual(m2.GetProp("_Name"), "Name") for atom in m2.GetAtoms(): self.assertEqual(atom.GetProp("_foo"), "bar" + str(atom.GetIdx())) self.assertEqual(atom.GetProp("foo"), "baz" + str(atom.GetIdx())) Chem.SetDefaultPickleProperties(Chem.PropertyPickleOptions.AtomProps) pkl = pickle.dumps(m) m2 = pickle.loads(pkl) smi1 = Chem.MolToSmiles(m) smi2 = Chem.MolToSmiles(m2) self.assertTrue(smi1 == smi2) self.assertFalse(m2.HasProp("_Name")) for atom in m2.GetAtoms(): self.assertFalse(atom.HasProp("_foo")) self.assertEqual(atom.GetProp("foo"), "baz" + str(atom.GetIdx())) Chem.SetDefaultPickleProperties(Chem.PropertyPickleOptions.NoProps) pkl = pickle.dumps(m) m2 = pickle.loads(pkl) smi1 = Chem.MolToSmiles(m) smi2 = Chem.MolToSmiles(m2) self.assertTrue(smi1 == smi2) self.assertFalse(m2.HasProp("_Name")) for atom in m2.GetAtoms(): self.assertFalse(atom.HasProp("_foo")) self.assertFalse(atom.HasProp("foo")) Chem.SetDefaultPickleProperties(Chem.PropertyPickleOptions.MolProps | Chem.PropertyPickleOptions.PrivateProps) pkl = pickle.dumps(m) m2 = pickle.loads(pkl) smi1 = Chem.MolToSmiles(m) smi2 = Chem.MolToSmiles(m2) self.assertTrue(smi1 == smi2) self.assertEqual(m2.GetProp("_Name"), "Name") for atom in m2.GetAtoms(): self.assertFalse(atom.HasProp("_foo")) self.assertFalse(atom.HasProp("foo")) def testGithub1352(self): self.assertTrue('SP' in Chem.HybridizationType.names) self.assertTrue('S' in Chem.HybridizationType.names) m = Chem.MolFromSmiles('CC(=O)O.[Na]') self.assertEqual(m.GetAtomWithIdx(0).GetHybridization().name, 'SP3') self.assertEqual(m.GetAtomWithIdx(4).GetHybridization().name, 'S') def testGithub1366(self): mol = Chem.MolFromSmiles('*C*') mol = Chem.RWMol(mol) ats = iter(mol.GetAtoms()) atom = next(ats) mol.RemoveAtom(atom.GetIdx()) self.assertRaises(RuntimeError, next, ats) mol = Chem.MolFromSmiles('*C*') mol = Chem.RWMol(mol) bonds = iter(mol.GetBonds()) bond = next(bonds) mol.RemoveBond(bond.GetBeginAtomIdx(), bond.GetEndAtomIdx()) self.assertRaises(RuntimeError, next, bonds) def testGithub1478(self): data = """ MJ150720 8 8 0 0 0 0 0 0 0 0999 V2000 -0.4242 -1.4883 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 0.2901 -1.0758 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.0046 0.9865 0.0000 A 0 0 0 0 0 0 0 0 0 0 0 0 1.0046 0.1614 0.0000 A 0 0 0 0 0 0 0 0 0 0 0 0 0.2901 -0.2508 0.0000 A 0 0 0 0 0 0 0 0 0 0 0 0 -0.4243 0.1614 0.0000 A 0 0 0 0 0 0 0 0 0 0 0 0 -0.4243 0.9865 0.0000 A 0 0 0 0 0 0 0 0 0 0 0 0 0.2901 1.3990 0.0000 A 0 0 0 0 0 0 0 0 0 0 0 0 7 6 4 0 0 0 0 8 7 4 0 0 0 0 6 5 4 0 0 0 0 5 4 4 0 0 0 0 5 2 1 0 0 0 0 4 3 4 0 0 0 0 8 3 4 0 0 0 0 2 1 2 0 0 0 0 M END """ pattern = Chem.MolFromMolBlock(data) m = Chem.MolFromSmiles("c1ccccc1C=O") self.assertTrue(m.HasSubstructMatch(pattern)) def testGithub1320(self): import pickle mol = Chem.MolFromSmiles('N[C@@H](C)O') mol2 = pickle.loads(pickle.dumps(mol)) self.assertEqual(Chem.MolToSmiles(mol, isomericSmiles=True), Chem.MolToSmiles(mol2, isomericSmiles=True)) Chem.SetDefaultPickleProperties(Chem.PropertyPickleOptions.AtomProps | Chem.PropertyPickleOptions.BondProps | Chem.PropertyPickleOptions.MolProps | Chem.PropertyPickleOptions.PrivateProps | Chem.PropertyPickleOptions.ComputedProps) mol3 = pickle.loads(pickle.dumps(mol)) for a1, a2 in zip(mol.GetAtoms(), mol3.GetAtoms()): d1 = a1.GetPropsAsDict() d2 = a2.GetPropsAsDict() if "__computedProps" in d1: c1 = list(d1["__computedProps"]) c2 = list(d2["__computedProps"]) del d1["__computedProps"] del d2["__computedProps"] self.assertEqual(c1, c2) assert d1 == d2 for a1, a2 in zip(mol.GetBonds(), mol3.GetBonds()): d1 = a1.GetPropsAsDict() d2 = a2.GetPropsAsDict() if "__computedProps" in d1: c1 = list(d1["__computedProps"]) c2 = list(d2["__computedProps"]) del d1["__computedProps"] del d2["__computedProps"] self.assertEqual(c1, c2) assert d1 == d2 self.assertEqual(Chem.MolToSmiles(mol, isomericSmiles=True), Chem.MolToSmiles(mol3, isomericSmiles=True)) def testOldPropPickles(self): data = 'crdkit.Chem.rdchem\nMol\np0\n(S\'\\xef\\xbe\\xad\\xde\\x00\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00)\\x00\\x00\\x00-\\x00\\x00\\x00\\x80\\x01\\x06\\x00`\\x00\\x00\\x00\\x01\\x03\\x07\\x00`\\x00\\x00\\x00\\x02\\x01\\x06 4\\x00\\x00\\x00\\x01\\x01\\x04\\x06\\x00`\\x00\\x00\\x00\\x01\\x03\\x06\\x00(\\x00\\x00\\x00\\x03\\x04\\x08\\x00(\\x00\\x00\\x00\\x03\\x02\\x07\\x00h\\x00\\x00\\x00\\x03\\x02\\x01\\x06 4\\x00\\x00\\x00\\x02\\x01\\x04\\x06\\x00(\\x00\\x00\\x00\\x03\\x04\\x08\\x00(\\x00\\x00\\x00\\x03\\x02\\x07\\x00(\\x00\\x00\\x00\\x03\\x03\\x06\\x00`\\x00\\x00\\x00\\x02\\x02\\x06 4\\x00\\x00\\x00\\x01\\x01\\x04\\x08\\x00(\\x00\\x00\\x00\\x03\\x02\\x06@(\\x00\\x00\\x00\\x03\\x04\\x06@h\\x00\\x00\\x00\\x03\\x03\\x01\\x06@h\\x00\\x00\\x00\\x03\\x03\\x01\\x06@h\\x00\\x00\\x00\\x03\\x03\\x01\\x06@h\\x00\\x00\\x00\\x03\\x03\\x01\\x06@h\\x00\\x00\\x00\\x03\\x03\\x01\\x06\\x00`\\x00\\x00\\x00\\x02\\x02\\x06 4\\x00\\x00\\x00\\x01\\x01\\x04\\x06\\x00(\\x00\\x00\\x00\\x03\\x04\\x08\\x00(\\x00\\x00\\x00\\x03\\x02\\x07\\x00h\\x00\\x00\\x00\\x03\\x02\\x01\\x06 4\\x00\\x00\\x00\\x02\\x01\\x04\\x06\\x00`\\x00\\x00\\x00\\x02\\x02\\x06\\x00`\\x00\\x00\\x00\\x02\\x02\\x06\\x00`\\x00\\x00\\x00\\x02\\x02\\x06@(\\x00\\x00\\x00\\x03\\x04\\x06@h\\x00\\x00\\x00\\x03\\x03\\x01\\x06@h\\x00\\x00\\x00\\x03\\x03\\x01\\x06@h\\x00\\x00\\x00\\x03\\x03\\x01\\x06@h\\x00\\x00\\x00\\x03\\x03\\x01\\x06@(\\x00\\x00\\x00\\x03\\x04\\x06\\x00`\\x00\\x00\\x00\\x03\\x01\\x06\\x00`\\x00\\x00\\x00\\x02\\x02\\x06\\x00`\\x00\\x00\\x00\\x02\\x02\\x06\\x00`\\x00\\x00\\x00\\x02\\x02\\x06\\x00`\\x00\\x00\\x00\\x02\\x02\\x06\\x00`\\x00\\x00\\x00\\x02\\x02\\x0b\\x00\\x01\\x00\\x01\\x02\\x00\\x02\\x03\\x00\\x02\\x04\\x00\\x04\\x05(\\x02\\x04\\x06 \\x06\\x07\\x00\\x07\\x08\\x00\\x08\\t(\\x02\\x08\\n \\n\\x0b\\x00\\x0b\\x0c\\x00\\x0c\\r\\x00\\r\\x0e \\x0e\\x0fh\\x0c\\x0f\\x10h\\x0c\\x10\\x11h\\x0c\\x11\\x12h\\x0c\\x12\\x13h\\x0c\\x0c\\x14\\x00\\x14\\x15\\x00\\x15\\x16\\x00\\x16\\x17(\\x02\\x16\\x18 \\x18\\x19\\x00\\x19\\x1a\\x00\\x1a\\x1b\\x00\\x1b\\x1c\\x00\\x1c\\x1d\\x00\\x1d\\x1eh\\x0c\\x1e\\x1fh\\x0c\\x1f h\\x0c !h\\x0c!"h\\x0c\\x07#\\x00#$\\x00$%\\x00%&\\x00&\\\'\\x00\\\'(\\x00\\x15\\n\\x00"\\x19\\x00(#\\x00\\x13\\x0eh\\x0c"\\x1dh\\x0c\\x14\\x05\\x05\\x0b\\n\\x15\\x14\\x0c\\x06\\x0f\\x10\\x11\\x12\\x13\\x0e\\x06\\x1a\\x1b\\x1c\\x1d"\\x19\\x06\\x1e\\x1f !"\\x1d\\x06$%&\\\'(#\\x17\\x00\\x00\\x00\\x00\\x12\\x03\\x00\\x00\\x00\\x07\\x00\\x00\\x00numArom\\x01\\x02\\x00\\x00\\x00\\x0f\\x00\\x00\\x00_StereochemDone\\x01\\x01\\x00\\x00\\x00\\x03\\x00\\x00\\x00foo\\x00\\x03\\x00\\x00\\x00bar\\x13:\\x02\\x00\\x00\\x00\\x08\\x00\\x00\\x00_CIPRank\\x02\\x12\\x00\\x00\\x00\\x05\\x00\\x00\\x00myidx\\x00\\x01\\x00\\x00\\x000\\x02\\x00\\x00\\x00\\x08\\x00\\x00\\x00_CIPRank\\x02\\x1d\\x00\\x00\\x00\\x05\\x00\\x00\\x00myidx\\x00\\x01\\x00\\x00\\x001\\x04\\x00\\x00\\x00\\x08\\x00\\x00\\x00_CIPRank\\x02\\x15\\x00\\x00\\x00\\x12\\x00\\x00\\x00_ChiralityPossible\\x01\\x01\\x00\\x00\\x00\\x08\\x00\\x00\\x00_CIPCode\\x00\\x01\\x00\\x00\\x00S\\x05\\x00\\x00\\x00myidx\\x00\\x01\\x00\\x00\\x002\\x02\\x00\\x00\\x00\\x08\\x00\\x00\\x00_CIPRank\\x02\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00myidx\\x00\\x01\\x00\\x00\\x003\\x02\\x00\\x00\\x00\\x08\\x00\\x00\\x00_CIPRank\\x02\\x1a\\x00\\x00\\x00\\x05\\x00\\x00\\x00myidx\\x00\\x01\\x00\\x00\\x004\\x02\\x00\\x00\\x00\\x08\\x00\\x00\\x00_CIPRank\\x02"\\x00\\x00\\x00\\x05\\x00\\x00\\x00myidx\\x00\\x01\\x00\\x00\\x005\\x02\\x00\\x00\\x00\\x08\\x00\\x00\\x00_CIPRank\\x02\\x1f\\x00\\x00\\x00\\x05\\x00\\x00\\x00myidx\\x00\\x01\\x00\\x00\\x006\\x04\\x00\\x00\\x00\\x08\\x00\\x00\\x00_CIPRank\\x02\\x16\\x00\\x00\\x00\\x12\\x00\\x00\\x00_ChiralityPossible\\x01\\x01\\x00\\x00\\x00\\x08\\x00\\x00\\x00_CIPCode\\x00\\x01\\x00\\x00\\x00S\\x05\\x00\\x00\\x00myidx\\x00\\x01\\x00\\x00\\x007\\x02\\x00\\x00\\x00\\x08\\x00\\x00\\x00_CIPRank\\x02\\x1c\\x00\\x00\\x00\\x05\\x00\\x00\\x00myidx\\x00\\x01\\x00\\x00\\x008\\x02\\x00\\x00\\x00\\x08\\x00\\x00\\x00_CIPRank\\x02$\\x00\\x00\\x00\\x05\\x00\\x00\\x00myidx\\x00\\x01\\x00\\x00\\x009\\x02\\x00\\x00\\x00\\x08\\x00\\x00\\x00_CIPRank\\x02 \\x00\\x00\\x00\\x05\\x00\\x00\\x00myidx\\x00\\x02\\x00\\x00\\x0010\\x02\\x00\\x00\\x00\\x08\\x00\\x00\\x00_CIPRank\\x02\\x13\\x00\\x00\\x00\\x05\\x00\\x00\\x00myidx\\x00\\x02\\x00\\x00\\x0011\\x04\\x00\\x00\\x00\\x08\\x00\\x00\\x00_CIPRank\\x02\\x18\\x00\\x00\\x00\\x12\\x00\\x00\\x00_ChiralityPossible\\x01\\x01\\x00\\x00\\x00\\x08\\x00\\x00\\x00_CIPCode\\x00\\x01\\x00\\x00\\x00S\\x05\\x00\\x00\\x00myidx\\x00\\x02\\x00\\x00\\x0012\\x02\\x00\\x00\\x00\\x08\\x00\\x00\\x00_CIPRank\\x02!\\x00\\x00\\x00\\x05\\x00\\x00\\x00myidx\\x00\\x02\\x00\\x00\\x0013\\x02\\x00\\x00\\x00\\x08\\x00\\x00\\x00_CIPRank\\x02\\x19\\x00\\x00\\x00\\x05\\x00\\x00\\x00myidx\\x00\\x02\\x00\\x00\\x0014\\x02\\x00\\x00\\x00\\x08\\x00\\x00\\x00_CIPRank\\x02\\x0f\\x00\\x00\\x00\\x05\\x00\\x00\\x00myidx\\x00\\x02\\x00\\x00\\x0015\\x02\\x00\\x00\\x00\\x08\\x00\\x00\\x00_CIPRank\\x02\\x0b\\x00\\x00\\x00\\x05\\x00\\x00\\x00myidx\\x00\\x02\\x00\\x00\\x0016\\x02\\x00\\x00\\x00\\x08\\x00\\x00\\x00_CIPRank\\x02\\x08\\x00\\x00\\x00\\x05\\x00\\x00\\x00myidx\\x00\\x02\\x00\\x00\\x0017\\x02\\x00\\x00\\x00\\x08\\x00\\x00\\x00_CIPRank\\x02\\x0b\\x00\\x00\\x00\\x05\\x00\\x00\\x00myidx\\x00\\x02\\x00\\x00\\x0018\\x02\\x00\\x00\\x00\\x08\\x00\\x00\\x00_CIPRank\\x02\\x0f\\x00\\x00\\x00\\x05\\x00\\x00\\x00myidx\\x00\\x02\\x00\\x00\\x0019\\x02\\x00\\x00\\x00\\x08\\x00\\x00\\x00_CIPRank\\x02\\x07\\x00\\x00\\x00\\x05\\x00\\x00\\x00myidx\\x00\\x02\\x00\\x00\\x0020\\x04\\x00\\x00\\x00\\x08\\x00\\x00\\x00_CIPRank\\x02\\x17\\x00\\x00\\x00\\x12\\x00\\x00\\x00_ChiralityPossible\\x01\\x01\\x00\\x00\\x00\\x08\\x00\\x00\\x00_CIPCode\\x00\\x01\\x00\\x00\\x00S\\x05\\x00\\x00\\x00myidx\\x00\\x02\\x00\\x00\\x0021\\x02\\x00\\x00\\x00\\x08\\x00\\x00\\x00_CIPRank\\x02\\x1b\\x00\\x00\\x00\\x05\\x00\\x00\\x00myidx\\x00\\x02\\x00\\x00\\x0022\\x02\\x00\\x00\\x00\\x08\\x00\\x00\\x00_CIPRank\\x02#\\x00\\x00\\x00\\x05\\x00\\x00\\x00myidx\\x00\\x02\\x00\\x00\\x0023\\x02\\x00\\x00\\x00\\x08\\x00\\x00\\x00_CIPRank\\x02\\x1e\\x00\\x00\\x00\\x05\\x00\\x00\\x00myidx\\x00\\x02\\x00\\x00\\x0024\\x04\\x00\\x00\\x00\\x08\\x00\\x00\\x00_CIPRank\\x02\\x14\\x00\\x00\\x00\\x12\\x00\\x00\\x00_ChiralityPossible\\x01\\x01\\x00\\x00\\x00\\x08\\x00\\x00\\x00_CIPCode\\x00\\x01\\x00\\x00\\x00R\\x05\\x00\\x00\\x00myidx\\x00\\x02\\x00\\x00\\x0025\\x02\\x00\\x00\\x00\\x08\\x00\\x00\\x00_CIPRank\\x02\\x06\\x00\\x00\\x00\\x05\\x00\\x00\\x00myidx\\x00\\x02\\x00\\x00\\x0026\\x02\\x00\\x00\\x00\\x08\\x00\\x00\\x00_CIPRank\\x02\\x03\\x00\\x00\\x00\\x05\\x00\\x00\\x00myidx\\x00\\x02\\x00\\x00\\x0027\\x02\\x00\\x00\\x00\\x08\\x00\\x00\\x00_CIPRank\\x02\\x05\\x00\\x00\\x00\\x05\\x00\\x00\\x00myidx\\x00\\x02\\x00\\x00\\x0028\\x02\\x00\\x00\\x00\\x08\\x00\\x00\\x00_CIPRank\\x02\\x10\\x00\\x00\\x00\\x05\\x00\\x00\\x00myidx\\x00\\x02\\x00\\x00\\x0029\\x02\\x00\\x00\\x00\\x08\\x00\\x00\\x00_CIPRank\\x02\\x0c\\x00\\x00\\x00\\x05\\x00\\x00\\x00myidx\\x00\\x02\\x00\\x00\\x0030\\x02\\x00\\x00\\x00\\x08\\x00\\x00\\x00_CIPRank\\x02\\t\\x00\\x00\\x00\\x05\\x00\\x00\\x00myidx\\x00\\x02\\x00\\x00\\x0031\\x02\\x00\\x00\\x00\\x08\\x00\\x00\\x00_CIPRank\\x02\\n\\x00\\x00\\x00\\x05\\x00\\x00\\x00myidx\\x00\\x02\\x00\\x00\\x0032\\x02\\x00\\x00\\x00\\x08\\x00\\x00\\x00_CIPRank\\x02\\r\\x00\\x00\\x00\\x05\\x00\\x00\\x00myidx\\x00\\x02\\x00\\x00\\x0033\\x02\\x00\\x00\\x00\\x08\\x00\\x00\\x00_CIPRank\\x02\\x11\\x00\\x00\\x00\\x05\\x00\\x00\\x00myidx\\x00\\x02\\x00\\x00\\x0034\\x02\\x00\\x00\\x00\\x08\\x00\\x00\\x00_CIPRank\\x02\\x0e\\x00\\x00\\x00\\x05\\x00\\x00\\x00myidx\\x00\\x02\\x00\\x00\\x0035\\x02\\x00\\x00\\x00\\x08\\x00\\x00\\x00_CIPRank\\x02\\x04\\x00\\x00\\x00\\x05\\x00\\x00\\x00myidx\\x00\\x02\\x00\\x00\\x0036\\x02\\x00\\x00\\x00\\x08\\x00\\x00\\x00_CIPRank\\x02\\x02\\x00\\x00\\x00\\x05\\x00\\x00\\x00myidx\\x00\\x02\\x00\\x00\\x0037\\x02\\x00\\x00\\x00\\x08\\x00\\x00\\x00_CIPRank\\x02\\x01\\x00\\x00\\x00\\x05\\x00\\x00\\x00myidx\\x00\\x02\\x00\\x00\\x0038\\x02\\x00\\x00\\x00\\x08\\x00\\x00\\x00_CIPRank\\x02\\x02\\x00\\x00\\x00\\x05\\x00\\x00\\x00myidx\\x00\\x02\\x00\\x00\\x0039\\x02\\x00\\x00\\x00\\x08\\x00\\x00\\x00_CIPRank\\x02\\x04\\x00\\x00\\x00\\x05\\x00\\x00\\x00myidx\\x00\\x02\\x00\\x00\\x0040\\x13\\x16\'\np1\ntp2\nRp3\n.' import pickle # bonds were broken in v1 m2 = pickle.loads(data.encode("utf-8"), encoding='bytes') self.assertEqual(m2.GetProp("foo"), "bar") for atom in m2.GetAtoms(): self.assertEqual(atom.GetProp("myidx"), str(atom.GetIdx())) self.assertEqual( Chem.MolToSmiles(m2, True), Chem.MolToSmiles( Chem.MolFromSmiles( "CN[C@@H](C)C(=O)N[C@H](C(=O)N1C[C@@H](Oc2ccccc2)C[C@H]1C(=O)N[C@@H]1CCCc2ccccc21)C1CCCCC1" ), True)) def testGithub1461(self): # this is simple, it should throw a precondition and not seg fault m = Chem.RWMol() try: m.AddBond(0, 1, Chem.BondType.SINGLE) self.assertFalse(True) # shouldn't get here except RuntimeError: pass def testMolBundles1(self): b = Chem.MolBundle() smis = ('CC(Cl)(F)CC(F)(Br)', 'C[C@](Cl)(F)C[C@H](F)(Br)', 'C[C@](Cl)(F)C[C@@H](F)(Br)') for smi in smis: b.AddMol(Chem.MolFromSmiles(smi)) self.assertEqual(len(b), 3) self.assertEqual(b.Size(), 3) self.assertRaises(IndexError, lambda: b[4]) self.assertEqual(Chem.MolToSmiles(b[1], isomericSmiles=True), Chem.MolToSmiles(Chem.MolFromSmiles(smis[1]), isomericSmiles=True)) self.assertTrue(b.HasSubstructMatch(Chem.MolFromSmiles('CC(Cl)(F)CC(F)(Br)'), useChirality=True)) self.assertTrue( b.HasSubstructMatch(Chem.MolFromSmiles('C[C@](Cl)(F)C[C@@H](F)(Br)'), useChirality=True)) self.assertTrue( b.HasSubstructMatch(Chem.MolFromSmiles('C[C@@](Cl)(F)C[C@@H](F)(Br)'), useChirality=False)) self.assertFalse( b.HasSubstructMatch(Chem.MolFromSmiles('C[C@@](Cl)(F)C[C@@H](F)(Br)'), useChirality=True)) self.assertEqual( len(b.GetSubstructMatch(Chem.MolFromSmiles('CC(Cl)(F)CC(F)(Br)'), useChirality=True)), 8) self.assertEqual( len(b.GetSubstructMatch(Chem.MolFromSmiles('C[C@](Cl)(F)C[C@@H](F)(Br)'), useChirality=True)), 8) self.assertEqual( len(b.GetSubstructMatch(Chem.MolFromSmiles('C[C@@](Cl)(F)C[C@@H](F)(Br)'), useChirality=False)), 8) self.assertEqual( len(b.GetSubstructMatch(Chem.MolFromSmiles('C[C@@](Cl)(F)C[C@@H](F)(Br)'), useChirality=True)), 0) self.assertEqual( len(b.GetSubstructMatches(Chem.MolFromSmiles('CC(Cl)(F)CC(F)(Br)'), useChirality=True)), 1) self.assertEqual( len(b.GetSubstructMatches(Chem.MolFromSmiles('C[C@](Cl)(F)C[C@@H](F)(Br)'), useChirality=True)), 1) self.assertEqual( len( b.GetSubstructMatches(Chem.MolFromSmiles('C[C@@](Cl)(F)C[C@@H](F)(Br)'), useChirality=False)), 1) self.assertEqual( len( b.GetSubstructMatches(Chem.MolFromSmiles('C[C@@](Cl)(F)C[C@@H](F)(Br)'), useChirality=True)), 0) self.assertEqual( len(b.GetSubstructMatches(Chem.MolFromSmiles('CC(Cl)(F)CC(F)(Br)'), useChirality=True)[0]), 8) self.assertEqual( len( b.GetSubstructMatches(Chem.MolFromSmiles('C[C@](Cl)(F)C[C@@H](F)(Br)'), useChirality=True)[0]), 8) self.assertEqual( len( b.GetSubstructMatches(Chem.MolFromSmiles('C[C@@](Cl)(F)C[C@@H](F)(Br)'), useChirality=False)[0]), 8) if Chem.MolBundleCanSerialize(): for b2 in (pickle.loads(pickle.dumps(b)), Chem.MolBundle(b.ToBinary())): self.assertEqual(len(b2), len(b)) for m, m2 in zip(b, b2): self.assertEqual(Chem.MolToSmiles(m), Chem.MolToSmiles(m2)) def testMolBundles2(self): b = Chem.MolBundle() smis = ('Fc1c(Cl)cccc1', 'Fc1cc(Cl)ccc1', 'Fc1ccc(Cl)cc1') for smi in smis: b.AddMol(Chem.MolFromSmiles(smi)) self.assertEqual(len(b), 3) self.assertEqual(b.Size(), 3) self.assertTrue(Chem.MolFromSmiles('Fc1c(Cl)cccc1').HasSubstructMatch(b)) self.assertTrue(Chem.MolFromSmiles('Fc1cc(Cl)ccc1').HasSubstructMatch(b)) self.assertTrue(Chem.MolFromSmiles('Fc1c(Cl)cccc1C').HasSubstructMatch(b)) self.assertTrue(Chem.MolFromSmiles('Fc1cc(Cl)ccc1C').HasSubstructMatch(b)) self.assertFalse(Chem.MolFromSmiles('Fc1c(Br)cccc1').HasSubstructMatch(b)) self.assertEqual(len(Chem.MolFromSmiles('Fc1c(Cl)cccc1').GetSubstructMatch(b)), 8) self.assertEqual(len(Chem.MolFromSmiles('Fc1c(Cl)cccc1').GetSubstructMatches(b)), 1) self.assertEqual(len(Chem.MolFromSmiles('Fc1c(Cl)cccc1').GetSubstructMatches(b)[0]), 8) self.assertEqual(len(Chem.MolFromSmiles('Fc1ccc(Cl)cc1').GetSubstructMatches(b)), 1) self.assertEqual( len(Chem.MolFromSmiles('Fc1ccc(Cl)cc1').GetSubstructMatches(b, uniquify=False)), 2) self.assertEqual(len(Chem.MolFromSmiles('Fc1c(C)cccc1').GetSubstructMatch(b)), 0) self.assertEqual(len(Chem.MolFromSmiles('Fc1c(C)cccc1').GetSubstructMatches(b)), 0) def testMolBundles3(self): smis = ('CCC', 'CCO', 'CCN') b = Chem.FixedMolSizeMolBundle() for smi in smis: b.AddMol(Chem.MolFromSmiles(smi)) self.assertEqual(len(b), 3) self.assertEqual(b.Size(), 3) with self.assertRaises(ValueError): b.AddMol(Chem.MolFromSmiles('CCCC')) b = Chem.MolBundle() for smi in smis: b.AddMol(Chem.MolFromSmiles(smi)) self.assertEqual(len(b), 3) self.assertEqual(b.Size(), 3) b.AddMol(Chem.MolFromSmiles('CCCC')) self.assertEqual(b.Size(), 4) def testGithub1622(self): nonaromatics = ( "C1=C[N]C=C1", # radicals are not two electron donors "O=C1C=CNC=C1", # exocyclic double bonds don't steal electrons "C1=CS(=O)C=C1", # not sure how to classify this example from the # OEChem docs "C1#CC=CC=C1" # benzyne # 5-membered heterocycles "C1=COC=C1", # furan "C1=CSC=C1", # thiophene "C1=CNC=C1", #pyrrole "C1=COC=N1", # oxazole "C1=CSC=N1", # thiazole "C1=CNC=N1", # imidazole "C1=CNN=C1", # pyrazole "C1=CON=C1", # isoxazole "C1=CSN=C1", # isothiazole "C1=CON=N1", # 1,2,3-oxadiazole "C1=CNN=N1", # 1,2,3-triazole "N1=CSC=N1", # 1,3,4-thiadiazole # not outside the second rows "C1=CC=C[Si]=C1", "C1=CC=CC=P1", # 5-membered heterocycles outside the second row "C1=C[Se]C=C1", 'C1=C[Te]C=C1') for smi in nonaromatics: m = Chem.MolFromSmiles(smi, sanitize=False) Chem.SanitizeMol(m, Chem.SANITIZE_ALL ^ Chem.SANITIZE_SETAROMATICITY) Chem.SetAromaticity(m, Chem.AROMATICITY_MDL) self.assertFalse(m.GetAtomWithIdx(0).GetIsAromatic()) aromatics = ( "C1=CC=CC=C1", # benzene, of course # hetrocyclics "N1=CC=CC=C1", # pyridine "N1=CC=CC=N1", # pyridazine "N1=CC=CN=C1", # pyrimidine "N1=CC=NC=C1", # pyrazine "N1=CN=CN=C1", # 1,3,5-triazine # polycyclic aromatics "C1=CC2=CC=CC=CC2=C1", # azulene "C1=CC=CC2=CC=CC=C12", "C1=CC2=CC=CC=CC=C12", "C1=CC=C2C(=C1)N=CC=N2", "C1=CN=CC2C=CC=CC1=2", "C1=CC=C2C(=C1)N=C3C=CC=CC3=N2", "C1=CN=NC2C=CC=CC1=2", # macrocycle aromatics "C1=CC=CC=CC=CC=C1", "C1=CC=CC=CC=CC=CC=CC=CC=CC=C1", "N1=CN=NC=CC=CC=CC=CC=CC=CC=CC=CC=CC=CC=CC=CC=C1") for smi in aromatics: m = Chem.MolFromSmiles(smi, sanitize=False) Chem.SanitizeMol(m, Chem.SANITIZE_ALL ^ Chem.SANITIZE_SETAROMATICITY) Chem.SetAromaticity(m, Chem.AROMATICITY_MDL) self.assertTrue(m.GetAtomWithIdx(0).GetIsAromatic()) def testMolBlockChirality(self): m = Chem.MolFromSmiles('C[C@H](Cl)Br') mb = Chem.MolToMolBlock(m) m2 = Chem.MolFromMolBlock(mb) csmi1 = Chem.MolToSmiles(m, isomericSmiles=True) csmi2 = Chem.MolToSmiles(m2, isomericSmiles=True) self.assertEqual(csmi1, csmi2) def testIssue1735(self): # this shouldn't seg fault... m = Chem.RWMol() ranks = Chem.CanonicalRankAtoms(m, breakTies=False) ranks = Chem.CanonicalRankAtoms(m, breakTies=True) def testGithub1615(self): mb = """Issue399a.mol ChemDraw04050615582D 4 4 0 0 0 0 0 0 0 0999 V2000 -0.7697 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.0553 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.7697 0.4125 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.7697 -0.4125 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 2 1 1 0 2 3 1 0 3 4 1 0 2 4 1 0 M END""" m = Chem.MolFromMolBlock(mb) self.assertFalse(m.GetAtomWithIdx(1).HasProp("_CIPCode")) self.assertEqual(m.GetBondWithIdx(0).GetBondDir(), Chem.BondDir.NONE) self.assertEqual(m.GetAtomWithIdx(1).GetChiralTag(), Chem.ChiralType.CHI_UNSPECIFIED) m.GetAtomWithIdx(1).SetChiralTag(Chem.ChiralType.CHI_TETRAHEDRAL_CW) Chem.AssignStereochemistry(m, force=True) self.assertTrue(m.GetAtomWithIdx(1).HasProp("_CIPCode")) self.assertEqual(m.GetAtomWithIdx(1).GetProp("_CIPCode"), "S") self.assertEqual(m.GetBondWithIdx(0).GetBondDir(), Chem.BondDir.NONE) Chem.WedgeBond(m.GetBondWithIdx(0), 1, m.GetConformer()) self.assertEqual(m.GetBondWithIdx(0).GetBondDir(), Chem.BondDir.BEGINWEDGE) def testSmilesToAtom(self): a = Chem.AtomFromSmiles("C") self.assertEqual(a.GetAtomicNum(), 6) b = Chem.BondFromSmiles("=") self.assertEqual(b.GetBondType(), Chem.BondType.DOUBLE) a = Chem.AtomFromSmiles("error") self.assertIs(a, None) b = Chem.BondFromSmiles("d") self.assertIs(b, None) a = Chem.AtomFromSmarts("C") self.assertEqual(a.GetAtomicNum(), 6) b = Chem.BondFromSmarts("=") self.assertEqual(b.GetBondType(), Chem.BondType.DOUBLE) a = Chem.AtomFromSmarts("error") self.assertIs(a, None) b = Chem.BondFromSmarts("d") self.assertIs(b, None) def testSVGParsing(self): svg = """ NH Cl O OH m1 """ mol = Chem.MolFromRDKitSVG(svg) self.assertEqual(mol.GetNumAtoms(), 7) self.assertEqual(Chem.MolToSmiles(mol), 'CN[C@H](Cl)C(=O)O') svg2 = """ NH Cl O OH m1 """ mol = Chem.MolFromRDKitSVG(svg2) self.assertTrue(mol is None) with self.assertRaises(RuntimeError): mol = Chem.MolFromRDKitSVG("bad svg") def testAssignChiralTypesFromBondDirs(self): """ Just check to see that AssignChiralTypesFromBondDirs is wrapped. Critical tests of the underlying C++ function already exist in SD file reader tests. """ mol = Chem.MolFromSmiles('C(F)(Cl)Br') rdkit.Chem.rdDepictor.Compute2DCoords(mol) atom0 = mol.GetAtomWithIdx(0) self.assertEqual(atom0.GetChiralTag(), Chem.rdchem.ChiralType.CHI_UNSPECIFIED) bond = mol.GetBondBetweenAtoms(0, 1) bond.SetBondDir(Chem.rdchem.BondDir.BEGINWEDGE) Chem.AssignChiralTypesFromBondDirs(mol) self.assertEqual(atom0.GetChiralTag(), Chem.rdchem.ChiralType.CHI_TETRAHEDRAL_CCW) def testAssignStereochemistryFrom3D(self): def _stereoTester(mol, expectedTag, expectedStereo): Chem.AssignStereochemistryFrom3D(mol) self.assertEqual(mol.GetNumAtoms(), 9) self.assertEqual(mol.GetAtomWithIdx(1).GetChiralTag(), expectedTag) self.assertEqual(mol.GetBondWithIdx(3).GetStereo(), expectedStereo) origVal = Chem.GetUseLegacyStereoPerception() for useLegacy in (True, False): Chem.SetUseLegacyStereoPerception(useLegacy) fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'test_data', 'stereochem.sdf') suppl = Chem.SDMolSupplier(fileN, sanitize=False) if useLegacy: expected = ( (Chem.ChiralType.CHI_TETRAHEDRAL_CW, Chem.BondStereo.STEREOZ), (Chem.ChiralType.CHI_TETRAHEDRAL_CW, Chem.BondStereo.STEREOE), (Chem.ChiralType.CHI_TETRAHEDRAL_CCW, Chem.BondStereo.STEREOZ), (Chem.ChiralType.CHI_TETRAHEDRAL_CCW, Chem.BondStereo.STEREOE), ) else: expected = ( (Chem.ChiralType.CHI_TETRAHEDRAL_CW, Chem.BondStereo.STEREOCIS), (Chem.ChiralType.CHI_TETRAHEDRAL_CW, Chem.BondStereo.STEREOTRANS), (Chem.ChiralType.CHI_TETRAHEDRAL_CCW, Chem.BondStereo.STEREOCIS), (Chem.ChiralType.CHI_TETRAHEDRAL_CCW, Chem.BondStereo.STEREOTRANS), ) for i, mol in enumerate(suppl): cip, stereo = expected[i] _stereoTester(mol, cip, stereo) Chem.SetUseLegacyStereoPerception(origVal) def testGitHub2082(self): ctab = """ MJ150720 9 9 0 0 0 0 0 0 0 0999 V2000 2.5687 -0.7144 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.1562 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.5687 0.7144 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 1.3312 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.9187 -0.7144 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.0937 -0.7144 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.3187 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.0937 0.7144 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.9187 0.7144 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2 1 1 6 2 3 1 0 2 4 1 0 4 5 2 0 5 6 1 0 6 7 2 0 7 8 1 0 8 9 2 0 9 4 1 0 M END """ mol = Chem.MolFromMolBlock(ctab) self.assertFalse(mol.GetConformer().Is3D()) self.assertTrue("@" in Chem.MolToSmiles(mol, True)) def testGitHub2082_2(self): # test a mol block that lies is 3D but labelled 2D ofile = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'Wrap', 'test_data', 'issue2082.mol') with open(ofile) as inf: ctab = inf.read() m = Chem.MolFromMolBlock(ctab) self.assertTrue(m.GetConformer().Is3D()) def testSetQuery(self): from rdkit.Chem import rdqueries pat = Chem.MolFromSmarts("[C]") self.assertFalse(Chem.MolFromSmiles("c1ccccc1").HasSubstructMatch(pat)) q = rdqueries.AtomNumEqualsQueryAtom(6) for atom in pat.GetAtoms(): atom.SetQuery(q) self.assertTrue(Chem.MolFromSmiles("c1ccccc1").HasSubstructMatch(pat)) def testGetQueryType(self): query_a = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'query_A.mol') m = next(Chem.SDMolSupplier(query_a)) self.assertTrue(m.GetAtomWithIdx(6).HasQuery()) self.assertTrue(m.GetAtomWithIdx(6).GetQueryType() == "A") query_a_v3k = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'query_A.v3k.mol') m = next(Chem.SDMolSupplier(query_a_v3k)) self.assertTrue(m.GetAtomWithIdx(6).HasQuery()) self.assertTrue(m.GetAtomWithIdx(6).GetQueryType() == "A") query_q = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'query_Q.mol') m = next(Chem.SDMolSupplier(query_q)) self.assertTrue(m.GetAtomWithIdx(6).HasQuery()) self.assertTrue(m.GetAtomWithIdx(6).GetQueryType() == "Q") query_q_v3k = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'query_Q.v3k.mol') m = next(Chem.SDMolSupplier(query_q_v3k)) self.assertTrue(m.GetAtomWithIdx(6).HasQuery()) self.assertTrue(m.GetAtomWithIdx(6).GetQueryType() == "Q") m = Chem.MolFromSmiles("*CC") params = Chem.rdmolops.AdjustQueryParameters.NoAdjustments() params.makeDummiesQueries = True m = Chem.rdmolops.AdjustQueryProperties(m, params) self.assertTrue(m.GetAtomWithIdx(0).HasQuery()) self.assertTrue(m.GetAtomWithIdx(0).GetQueryType() == "") def testBondSetQuery(self): pat = Chem.MolFromSmarts('[#6]=[#6]') mol = Chem.MolFromSmiles("c1ccccc1") self.assertFalse(mol.HasSubstructMatch(pat)) pat2 = Chem.MolFromSmarts('C:C') for bond in pat.GetBonds(): bond.SetQuery(pat2.GetBondWithIdx(0)) self.assertTrue(mol.HasSubstructMatch(pat)) def testBondExpandQuery(self): pat = Chem.MolFromSmarts('C-C') mol = Chem.MolFromSmiles("C=C-C") self.assertEqual(len(mol.GetSubstructMatches(pat)), 1) pat2 = Chem.MolFromSmarts('C=C') for bond in pat.GetBonds(): bond.ExpandQuery(pat2.GetBondWithIdx(0), Chem.CompositeQueryType.COMPOSITE_OR) self.assertEqual(len(mol.GetSubstructMatches(pat)), 2) def testGitHub1985(self): # simple check, this used to throw an exception try: Chem.MolToSmarts(Chem.MolFromSmarts("[C@]")) except Exception: self.fail("[C@] caused an exception when roundtripping smarts") def testGetEnhancedStereo(self): rdbase = os.environ['RDBASE'] filename = os.path.join(rdbase, 'Code/GraphMol/FileParsers/test_data/two_centers_or.mol') m = Chem.MolFromMolFile(filename) sg = m.GetStereoGroups() self.assertEqual(len(sg), 2) group1 = sg[1] self.assertEqual(group1.GetGroupType(), Chem.StereoGroupType.STEREO_OR) stereo_atoms = group1.GetAtoms() self.assertEqual(len(stereo_atoms), 2) # file is 1 indexed and says 5 self.assertEqual(stereo_atoms[1].GetIdx(), 4) # no bonds here: stereo_bonds = group1.GetBonds() self.assertEqual(len(stereo_bonds), 0) # make sure the atoms are connected to the parent molecule stereo_atoms[1].SetProp("foo", "bar") self.assertTrue(m.GetAtomWithIdx(4).HasProp("foo")) # make sure that we can iterate over the atoms: for at in stereo_atoms: at.SetProp("foo2", "bar2") self.assertTrue(m.GetAtomWithIdx(at.GetIdx()).HasProp("foo2")) def testGetEnhancedStereoAtrop(self): m = Chem.MolFromSmiles('Cc1cccc(O)c1-c1c(C)cccc1F |wU:7.7,&1:7|') sg = m.GetStereoGroups() self.assertEqual(len(sg), 1) group1 = sg[0] self.assertEqual(group1.GetGroupType(), Chem.StereoGroupType.STEREO_AND) stereo_atoms = group1.GetAtoms() self.assertEqual(len(stereo_atoms), 0) stereo_bonds = group1.GetBonds() self.assertEqual(len(stereo_bonds), 1) self.assertEqual(stereo_bonds[0].GetIdx(), 7) def testEnhancedStereoPreservesMol(self): """ Check that the stereo group (and the atoms therein) preserve the lifetime of the associated mol. """ rdbase = os.environ['RDBASE'] filename = os.path.join(rdbase, 'Code/GraphMol/FileParsers/test_data/two_centers_or.mol') m = Chem.MolFromMolFile(filename) sg = m.GetStereoGroups() m = None gc.collect() self.assertEqual(len(sg), 2) group1 = sg[1] stereo_atoms = group1.GetAtoms() sg = None gc.collect() self.assertEqual(stereo_atoms[1].GetIdx(), 4) self.assertEqual(stereo_atoms[1].GetOwningMol().GetNumAtoms(), 8) def testSetEnhancedStereoGroup(self): m = Chem.MolFromSmiles('F[C@@H](Br)[C@H](F)Cl |o1:1|') m2 = Chem.RWMol(m) groups = m2.GetStereoGroups() self.assertEqual(len(groups), 1) # Can clear the StereoGroups by setting to an empty list m2.SetStereoGroups([]) self.assertEqual(len(m2.GetStereoGroups()), 0) # Can add new StereoGroups group1 = Chem.rdchem.CreateStereoGroup(Chem.rdchem.StereoGroupType.STEREO_OR, m2, [1]) m2.SetStereoGroups([group1]) self.assertEqual(len(m2.GetStereoGroups()), 1) #test creation of bond-only stereogroup m = Chem.MolFromSmiles('Cc1cccc(F)c1-c1c(C)cc([C@H](C)O)cc1Cl |wU:7.7|') m2 = Chem.RWMol(m) groups = m2.GetStereoGroups() self.assertEqual(len(groups), 0) #Creating new StereoGroup with no atoms or bonds should not be allowed try: group1 = Chem.rdchem.CreateStereoGroup(Chem.rdchem.StereoGroupType.STEREO_ABSOLUTE, m2, [], []) except ValueError: ok = 1 else: ok = 0 self.assertTrue(ok) # Can add new bond-only StereoGroups group1 = Chem.rdchem.CreateStereoGroup(Chem.rdchem.StereoGroupType.STEREO_ABSOLUTE, m2, [], [7]) m2.SetStereoGroups([group1]) self.assertEqual(len(m2.GetStereoGroups()), 1) #test creation of bond&atom stereogroup m = Chem.MolFromSmiles('Cc1cccc(F)c1-c1c(C)cc([C@H](C)O)cc1Cl |wU:7.7|') m2 = Chem.RWMol(m) groups = m2.GetStereoGroups() self.assertEqual(len(groups), 0) # Can add new atom&bond StereoGroup group1 = Chem.rdchem.CreateStereoGroup(Chem.rdchem.StereoGroupType.STEREO_ABSOLUTE, m2, [13], [7]) m2.SetStereoGroups([group1]) self.assertEqual(len(m2.GetStereoGroups()), 1) def testSetEnhancedStereoGroupOwnershipCheck(self): # make sure that the object returned by CreateStereoGroup() # preserves the owning molecule: m = Chem.RWMol(Chem.MolFromSmiles('F[C@@H](Br)[C@H](F)Cl')) group1 = Chem.rdchem.CreateStereoGroup(Chem.rdchem.StereoGroupType.STEREO_OR, m, [1]) m.SetStereoGroups([group1]) self.assertEqual(len(m.GetStereoGroups()), 1) m = None gc.collect() stereo_atoms = group1.GetAtoms() self.assertEqual(stereo_atoms[0].GetIdx(), 1) return self.assertEqual(stereo_atoms[0].GetOwningMol().GetNumAtoms(), 6) # make sure we can't add StereoGroups constructed from one molecule # to a different one: m2 = Chem.RWMol(Chem.MolFromSmiles('F[C@@H](Br)[C@H](F)Cl')) with self.assertRaises(ValueError): m2.SetStereoGroups([group1]) def testSetEnhancedStereoTypeChecking(self): m = Chem.RWMol(Chem.MolFromSmiles('F[C@@H](Br)[C@H](F)Cl')) # List or tuple should be allowed: group = Chem.rdchem.CreateStereoGroup(Chem.rdchem.StereoGroupType.STEREO_OR, m, [1, 3]) group = Chem.rdchem.CreateStereoGroup(Chem.rdchem.StereoGroupType.STEREO_OR, m, (1, 3)) # Python ValueError (range error) with index past the end with self.assertRaises(ValueError): group = Chem.rdchem.CreateStereoGroup(Chem.rdchem.StereoGroupType.STEREO_OR, m, [100]) # Mol is None with self.assertRaises(TypeError): group = Chem.rdchem.CreateStereoGroup(Chem.rdchem.StereoGroupType.STEREO_OR, None, [1]) # Atom indices must be numbers with self.assertRaises(TypeError): group = Chem.rdchem.CreateStereoGroup(Chem.rdchem.StereoGroupType.STEREO_OR, m, [1, 'text']) def testSubstructParameters(self): m = Chem.MolFromSmiles('C[C@](F)(Cl)OCC') p1 = Chem.MolFromSmiles('C[C@](F)(Cl)O') p2 = Chem.MolFromSmiles('C[C@@](F)(Cl)O') p3 = Chem.MolFromSmiles('CC(F)(Cl)O') ps = Chem.SubstructMatchParameters() self.assertTrue(m.HasSubstructMatch(p1, ps)) self.assertTrue(m.HasSubstructMatch(p2, ps)) self.assertTrue(m.HasSubstructMatch(p3, ps)) self.assertEqual(m.GetSubstructMatch(p1, ps), (0, 1, 2, 3, 4)) self.assertEqual(m.GetSubstructMatch(p2, ps), (0, 1, 2, 3, 4)) self.assertEqual(m.GetSubstructMatch(p3, ps), (0, 1, 2, 3, 4)) self.assertEqual(m.GetSubstructMatches(p1, ps), ((0, 1, 2, 3, 4), )) self.assertEqual(m.GetSubstructMatches(p2, ps), ((0, 1, 2, 3, 4), )) self.assertEqual(m.GetSubstructMatches(p3, ps), ((0, 1, 2, 3, 4), )) ps.useChirality = True self.assertTrue(m.HasSubstructMatch(p1, ps)) self.assertFalse(m.HasSubstructMatch(p2, ps)) self.assertTrue(m.HasSubstructMatch(p3, ps)) self.assertEqual(m.GetSubstructMatch(p1, ps), (0, 1, 2, 3, 4)) self.assertEqual(m.GetSubstructMatch(p2, ps), ()) self.assertEqual(m.GetSubstructMatch(p3, ps), (0, 1, 2, 3, 4)) self.assertEqual(m.GetSubstructMatches(p1, ps), ((0, 1, 2, 3, 4), )) self.assertEqual(m.GetSubstructMatches(p2, ps), ()) self.assertEqual(m.GetSubstructMatches(p3, ps), ((0, 1, 2, 3, 4), )) def testForwardEnhancedStereoGroupIds(self): m = Chem.MolFromSmiles('C[C@H](O)Cl |o5:1|') self.assertIsNotNone(m) # StereoGroup id is read, but not forwarded to "write id" stgs = m.GetStereoGroups() self.assertEqual(len(stgs), 1) self.assertEqual(stgs[0].GetGroupType(), Chem.StereoGroupType.STEREO_OR) self.assertEqual(stgs[0].GetReadId(), 5) self.assertEqual(stgs[0].GetWriteId(), 0) self.assertEqual(Chem.MolToCXSmiles(m), 'C[C@H](O)Cl |o1:1|') stgs[0].SetWriteId(7) self.assertEqual(stgs[0].GetWriteId(), 7) self.assertEqual(Chem.MolToCXSmiles(m), 'C[C@H](O)Cl |o7:1|') # ids are forwarded to copies of the mol m2 = Chem.RWMol(m) self.assertIsNotNone(m2) stgs2 = m2.GetStereoGroups() self.assertEqual(len(stgs), 1) self.assertEqual(stgs2[0].GetGroupType(), Chem.StereoGroupType.STEREO_OR) self.assertEqual(stgs2[0].GetReadId(), 5) self.assertEqual(stgs2[0].GetWriteId(), 7) self.assertEqual(Chem.MolToCXSmiles(m2), 'C[C@H](O)Cl |o7:1|') # Forwardings the ids overrides the WriteId Chem.ForwardStereoGroupIds(m) self.assertEqual(stgs[0].GetWriteId(), 5) self.assertEqual(Chem.MolToCXSmiles(m), 'C[C@H](O)Cl |o5:1|') # the copy mol is not affected self.assertEqual(stgs2[0].GetWriteId(), 7) def testSubstructParametersBundles(self): b = Chem.MolBundle() smis = ('C[C@](F)(Cl)O', 'C[C@](Br)(Cl)O', 'C[C@](I)(Cl)O') for smi in smis: b.AddMol(Chem.MolFromSmiles(smi)) self.assertEqual(len(b), 3) self.assertEqual(b.Size(), 3) ps = Chem.SubstructMatchParameters() ps.useChirality = True self.assertTrue(Chem.MolFromSmiles('C[C@](F)(Cl)OCC').HasSubstructMatch(b, ps)) self.assertFalse(Chem.MolFromSmiles('C[C@@](F)(Cl)OCC').HasSubstructMatch(b, ps)) self.assertTrue(Chem.MolFromSmiles('C[C@](I)(Cl)OCC').HasSubstructMatch(b, ps)) self.assertFalse(Chem.MolFromSmiles('C[C@@](I)(Cl)OCC').HasSubstructMatch(b, ps)) self.assertEqual( Chem.MolFromSmiles('C[C@](F)(Cl)OCC').GetSubstructMatch(b, ps), (0, 1, 2, 3, 4)) self.assertEqual(Chem.MolFromSmiles('C[C@@](F)(Cl)OCC').GetSubstructMatch(b, ps), ()) self.assertEqual( Chem.MolFromSmiles('C[C@](I)(Cl)OCC').GetSubstructMatch(b, ps), (0, 1, 2, 3, 4)) self.assertEqual(Chem.MolFromSmiles('C[C@@](I)(Cl)OCC').GetSubstructMatch(b, ps), ()) self.assertEqual( Chem.MolFromSmiles('C[C@](F)(Cl)OCC').GetSubstructMatches(b, ps), ((0, 1, 2, 3, 4), )) self.assertEqual(Chem.MolFromSmiles('C[C@@](F)(Cl)OCC').GetSubstructMatches(b, ps), ()) self.assertEqual( Chem.MolFromSmiles('C[C@](I)(Cl)OCC').GetSubstructMatches(b, ps), ((0, 1, 2, 3, 4), )) self.assertEqual(Chem.MolFromSmiles('C[C@@](I)(Cl)OCC').GetSubstructMatches(b, ps), ()) def testSubstructParametersBundles2(self): b = Chem.MolBundle() smis = ('C[C@](F)(Cl)O', 'C[C@](Br)(Cl)O', 'C[C@](I)(Cl)O') for smi in smis: b.AddMol(Chem.MolFromSmiles(smi)) self.assertEqual(len(b), 3) b2 = Chem.MolBundle() smis = ('C[C@@](F)(Cl)O', 'C[C@@](Br)(Cl)O', 'C[C@@](I)(Cl)O') for smi in smis: b2.AddMol(Chem.MolFromSmiles(smi)) self.assertEqual(len(b2), 3) ps = Chem.SubstructMatchParameters() ps.useChirality = True self.assertTrue(b.HasSubstructMatch(b, ps)) self.assertFalse(b.HasSubstructMatch(b2, ps)) self.assertFalse(b2.HasSubstructMatch(b, ps)) self.assertEqual(b.GetSubstructMatch(b, ps), (0, 1, 2, 3, 4)) self.assertEqual(b.GetSubstructMatch(b2, ps), ()) self.assertEqual(b2.GetSubstructMatch(b, ps), ()) self.assertEqual(b.GetSubstructMatches(b, ps), ((0, 1, 2, 3, 4), )) self.assertEqual(b.GetSubstructMatches(b2, ps), ()) self.assertEqual(b2.GetSubstructMatches(b, ps), ()) def testSubstructMatchAtomProperties(self): m = Chem.MolFromSmiles("CCCCCCCCC") query = Chem.MolFromSmiles("CCC") m.GetAtomWithIdx(0).SetProp("test_prop", "1") query.GetAtomWithIdx(0).SetProp("test_prop", "1") ps = Chem.SubstructMatchParameters() ps.atomProperties = ["test_prop"] self.assertEqual(len(m.GetSubstructMatches(query)), 7) self.assertEqual(len(m.GetSubstructMatches(query, ps)), 1) # more than one property works as well m.GetAtomWithIdx(1).SetProp("test_prop2", "1") query.GetAtomWithIdx(1).SetProp("test_prop2", "1") ps.atomProperties = ["test_prop", "test_prop2"] self.assertEqual(len(m.GetSubstructMatches(query, ps)), 1) def testSubstructMatchBondProperties(self): m = Chem.MolFromSmiles("CCCCCCCCC") query = Chem.MolFromSmiles("CCC") m.GetBondWithIdx(0).SetProp("test_prop", "1") query.GetBondWithIdx(0).SetProp("test_prop", "1") ps = Chem.SubstructMatchParameters() ps.bondProperties = ["test_prop"] self.assertEqual(len(m.GetSubstructMatches(query)), 7) self.assertEqual(len(m.GetSubstructMatches(query, ps)), 1) # more than one property works as well m.GetBondWithIdx(1).SetProp("test_prop2", "1") query.GetBondWithIdx(1).SetProp("test_prop2", "1") ps.bondProperties = ["test_prop", "test_prop2"] self.assertEqual(len(m.GetSubstructMatches(query, ps)), 1) # atom and bond properties work together m.GetAtomWithIdx(0).SetProp("test_prop", "1") query.GetAtomWithIdx(0).SetProp("test_prop", "1") ps.atomProperties = ["test_prop"] self.assertEqual(len(m.GetSubstructMatches(query, ps)), 1) def testGithub2285(self): fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'github2285.sdf') supp = Chem.ForwardSDMolSupplier(fileN, removeHs=False) if hasattr(supp, "__next__"): self.assertTrue(supp.__next__() is not None) else: self.assertTrue(supp.next() is not None) def testBitVectProp(self): bv = DataStructs.ExplicitBitVect(100) m = Chem.MolFromSmiles("CC") for atom in m.GetAtoms(): bv.SetBit(atom.GetIdx()) atom.SetExplicitBitVectProp("prop", bv) for atom in m.GetAtoms(): bv = atom.GetExplicitBitVectProp("prop") self.assertTrue(bv.GetBit(atom.GetIdx())) def testBitVectQuery(self): bv = DataStructs.ExplicitBitVect(4) bv.SetBit(0) bv.SetBit(2) # wow, what a mouthfull.. qa = rdqueries.HasBitVectPropWithValueQueryAtom("prop", bv, tolerance=0.0) m = Chem.MolFromSmiles("CC") for atom in m.GetAtoms(): if atom.GetIdx() == 0: atom.SetExplicitBitVectProp("prop", bv) l = tuple([x.GetIdx() for x in m.GetAtomsMatchingQuery(qa)]) self.assertEqual(l, (0, )) m = Chem.MolFromSmiles("CC") for atom in m.GetAtoms(): bv = DataStructs.ExplicitBitVect(4) bv.SetBit(atom.GetIdx()) atom.SetExplicitBitVectProp("prop", bv) sma = Chem.MolFromSmarts("C") for atom in sma.GetAtoms(): bv = DataStructs.ExplicitBitVect(4) bv.SetBit(1) qa = rdqueries.HasBitVectPropWithValueQueryAtom("prop", bv, tolerance=0.0) atom.ExpandQuery(qa) res = m.GetSubstructMatches(sma) self.assertEqual(res, ((1, ), )) sma = Chem.MolFromSmarts("C") for atom in sma.GetAtoms(): bv = DataStructs.ExplicitBitVect(4) bv.SetBit(0) qa = rdqueries.HasBitVectPropWithValueQueryAtom("prop", bv, tolerance=0.0) atom.ExpandQuery(qa) res = m.GetSubstructMatches(sma) self.assertEqual(res, ((0, ), )) sma = Chem.MolFromSmarts("C") for atom in sma.GetAtoms(): bv = DataStructs.ExplicitBitVect(4) bv.SetBit(0) qa = rdqueries.HasBitVectPropWithValueQueryAtom("prop", bv, tolerance=1.0) atom.ExpandQuery(qa) res = m.GetSubstructMatches(sma) self.assertEqual(res, ((0, ), (1, ))) def testGithub2441(self): m = Chem.MolFromSmiles("CC") conf = Chem.Conformer(2) m.AddConformer(conf, assignId=False) m.GetConformer().SetIntProp("foo", 1) m.GetConformer().SetProp("bar", "foo") self.assertTrue(m.GetConformer().HasProp("foo")) self.assertFalse(m.GetConformer().HasProp("food")) d = m.GetConformer().GetPropsAsDict() self.assertTrue('foo' in d) self.assertTrue('bar' in d) self.assertEqual(d['bar'], 'foo') self.assertEqual(m.GetConformer().GetProp("bar"), "foo") self.assertEqual(m.GetConformer().GetIntProp("foo"), 1) def testGithub2479(self): # Chemistry failure in last entry smi2 = '''c1ccccc duff c1ccccc1 ok c1ccncc1 pyridine C(C garbage C1CC1 ok2 C1C(Cl)C1 ok3 CC(C)(C)(C)C duff2 ''' suppl2 = Chem.SmilesMolSupplier() suppl2.SetData(smi2, titleLine=False, nameColumn=1) l = [x for x in suppl2] self.assertEqual(len(l), 7) self.assertTrue(l[6] is None) # SMILES failure in last entry smi2 = '''c1ccccc duff c1ccccc1 ok c1ccncc1 pyridine C(C garbage C1CC1 ok2 C1C(Cl)C1 ok3 C1C(Cl)CCCC duff2 ''' suppl2 = Chem.SmilesMolSupplier() suppl2.SetData(smi2, titleLine=False, nameColumn=1) l = [x for x in suppl2] self.assertEqual(len(l), 7) self.assertTrue(l[6] is None) sdf = b""" Mrv1810 06051911332D 3 2 0 0 0 0 999 V2000 -13.3985 4.9850 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -12.7066 5.4343 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 -12.0654 4.9151 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 2 3 1 0 0 0 0 M END $$$$ Mrv1810 06051911332D 3 2 0 0 0 0 999 V2000 -10.3083 4.8496 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -9.6408 5.3345 0.0000 F 0 0 0 0 0 0 0 0 0 0 0 0 -9.0277 4.7825 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 2 3 1 0 0 0 0 M END $$$$ Mrv1810 06051911332D 3 2 0 0 0 0 999 V2000 -10.3083 4.8496 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -9.6""" suppl3 = Chem.SDMolSupplier() suppl3.SetData(sdf) l = [x for x in suppl3] self.assertEqual(len(l), 3) self.assertTrue(l[1] is None) self.assertTrue(l[2] is None) sio = BytesIO(sdf) suppl3 = Chem.ForwardSDMolSupplier(sio) l = [x for x in suppl3] self.assertEqual(len(l), 3) self.assertTrue(l[1] is None) self.assertTrue(l[2] is None) sdf = b""" Mrv1810 06051911332D 3 2 0 0 0 0 999 V2000 -13.3985 4.9850 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -12.7066 5.4343 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 -12.0654 4.9151 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 2 3 1 0 0 0 0 M END > (1) [1,2,] $$$$ Mrv1810 06051911332D 3 2 0 0 0 0 999 V2000 -10.3083 4.8496 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -9.6408 5.3345 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 -9.0277 4.7825 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 2 3 1 0 0 0 0 M END > (1) [1,2,] """ suppl3 = Chem.SDMolSupplier() suppl3.SetData(sdf) l = [x for x in suppl3] self.assertEqual(len(l), 2) self.assertTrue(l[0] is not None) self.assertTrue(l[1] is not None) sio = BytesIO(sdf) suppl3 = Chem.ForwardSDMolSupplier(sio) l = [x for x in suppl3] self.assertEqual(len(l), 2) self.assertTrue(l[0] is not None) self.assertTrue(l[1] is not None) def testCMLWriter(self): self.maxDiff = None # XXX conf = Chem.Conformer(11) conf.SetAtomPosition(0, [-0.95330, 0.60416, 1.01609]) conf.SetAtomPosition(1, [-1.00832, 1.68746, 0.83520]) conf.SetAtomPosition(2, [-1.96274, 0.16103, 0.94471]) conf.SetAtomPosition(3, [-0.57701, 0.44737, 2.04167]) conf.SetAtomPosition(4, [0.00000, 0.00000, 0.00000]) conf.SetAtomPosition(5, [-0.43038, 0.18596, -1.01377]) conf.SetAtomPosition(6, [0.22538, -1.36531, 0.19373]) conf.SetAtomPosition(7, [1.21993, -1.33937, 0.14580]) conf.SetAtomPosition(8, [1.38490, 0.73003, 0.00000]) conf.SetAtomPosition(9, [1.38490, 1.96795, 0.00000]) conf.SetAtomPosition(10, [2.35253, -0.07700, 0.00000]) emol = Chem.EditableMol(Chem.Mol()) for z in [6, 1, 1, 1, 6, 1, 8, 1, 6, 8, 8]: emol.AddAtom(Chem.Atom(z)) emol.AddBond(0, 1, Chem.BondType.SINGLE) emol.AddBond(0, 2, Chem.BondType.SINGLE) emol.AddBond(0, 3, Chem.BondType.SINGLE) emol.AddBond(0, 4, Chem.BondType.SINGLE) emol.AddBond(4, 5, Chem.BondType.SINGLE) emol.AddBond(4, 6, Chem.BondType.SINGLE) emol.AddBond(4, 8, Chem.BondType.SINGLE) emol.AddBond(6, 7, Chem.BondType.SINGLE) emol.AddBond(8, 9, Chem.BondType.DOUBLE) emol.AddBond(8, 10, Chem.BondType.SINGLE) mol = emol.GetMol() mol.SetProp('_Name', 'S-lactic acid') mol.AddConformer(conf) mol.GetAtomWithIdx(7).SetIsotope(2) mol.GetAtomWithIdx(10).SetFormalCharge(-1) mol.GetAtomWithIdx(4).SetChiralTag(Chem.ChiralType.CHI_TETRAHEDRAL_CCW) cmlblock_expected = """ S-lactic acid 1 """ self.assertEqual(Chem.MolToCMLBlock(mol), cmlblock_expected) def testXYZ(self): conf = Chem.Conformer(5) conf.SetAtomPosition(0, [0.000, 0.000, 0.000]) conf.SetAtomPosition(1, [-0.635, -0.635, 0.635]) conf.SetAtomPosition(2, [-0.635, 0.635, -0.635]) conf.SetAtomPosition(3, [0.635, -0.635, -0.635]) conf.SetAtomPosition(4, [0.635, 0.635, 0.635]) emol = Chem.EditableMol(Chem.Mol()) for z in [6, 1, 1, 1, 1]: emol.AddAtom(Chem.Atom(z)) mol = emol.GetMol() mol.SetProp('_Name', 'methane\nthis part should not be output') mol.AddConformer(conf) xyzblock_expected = """5 methane C 0.000000 0.000000 0.000000 H -0.635000 -0.635000 0.635000 H -0.635000 0.635000 -0.635000 H 0.635000 -0.635000 -0.635000 H 0.635000 0.635000 0.635000 """ self.assertEqual(Chem.MolToXYZBlock(mol), xyzblock_expected) def testSanitizationExceptionBasics(self): try: Chem.SanitizeMol(Chem.MolFromSmiles('CFC', sanitize=False)) except Chem.AtomValenceException as exc: self.assertEqual(exc.cause.GetAtomIdx(), 1) else: self.assertFalse(True) try: Chem.SanitizeMol(Chem.MolFromSmiles('c1cc1', sanitize=False)) except Chem.KekulizeException as exc: self.assertEqual(exc.cause.GetAtomIndices(), (0, 1, 2)) else: self.assertFalse(True) def testSanitizationExceptionHierarchy(self): with self.assertRaises(Chem.AtomValenceException): Chem.SanitizeMol(Chem.MolFromSmiles('CFC', sanitize=False)) with self.assertRaises(Chem.AtomSanitizeException): Chem.SanitizeMol(Chem.MolFromSmiles('CFC', sanitize=False)) with self.assertRaises(Chem.MolSanitizeException): Chem.SanitizeMol(Chem.MolFromSmiles('CFC', sanitize=False)) with self.assertRaises(ValueError): Chem.SanitizeMol(Chem.MolFromSmiles('CFC', sanitize=False)) with self.assertRaises(Chem.KekulizeException): Chem.SanitizeMol(Chem.MolFromSmiles('c1cc1', sanitize=False)) with self.assertRaises(Chem.MolSanitizeException): Chem.SanitizeMol(Chem.MolFromSmiles('c1cc1', sanitize=False)) with self.assertRaises(ValueError): Chem.SanitizeMol(Chem.MolFromSmiles('c1cc1', sanitize=False)) def testNoExceptionSmilesParserParams(self): """ MolFromSmiles should catch exceptions even when SmilesParserParams is provided. """ smiles_params = Chem.SmilesParserParams() mol = Chem.MolFromSmiles("C1CC", smiles_params) self.assertIsNone(mol) def testDetectChemistryProblems(self): m = Chem.MolFromSmiles('CFCc1cc1FC', sanitize=False) ps = Chem.DetectChemistryProblems(m) self.assertEqual(len(ps), 3) self.assertEqual([x.GetType() for x in ps], ['AtomValenceException', 'AtomValenceException', 'KekulizeException']) self.assertEqual(ps[0].GetAtomIdx(), 1) self.assertEqual(ps[1].GetAtomIdx(), 6) self.assertEqual(ps[2].GetAtomIndices(), (3, 4, 5)) def testGithub2611(self): mol = Chem.MolFromSmiles('ONCS.ONCS') for atom in mol.GetAtoms(): atom.SetIsotope(atom.GetIdx()) order1 = list( Chem.CanonicalRankAtomsInFragment(mol, atomsToUse=range(0, 4), breakTies=False, includeIsotopes=True)) order2 = list( Chem.CanonicalRankAtomsInFragment(mol, atomsToUse=range(0, 8), breakTies=False, includeIsotopes=False)) self.assertNotEqual(order1[:4], order2[4:]) # ensure that the orders are ignored in the second batch self.assertEqual(order2[:4], order2[4:]) for smi in ['ONCS.ONCS', 'F[C@@H](Br)[C@H](F)Cl']: mol = Chem.MolFromSmiles(smi) for atom in mol.GetAtoms(): atom.SetIsotope(atom.GetIdx()) for iso, chiral in [(True, True), (True, False), (False, True), (False, False)]: order1 = list( Chem.CanonicalRankAtomsInFragment(mol, atomsToUse=range(0, mol.GetNumAtoms()), bondsToUse=range(0, mol.GetNumBonds()), breakTies=False, includeIsotopes=iso, includeChirality=chiral)) order2 = list( Chem.CanonicalRankAtomsInFragment(mol, atomsToUse=range(0, mol.GetNumAtoms()), bondsToUse=range(0, mol.GetNumBonds()), breakTies=True, includeIsotopes=iso, includeChirality=chiral)) order3 = list( Chem.CanonicalRankAtoms(mol, breakTies=False, includeIsotopes=iso, includeChirality=chiral)) order4 = list( Chem.CanonicalRankAtoms(mol, breakTies=True, includeIsotopes=iso, includeChirality=chiral)) self.assertEqual(order1, order3) self.assertEqual(order2, order4) def testRankAtomsInFragmentsWithoutBonds(self): mol = Chem.MolFromSmiles('ONCO') order1 = list(Chem.CanonicalRankAtomsInFragment(mol, atomsToUse=range(0, 4), breakTies=False)) order2 = list(Chem.CanonicalRankAtoms(mol, breakTies=False)) self.assertNotEqual(order1[:4], order2[4:]) order3 = list( Chem.CanonicalRankAtomsInFragment(mol, atomsToUse=range(0, 4), bondsToUse=(0, 1, 2), breakTies=False)) def testSetBondStereoFromDirections(self): m1 = Chem.MolFromMolBlock( ''' Mrv1810 10141909482D 4 3 0 0 0 0 999 V2000 3.3412 -2.9968 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.5162 -2.9968 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.1037 -3.7112 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.7537 -2.2823 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 2 0 0 0 0 2 3 1 0 0 0 0 1 4 1 0 0 0 0 M END ''', sanitize=False) self.assertEqual(m1.GetBondBetweenAtoms(0, 1).GetBondType(), Chem.BondType.DOUBLE) self.assertEqual(m1.GetBondBetweenAtoms(0, 1).GetStereo(), Chem.BondStereo.STEREONONE) Chem.SetBondStereoFromDirections(m1) self.assertEqual(m1.GetBondBetweenAtoms(0, 1).GetStereo(), Chem.BondStereo.STEREOTRANS) m2 = Chem.MolFromMolBlock( ''' Mrv1810 10141909542D 4 3 0 0 0 0 999 V2000 3.4745 -5.2424 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.6495 -5.2424 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.2370 -5.9569 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.8870 -5.9569 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 2 0 0 0 0 2 3 1 0 0 0 0 1 4 1 0 0 0 0 M END ''', sanitize=False) self.assertEqual(m2.GetBondBetweenAtoms(0, 1).GetBondType(), Chem.BondType.DOUBLE) self.assertEqual(m2.GetBondBetweenAtoms(0, 1).GetStereo(), Chem.BondStereo.STEREONONE) Chem.SetBondStereoFromDirections(m2) self.assertEqual(m2.GetBondBetweenAtoms(0, 1).GetStereo(), Chem.BondStereo.STEREOCIS) def testSetBondDirFromStereo(self): m1 = Chem.MolFromSmiles('CC=CC') m1.GetBondWithIdx(1).SetStereoAtoms(0, 3) m1.GetBondWithIdx(1).SetStereo(Chem.BondStereo.STEREOCIS) Chem.SetDoubleBondNeighborDirections(m1) self.assertEqual(Chem.MolToSmiles(m1), r"C/C=C\C") self.assertEqual(m1.GetBondWithIdx(0).GetBondDir(), Chem.BondDir.ENDUPRIGHT) self.assertEqual(m1.GetBondWithIdx(2).GetBondDir(), Chem.BondDir.ENDDOWNRIGHT) def testAssignChiralTypesFromMolParity(self): class TestAssignChiralTypesFromMolParity: class BondDef: def __init__(self, bi, ei, t): self.beginIdx = bi self.endIdx = ei self.type = t def __init__(self, mol, parent): self.parent = parent self.parityMap = { Chem.ChiralType.CHI_TETRAHEDRAL_CW: 1, Chem.ChiralType.CHI_TETRAHEDRAL_CCW: 2, Chem.ChiralType.CHI_UNSPECIFIED: 0, Chem.ChiralType.CHI_OTHER: 0 } self.d_rwMol = Chem.RWMol(mol) self.assignMolParity() self.fillBondDefVect() Chem.AssignAtomChiralTagsFromMolParity(self.d_rwMol) self.d_refSmiles = Chem.MolToSmiles(self.d_rwMol) self.heapPermutation() def assignMolParity(self): Chem.AssignAtomChiralTagsFromStructure(self.d_rwMol) for a in self.d_rwMol.GetAtoms(): parity = self.parityMap[a.GetChiralTag()] a.SetIntProp("molParity", parity) a.SetChiralTag(Chem.ChiralType.CHI_UNSPECIFIED) def fillBondDefVect(self): self.d_bondDefVect = [ self.BondDef(b.GetBeginAtomIdx(), b.GetEndAtomIdx(), b.GetBondType()) for b in self.d_rwMol.GetBonds() ] def stripBonds(self): for i in reversed(range(self.d_rwMol.GetNumBonds())): b = self.d_rwMol.GetBondWithIdx(i) self.d_rwMol.RemoveBond(b.GetBeginAtomIdx(), b.GetEndAtomIdx()) def addBonds(self): [ self.d_rwMol.AddBond(bondDef.beginIdx, bondDef.endIdx, bondDef.type) for bondDef in self.d_bondDefVect ] def checkBondPermutation(self): self.stripBonds() self.addBonds() Chem.SanitizeMol(self.d_rwMol) Chem.AssignAtomChiralTagsFromMolParity(self.d_rwMol) self.parent.assertEqual(Chem.MolToSmiles(self.d_rwMol), self.d_refSmiles) def heapPermutation(self, s=0): # if size becomes 1 the permutation is ready to use if (s == 0): s = len(self.d_bondDefVect) if (s == 1): self.checkBondPermutation() return for i in range(s): self.heapPermutation(s - 1) # if size is odd, swap first and last element j = 0 if (s % 2 == 1) else i self.d_bondDefVect[j], self.d_bondDefVect[s - 1] = \ self.d_bondDefVect[s - 1], self.d_bondDefVect[j] molb = """ RDKit 3D 6 5 0 0 1 0 0 0 0 0999 V2000 -2.9747 1.7234 0.0753 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.4586 1.4435 0.1253 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.5885 2.6215 1.4893 Cl 0 0 0 0 0 0 0 0 0 0 0 0 -3.7306 0.3885 -0.0148 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.3395 3.0471 0.1580 Br 0 0 0 0 0 0 0 0 0 0 0 0 -1.1574 0.7125 1.2684 F 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 1 3 1 0 1 4 1 0 2 5 1 0 2 6 1 0 M END """ m = Chem.RWMol(Chem.MolFromMolBlock(molb, sanitize=True, removeHs=False)) self.assertIsNotNone(m) TestAssignChiralTypesFromMolParity(m, self) def testCXSMILESErrors(self): smi = "CCC |FAILURE|" ps = Chem.SmilesParserParams() ps.strictCXSMILES = False m = Chem.MolFromSmiles(smi, ps) self.assertTrue(m is not None) self.assertEqual(m.GetNumAtoms(), 3) def testRemoveHsParams(self): smips = Chem.SmilesParserParams() smips.removeHs = False m = Chem.MolFromSmiles('F.[H]', smips) ps = Chem.RemoveHsParameters() m = Chem.RemoveHs(m, ps) self.assertEqual(m.GetNumAtoms(), 2) ps.removeDegreeZero = True m = Chem.RemoveHs(m, ps) self.assertEqual(m.GetNumAtoms(), 1) m = Chem.MolFromSmiles('F[H-]F', smips) ps = Chem.RemoveHsParameters() m = Chem.RemoveHs(m, ps) self.assertEqual(m.GetNumAtoms(), 3) m = Chem.MolFromSmiles('F[H-]F', smips) ps.removeHigherDegrees = True m = Chem.RemoveHs(m, ps) self.assertEqual(m.GetNumAtoms(), 3) m = Chem.MolFromSmiles('F[H-]F', smips) ps.removeHigherDegrees = True ps.removeHydrides = True m = Chem.RemoveHs(m, ps) self.assertEqual(m.GetNumAtoms(), 2) m = Chem.MolFromSmiles('[H][H]', smips) ps = Chem.RemoveHsParameters() m = Chem.RemoveHs(m, ps) self.assertEqual(m.GetNumAtoms(), 2) m = Chem.MolFromSmiles('[H][H]', smips) ps.removeOnlyHNeighbors = True m = Chem.RemoveHs(m, ps) self.assertEqual(m.GetNumAtoms(), 0) m = Chem.MolFromSmiles('F[2H]', smips) ps = Chem.RemoveHsParameters() m = Chem.RemoveHs(m, ps) self.assertEqual(m.GetNumAtoms(), 2) m = Chem.MolFromSmiles('F[2H]', smips) ps.removeIsotopes = True m = Chem.RemoveHs(m, ps) self.assertEqual(m.GetNumAtoms(), 1) m = Chem.MolFromSmiles('c1c(C([2H])([2H])F)cccc1', smips) ps = Chem.RemoveHsParameters() m_noh = Chem.RemoveHs(m, ps) self.assertEqual(m_noh.GetNumAtoms(), m.GetNumAtoms()) ps.removeAndTrackIsotopes = True m_noh = Chem.RemoveHs(m, ps) self.assertEqual(m_noh.GetNumAtoms(), m.GetNumAtoms() - 2) self.assertTrue(m_noh.GetAtomWithIdx(2).HasProp("_isotopicHs")) self.assertEqual(tuple(m_noh.GetAtomWithIdx(2).GetPropsAsDict().get("_isotopicHs")), (2, 2)) m_h = Chem.AddHs(m_noh) self.assertFalse(m_h.GetAtomWithIdx(2).HasProp("_isotopicHs")) self.assertEqual( sum([ 1 for nbr in m_h.GetAtomWithIdx(2).GetNeighbors() if (nbr.GetAtomicNum() == 1 and nbr.GetIsotope()) ]), 2) m = Chem.MolFromSmiles('*[H]', smips) ps = Chem.RemoveHsParameters() m = Chem.RemoveHs(m, ps) self.assertEqual(m.GetNumAtoms(), 2) m = Chem.MolFromSmiles('*[H]', smips) ps.removeDummyNeighbors = True m = Chem.RemoveHs(m, ps) self.assertEqual(m.GetNumAtoms(), 1) m = Chem.MolFromSmiles('F/C=N/[H]', smips) ps = Chem.RemoveHsParameters() m = Chem.RemoveHs(m, ps) self.assertEqual(m.GetNumAtoms(), 4) m = Chem.MolFromSmiles('F/C=N/[H]', smips) ps.removeDefiningBondStereo = True m = Chem.RemoveHs(m, ps) self.assertEqual(m.GetNumAtoms(), 3) m = Chem.MolFromSmiles('FC([H])(O)Cl', smips) m.GetBondBetweenAtoms(1, 2).SetBondDir(Chem.BondDir.BEGINWEDGE) ps = Chem.RemoveHsParameters() m = Chem.RemoveHs(m, ps) self.assertEqual(m.GetNumAtoms(), 4) m = Chem.MolFromSmiles('FC([H])(O)Cl', smips) m.GetBondBetweenAtoms(1, 2).SetBondDir(Chem.BondDir.BEGINWEDGE) ps.removeWithWedgedBond = False m = Chem.RemoveHs(m, ps) self.assertEqual(m.GetNumAtoms(), 5) m = Chem.MolFromSmarts('F[#1]') ps = Chem.RemoveHsParameters() m = Chem.RemoveHs(m, ps) self.assertEqual(m.GetNumAtoms(), 2) m = Chem.MolFromSmarts('F[#1]') ps.removeWithQuery = True m = Chem.RemoveHs(m, ps) self.assertEqual(m.GetNumAtoms(), 1) m = Chem.MolFromSmiles('[C@]12([H])CCC1CO2.[H+].F[H-]F.[H][H].[H]*.F/C=C/[H]') m = Chem.RemoveAllHs(m) for at in m.GetAtoms(): self.assertNotEqual(at.GetAtomicNum(), 1) def testPickleCoordsAsDouble(self): import pickle m = Chem.MolFromSmiles('C') test_num = 1e50 conf = Chem.Conformer(1) conf.SetAtomPosition(0, (test_num, 0.0, 0.0)) m.AddConformer(conf) opts = Chem.GetDefaultPickleProperties() Chem.SetDefaultPickleProperties(Chem.PropertyPickleOptions.NoProps) self.assertNotEqual(pickle.loads(pickle.dumps(m)).GetConformer().GetAtomPosition(0).x, test_num) try: Chem.SetDefaultPickleProperties(Chem.PropertyPickleOptions.CoordsAsDouble) self.assertEqual(pickle.loads(pickle.dumps(m)).GetConformer().GetAtomPosition(0).x, test_num) finally: Chem.SetDefaultPickleProperties(opts) def testCustomSubstructMatchCheck(self): def accept_none(mol, vect): return False def accept_all(mol, vect): return True def accept_large(mol, vect): return sum(vect) > 5 m = Chem.MolFromSmiles('CCOCC') p = Chem.MolFromSmiles('CCO') ps = Chem.SubstructMatchParameters() self.assertEqual(len(m.GetSubstructMatches(p, ps)), 2) ps.setExtraFinalCheck(accept_none) self.assertEqual(len(m.GetSubstructMatches(p, ps)), 0) self.assertEqual(len(m.GetSubstructMatch(p, ps)), 0) self.assertFalse(m.HasSubstructMatch(p, ps)) ps.setExtraFinalCheck(accept_all) self.assertEqual(len(m.GetSubstructMatches(p, ps)), 2) self.assertEqual(len(m.GetSubstructMatch(p, ps)), 3) self.assertTrue(m.HasSubstructMatch(p, ps)) ps.setExtraFinalCheck(accept_large) self.assertEqual(len(m.GetSubstructMatches(p, ps)), 1) self.assertEqual(len(m.GetSubstructMatch(p, ps)), 3) self.assertTrue(m.HasSubstructMatch(p, ps)) def testMostSubstitutedCoreMatch(self): core = Chem.MolFromSmarts("[*:1]c1cc([*:2])ccc1[*:3]") orthoMeta = Chem.MolFromSmiles("c1ccc(-c2ccc(-c3ccccc3)c(-c3ccccc3)c2)cc1") ortho = Chem.MolFromSmiles("c1ccc(-c2ccccc2-c2ccccc2)cc1") meta = Chem.MolFromSmiles("c1ccc(-c2cccc(-c3ccccc3)c2)cc1") biphenyl = Chem.MolFromSmiles("c1ccccc1-c1ccccc1") phenyl = Chem.MolFromSmiles("c1ccccc1") def numHsMatchingDummies(mol, core, match): return sum([ 1 for qi, ai in enumerate(match) if core.GetAtomWithIdx(qi).GetAtomicNum() == 0 and mol.GetAtomWithIdx(ai).GetAtomicNum() == 1 ]) for mol, res in ((orthoMeta, 0), (ortho, 1), (meta, 1), (biphenyl, 2), (phenyl, 3)): mol = Chem.AddHs(mol) matches = mol.GetSubstructMatches(core) bestMatch = Chem.GetMostSubstitutedCoreMatch(mol, core, matches) self.assertEqual(numHsMatchingDummies(mol, core, bestMatch), res) ctrlCounts = sorted([numHsMatchingDummies(mol, core, match) for match in matches]) sortedCounts = [ numHsMatchingDummies(mol, core, match) for match in Chem.SortMatchesByDegreeOfCoreSubstitution(mol, core, matches) ] self.assertEqual(len(ctrlCounts), len(sortedCounts)) self.assertTrue(all(ctrl == sortedCounts[i] for i, ctrl in enumerate(ctrlCounts))) with self.assertRaises(ValueError): Chem.GetMostSubstitutedCoreMatch(orthoMeta, core, []) with self.assertRaises(ValueError): Chem.SortMatchesByDegreeOfCoreSubstitution(orthoMeta, core, []) def testSetCoordsTerminalAtom(self): mol = Chem.MolFromMolBlock(""" RDKit 2D 6 6 0 0 0 0 0 0 0 0999 V2000 1.5000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.7500 -1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.7500 -1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.5000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.7500 1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.7500 1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 2 0 2 3 1 0 3 4 2 0 4 5 1 0 5 6 2 0 6 1 1 0 M END """) mol = Chem.RWMol(mol) atom = Chem.Atom(0) idx = mol.AddAtom(atom) mol.AddBond(idx, 0) Chem.SetTerminalAtomCoords(mol, idx, 0) coord = mol.GetConformer().GetAtomPosition(idx) self.assertAlmostEqual(coord.x, 2.5, 2) self.assertAlmostEqual(coord.y, 0, 2) self.assertAlmostEqual(coord.z, 0, 2) def testSuppliersReadingDirectories(self): # this is an odd one, basically we need to check that we don't hang # which is pretty much a bad test in my opinion, but YMMV d = tempfile.mkdtemp() self.assertTrue(os.path.exists(d)) for supplier in [ Chem.SmilesMolSupplier, Chem.SDMolSupplier, Chem.TDTMolSupplier, #Chem.CompressedSDMolSupplier, ]: print("supplier:", supplier) with self.assertRaises(OSError): suppl = supplier(d) if hasattr(Chem, 'MaeMolSupplier'): with self.assertRaises(OSError): suppl = Chem.MaeMolSupplier(d) os.rmdir(d) def testRandomSmilesVect(self): m = Chem.MolFromSmiles("C1OCC1N(CO)(Cc1ccccc1NCCl)") v = Chem.MolToRandomSmilesVect(m, 5, randomSeed=0xf00d) self.assertEqual(v, [ "c1cc(CN(C2COC2)CO)c(cc1)NCCl", "N(CCl)c1c(CN(C2COC2)CO)cccc1", "N(CCl)c1ccccc1CN(C1COC1)CO", "OCN(Cc1ccccc1NCCl)C1COC1", "C(N(C1COC1)Cc1c(cccc1)NCCl)O" ]) v = Chem.MolToRandomSmilesVect(m, 5, randomSeed=0xf00d, allHsExplicit=True) self.assertEqual(v, [ "[cH]1[cH][c]([CH2][N]([CH]2[CH2][O][CH2]2)[CH2][OH])[c]([cH][cH]1)[NH][CH2][Cl]", "[NH]([CH2][Cl])[c]1[c]([CH2][N]([CH]2[CH2][O][CH2]2)[CH2][OH])[cH][cH][cH][cH]1", "[NH]([CH2][Cl])[c]1[cH][cH][cH][cH][c]1[CH2][N]([CH]1[CH2][O][CH2]1)[CH2][OH]", "[OH][CH2][N]([CH2][c]1[cH][cH][cH][cH][c]1[NH][CH2][Cl])[CH]1[CH2][O][CH2]1", "[CH2]([N]([CH]1[CH2][O][CH2]1)[CH2][c]1[c]([cH][cH][cH][cH]1)[NH][CH2][Cl])[OH]" ]) def testGithub3198(self): ps = Chem.SmilesParserParams() ps.removeHs = False m = Chem.MolFromSmiles('[H]OC(F).[Na]', ps) qa = rdqueries.AAtomQueryAtom() self.assertEqual(tuple(x.GetIdx() for x in m.GetAtomsMatchingQuery(qa)), (1, 2, 3, 4)) qa = rdqueries.AHAtomQueryAtom() self.assertEqual(tuple(x.GetIdx() for x in m.GetAtomsMatchingQuery(qa)), (0, 1, 2, 3, 4)) qa = rdqueries.QAtomQueryAtom() self.assertEqual(tuple(x.GetIdx() for x in m.GetAtomsMatchingQuery(qa)), (1, 3, 4)) qa = rdqueries.QHAtomQueryAtom() self.assertEqual(tuple(x.GetIdx() for x in m.GetAtomsMatchingQuery(qa)), (0, 1, 3, 4)) qa = rdqueries.XAtomQueryAtom() self.assertEqual(tuple(x.GetIdx() for x in m.GetAtomsMatchingQuery(qa)), (3, )) qa = rdqueries.XHAtomQueryAtom() self.assertEqual(tuple(x.GetIdx() for x in m.GetAtomsMatchingQuery(qa)), (0, 3)) qa = rdqueries.MAtomQueryAtom() self.assertEqual(tuple(x.GetIdx() for x in m.GetAtomsMatchingQuery(qa)), (4, )) qa = rdqueries.MHAtomQueryAtom() self.assertEqual(tuple(x.GetIdx() for x in m.GetAtomsMatchingQuery(qa)), (0, 4)) def testAdjustQueryPropertiesFiveRings(self): mol = Chem.MolFromSmiles('c1cc[nH]c1') nops = Chem.AdjustQueryParameters.NoAdjustments() nmol = Chem.AdjustQueryProperties(mol, nops) self.assertEqual(Chem.MolToSmarts(nmol), "[#6]1:[#6]:[#6]:[#7]:[#6]:1") nops.adjustConjugatedFiveRings = True nmol = Chem.AdjustQueryProperties(mol, nops) self.assertEqual(Chem.MolToSmarts(nmol), "[#6]1-,=,:[#6]-,=,:[#6]-,=,:[#7]-,=,:[#6]-,=,:1") def testFindPotentialStereo(self): mol = Chem.MolFromSmiles('C[C@H](F)C=CC') si = Chem.FindPotentialStereo(mol) self.assertEqual(len(si), 2) self.assertEqual(si[0].type, Chem.StereoType.Atom_Tetrahedral) self.assertEqual(si[0].specified, Chem.StereoSpecified.Specified) self.assertEqual(si[0].centeredOn, 1) self.assertEqual(si[0].descriptor, Chem.StereoDescriptor.Tet_CCW) self.assertEqual(list(si[0].controllingAtoms), [0, 2, 3]) self.assertEqual(si[1].type, Chem.StereoType.Bond_Double) self.assertEqual(si[1].specified, Chem.StereoSpecified.Unspecified) self.assertEqual(si[1].centeredOn, 3) self.assertEqual(si[1].descriptor, Chem.StereoDescriptor.NoValue) self.assertEqual(list(si[1].controllingAtoms), [1, Chem.Atom.NOATOM, 5, Chem.Atom.NOATOM]) def testNewFindMolChiralCenters(self): mol = Chem.MolFromSmiles('C[C@H](F)C=CC(F)Cl') ctrs = Chem.FindMolChiralCenters(mol, useLegacyImplementation=False) self.assertEqual(len(ctrs), 1) self.assertEqual(ctrs, [(1, 'S')]) ctrs = Chem.FindMolChiralCenters(mol, useLegacyImplementation=False, includeCIP=False) self.assertEqual(len(ctrs), 1) self.assertEqual(ctrs, [(1, 'Tet_CCW')]) ctrs = Chem.FindMolChiralCenters(mol, useLegacyImplementation=False, includeUnassigned=True, includeCIP=False) self.assertEqual(len(ctrs), 2) self.assertEqual(ctrs, [(1, 'Tet_CCW'), (5, '?')]) ctrs = Chem.FindMolChiralCenters(mol, useLegacyImplementation=False, includeUnassigned=True, includeCIP=True) self.assertEqual(len(ctrs), 2) self.assertEqual(ctrs, [(1, 'S'), (5, '?')]) def testGithub6945(self): origVal = Chem.GetUseLegacyStereoPerception() tgt = [(1, '?'), (4, 'R')] try: for opt in (not origVal, origVal): Chem.SetUseLegacyStereoPerception(opt) # make sure calling with the default value works: self.assertEqual( tgt, Chem.FindMolChiralCenters(Chem.MolFromSmiles('FC(Cl)(Br)[C@H](F)Cl'), includeUnassigned=True)) for useLegacy in (True, False): self.assertEqual( tgt, Chem.FindMolChiralCenters(Chem.MolFromSmiles('FC(Cl)(Br)[C@H](F)Cl'), includeUnassigned=True, useLegacyImplementation=useLegacy)) except: raise finally: Chem.SetUseLegacyStereoPerception(origVal) @unittest.skipUnless(hasattr(Chem, 'MolFromPNGFile'), "RDKit not built with iostreams support") def testMolFromPNG(self): fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'colchicine.png') mol = Chem.MolFromPNGFile(fileN) self.assertIsNotNone(mol) self.assertEqual(mol.GetNumAtoms(), 29) with open(fileN, 'rb') as inf: d = inf.read() mol = Chem.MolFromPNGString(d) self.assertIsNotNone(mol) self.assertEqual(mol.GetNumAtoms(), 29) @unittest.skipUnless(hasattr(Chem, 'MolFromPNGFile'), "RDKit not built with iostreams support") def testMolToPNG(self): fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'colchicine.no_metadata.png') with open(fileN, 'rb') as inf: d = inf.read() mol = Chem.MolFromSmiles('COc1cc2c(c(OC)c1OC)-c1ccc(OC)c(=O)cc1[C@@H](NC(C)=O)CC2') mol.SetProp('property', 'value') self.assertIsNotNone(mol) self.assertEqual(mol.GetNumAtoms(), 29) params = Chem.PNGMetadataParams() params.propertyFlags = Chem.PropertyPickleOptions.AllProps nd = Chem.MolMetadataToPNGString(mol, d, params) molFromPNG = Chem.MolFromPNGString(nd) self.assertIsNotNone(molFromPNG) self.assertEqual(molFromPNG.GetNumAtoms(), 29) self.assertTrue(molFromPNG.HasProp('property')) rdkit.Chem.rdDepictor.Compute2DCoords(mol) self.assertEqual(mol.GetNumConformers(), 1) nd = Chem.MolMetadataToPNGString(mol, d, includePkl=False) molFromPNG = Chem.MolFromPNGString(nd) self.assertIsNotNone(molFromPNG) self.assertEqual(molFromPNG.GetNumAtoms(), 29) self.assertEqual(molFromPNG.GetNumConformers(), 1) self.assertFalse(molFromPNG.HasProp('property')) params = Chem.PNGMetadataParams() params.includePkl = True params.propertyFlags = Chem.PropertyPickleOptions.AllProps nd = Chem.MolMetadataToPNGString(mol, d, params) molFromPNG = Chem.MolFromPNGString(nd) self.assertIsNotNone(molFromPNG) self.assertEqual(molFromPNG.GetNumAtoms(), 29) self.assertEqual(molFromPNG.GetNumConformers(), 1) self.assertTrue(molFromPNG.HasProp('property')) params = Chem.PNGMetadataParams() params.includePkl = False params.cxSmilesFlags = Chem.CXSmilesFields.CX_ALL_BUT_COORDS nd = Chem.MolMetadataToPNGString(mol, d, params) molFromPNG = Chem.MolFromPNGString(nd) self.assertIsNotNone(molFromPNG) self.assertEqual(molFromPNG.GetNumAtoms(), 29) self.assertEqual(molFromPNG.GetNumConformers(), 0) self.assertFalse(molFromPNG.HasProp('property')) params.includePkl = True params.propertyFlags = Chem.PropertyPickleOptions.NoProps nd = Chem.MolMetadataToPNGString(mol, d, params) molFromPNG = Chem.MolFromPNGString(nd) self.assertIsNotNone(molFromPNG) self.assertEqual(molFromPNG.GetNumAtoms(), 29) self.assertEqual(molFromPNG.GetNumConformers(), 1) self.assertFalse(molFromPNG.HasProp('property')) params.propertyFlags = Chem.PropertyPickleOptions.AllProps nd = Chem.MolMetadataToPNGString(mol, d, params) molFromPNG = Chem.MolFromPNGString(nd) self.assertIsNotNone(molFromPNG) self.assertEqual(molFromPNG.GetNumAtoms(), 29) self.assertEqual(molFromPNG.GetNumConformers(), 1) self.assertTrue(molFromPNG.HasProp('property')) nd = Chem.MolMetadataToPNGFile(mol, fileN, includePkl=False) molFromPNG = Chem.MolFromPNGString(nd) self.assertIsNotNone(molFromPNG) self.assertEqual(molFromPNG.GetNumAtoms(), 29) self.assertEqual(molFromPNG.GetNumConformers(), 1) self.assertFalse(molFromPNG.HasProp('property')) params = Chem.PNGMetadataParams() params.includePkl = True params.propertyFlags = Chem.PropertyPickleOptions.AllProps nd = Chem.MolMetadataToPNGFile(mol, fileN, params) molFromPNG = Chem.MolFromPNGString(nd) self.assertIsNotNone(molFromPNG) self.assertEqual(molFromPNG.GetNumAtoms(), 29) self.assertEqual(molFromPNG.GetNumConformers(), 1) self.assertTrue(molFromPNG.HasProp('property')) params = Chem.PNGMetadataParams() params.includePkl = False params.cxSmilesFlags = Chem.CXSmilesFields.CX_ALL_BUT_COORDS nd = Chem.MolMetadataToPNGFile(mol, fileN, params) molFromPNG = Chem.MolFromPNGString(nd) self.assertIsNotNone(molFromPNG) self.assertEqual(molFromPNG.GetNumAtoms(), 29) self.assertEqual(molFromPNG.GetNumConformers(), 0) self.assertFalse(molFromPNG.HasProp('property')) params.includePkl = True params.propertyFlags = Chem.PropertyPickleOptions.NoProps nd = Chem.MolMetadataToPNGFile(mol, fileN, params) molFromPNG = Chem.MolFromPNGString(nd) self.assertIsNotNone(molFromPNG) self.assertEqual(molFromPNG.GetNumAtoms(), 29) self.assertEqual(molFromPNG.GetNumConformers(), 1) self.assertFalse(molFromPNG.HasProp('property')) params.propertyFlags = Chem.PropertyPickleOptions.AllProps nd = Chem.MolMetadataToPNGFile(mol, fileN, params) molFromPNG = Chem.MolFromPNGString(nd) self.assertIsNotNone(molFromPNG) self.assertEqual(molFromPNG.GetNumAtoms(), 29) self.assertEqual(molFromPNG.GetNumConformers(), 1) self.assertTrue(molFromPNG.HasProp('property')) @unittest.skipUnless(hasattr(Chem, 'MolFromPNGFile'), "RDKit not built with iostreams support") def testMolsFromPNG(self): refMols = [Chem.MolFromSmiles(x) for x in ('c1ccccc1', 'CCO', 'CC(=O)O', 'c1ccccn1')] fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'multiple_mols.png') mols = Chem.MolsFromPNGFile(fileN) self.assertEqual(len(mols), len(refMols)) for mol, refMol in zip(mols, refMols): self.assertEqual(Chem.MolToSmiles(mol), Chem.MolToSmiles(refMol)) @unittest.skipUnless(hasattr(Chem, 'MolFromPNGFile'), "RDKit not built with iostreams support") def testMetadataToPNG(self): fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'colchicine.png') with open(fileN, 'rb') as inf: d = inf.read() mol = Chem.MolFromPNGString(d) self.assertIsNotNone(mol) nd = Chem.MolMetadataToPNGString(mol, d) vals = {'foo': '1', 'bar': '2'} nd = Chem.AddMetadataToPNGString(vals, nd) nvals = Chem.MetadataFromPNGString(nd) self.assertTrue('foo' in nvals) self.assertEqual(nvals['foo'], b'1') self.assertTrue('bar' in nvals) self.assertEqual(nvals['bar'], b'2') with open(fileN, 'rb') as inf: d = inf.read() mol = Chem.MolFromPNGString(d) self.assertIsNotNone(mol) nd = Chem.MolMetadataToPNGString(mol, d) nd = Chem.AddMetadataToPNGString(vals, nd) vals2 = {'foo': '3', 'bar': '4'} nd = Chem.AddMetadataToPNGString(vals2, nd) nvals = Chem.MetadataFromPNGString(nd, asList=True) self.assertEqual(len(nvals), 7) self.assertEqual([k.split()[0] for k, _ in nvals], ['SMILES', 'rdkitPKL', 'SMILES', 'foo', 'bar', 'foo', 'bar']) self.assertEqual([v.decode() for k, v in nvals if k == 'foo'], ['1', '3']) self.assertEqual([v.decode() for k, v in nvals if k == 'bar'], ['2', '4']) nd = Chem.AddMetadataToPNGFile(vals, fileN) nvals = Chem.MetadataFromPNGString(nd) self.assertTrue('foo' in nvals) self.assertEqual(nvals['foo'], b'1') self.assertTrue('bar' in nvals) self.assertEqual(nvals['bar'], b'2') vals = {'foo': 1, 'bar': '2'} with self.assertRaises(TypeError): nd = Chem.AddMetadataToPNGString(vals, d) def test_github3403(self): core1 = "[$(C-!@[a])](=O)(Cl)" sma = Chem.MolFromSmarts(core1) m = Chem.MolFromSmiles("c1ccccc1C(=O)Cl") self.assertFalse(m.HasSubstructMatch(sma, recursionPossible=False)) m = Chem.MolFromSmiles("c1ccccc1C(=O)Cl") self.assertTrue(m.HasSubstructMatch(sma)) m = Chem.MolFromSmiles("c1ccccc1C(=O)Cl") self.assertFalse(m.HasSubstructMatch(sma, recursionPossible=False)) def test_github3517(self): fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'NCI_aids_few.sdf') sdSup = Chem.SDMolSupplier(fileN) mols_list = list(sdSup) mols_list_compr = [m for m in sdSup] self.assertEqual(len(mols_list), len(mols_list_compr)) def test_github3492(self): def read_smile(s): m = Chem.MolFromSmiles(s) rdkit.Chem.rdDepictor.Compute2DCoords(m) return m def sq_dist(a, b): ab = [a[i] - b[i] for i, _ in enumerate(a)] return sum([d * d for d in ab]) self.assertIsNotNone(Chem.MolFromSmiles("OCCN").GetAtoms()[0].GetOwningMol()) self.assertEqual([Chem.MolFromSmiles("OCCN").GetAtoms()[i].GetAtomicNum() for i in range(4)], [8, 6, 6, 7]) self.assertIsNotNone(Chem.MolFromSmiles("O=CCC=N").GetBonds()[0].GetOwningMol()) self.assertEqual( [Chem.MolFromSmiles("O=CCC=N").GetBonds()[i].GetBondType() for i in range(4)], [Chem.BondType.DOUBLE, Chem.BondType.SINGLE, Chem.BondType.SINGLE, Chem.BondType.DOUBLE]) self.assertIsNotNone(read_smile("CCC").GetConformers()[0].GetOwningMol()) pos = read_smile("CCC").GetConformers()[0].GetPositions() self.assertAlmostEqual(sq_dist(pos[0], pos[1]), sq_dist(pos[1], pos[2])) def test_get_set_positions(self): m = Chem.MolFromSmiles('CCC |(-1.29904,-0.25,;0,0.5,;1.29904,-0.25,)|') pos = np.zeros([3, 3], np.double) pos[0][1] = 1 pos[0][2] = 2 pos[1][0] = 3 pos[1][1] = 4 pos[1][2] = 5 pos[2][0] = 6 pos[2][1] = 6 pos[2][2] = 7 m.GetConformer(0).SetPositions(pos) pos2 = m.GetConformer(0).GetPositions() self.assertTrue((pos == pos2).all()) def test_get_set_positions_stride(self): m = Chem.MolFromSmiles('CCC |(-1.29904,-0.25,;0,0.5,;1.29904,-0.25,)|') # to check stride walking in SetPositions # allocate a double size array, then slice every other element # numpy will mess with the striding to create the view onto the double size array pos = np.zeros([6, 3], np.double)[::2] pos[0][1] = 1 pos[0][2] = 2 pos[1][0] = 3 pos[1][1] = 4 pos[1][2] = 5 pos[2][0] = 6 pos[2][1] = 6 pos[2][2] = 7 m.GetConformer(0).SetPositions(pos) pos2 = m.GetConformer(0).GetPositions() self.assertTrue((pos == pos2).all()) def test_github3553(self): fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'Wrap', 'test_data', 'github3553.sdf') sdSup = Chem.SDMolSupplier(fileN) for mol in sdSup: pval = mol.GetProp('boiling.point.predicted') sio = StringIO() w = Chem.SDWriter(sio) w.SetKekulize(True) w.SetForceV3000(True) w.write(mol) w.flush() txt = sio.getvalue() self.assertTrue(pval in txt) def test_github1631(self): fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', '1CRN.pdb') m = Chem.MolFromPDBFile(fileN) info = m.GetAtomWithIdx(0).GetPDBResidueInfo() self.assertEqual(info.GetName(), " N ") self.assertEqual(info.GetResidueName(), "THR") self.assertAlmostEqual(info.GetTempFactor(), 13.79, 2) m2 = Chem.MolFromSmiles('CC(C(C(=O)O)N)O') self.assertTrue(m2.GetAtomWithIdx(6).GetPDBResidueInfo() is None) m2.GetAtomWithIdx(6).SetPDBResidueInfo(info) info2 = m2.GetAtomWithIdx(6).GetPDBResidueInfo() self.assertEqual(info2.GetName(), " N ") self.assertEqual(info2.GetResidueName(), "THR") self.assertAlmostEqual(info2.GetTempFactor(), 13.79, 2) def testMolzip(self): tests = [["C[*:1]", "N[*:1]", "CN", Chem.MolzipParams()]] for a, b, res, params in tests: self.assertEqual( Chem.CanonSmiles(res), Chem.MolToSmiles(Chem.molzip(Chem.MolFromSmiles(a), Chem.MolFromSmiles(b), params))) # multiple arg test a = Chem.MolFromSmiles('C=C[1*]') b = Chem.MolFromSmiles('O/C=N/[1*]') p = Chem.MolzipParams() p.label = Chem.MolzipLabel.Isotope c = Chem.molzip(a, b, p) self.assertEqual(Chem.MolToSmiles(c), 'C=C/N=C/O') # single argument test a = Chem.MolFromSmiles('C=C[1*].O/C=N/[1*]') p = Chem.MolzipParams() p.label = Chem.MolzipLabel.Isotope c = Chem.molzip(a, p) self.assertEqual(Chem.MolToSmiles(c), 'C=C/N=C/O') a = Chem.MolFromSmiles("[C@H]([Xe])(F)([V])") b = Chem.MolFromSmiles("[Xe]N.[V]I") p = Chem.MolzipParams() p.label = Chem.MolzipLabel.AtomType p.setAtomSymbols(["Xe", "V"]) c = Chem.molzip(a, b, p) self.assertEqual(Chem.MolToSmiles(c), "N[C@@H](F)I") a = Chem.MolFromSmiles("C(=[*:1])N") b = Chem.MolFromSmiles("[*:1]-N=C") p = Chem.MolzipParams() p.enforceValenceRules = False c = Chem.molzip(a, b, p) def testContextManagers(self): from rdkit import RDLogger RDLogger.DisableLog('rdApp.*') fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'Wrap', 'test_data', 'github3553.sdf') with Chem.SDMolSupplier(fileN) as suppl: mols = [x for x in suppl if x is not None] sio = StringIO() with Chem.SDWriter(sio) as w: for m in mols: w.write(m) txt = sio.getvalue() self.assertEqual(txt.count('$$$$'), len(mols)) with self.assertRaises(RuntimeError): w.write(mols[0]) with Chem.ForwardSDMolSupplier(fileN) as suppl: mols = [x for x in suppl if x is not None] fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'first_200.tpsa.csv') with Chem.SmilesMolSupplier(fileN, ",", 0, -1) as suppl: ms = [x for x in suppl if x is not None] sio = StringIO() with Chem.SmilesWriter(sio) as w: for m in mols: w.write(m) txt = sio.getvalue() self.assertEqual(txt.count('\n'), len(mols) + 1) with self.assertRaises(RuntimeError): w.write(mols[0]) data = """$SMI CAS<17584-12-2> | $SMI CAS<~> | $SMI CAS<932-53-6> | $SMI CAS<~> |""" with Chem.TDTMolSupplier() as suppl: suppl.SetData(data, "CAS") ms = [x for x in suppl if x is not None] sio = StringIO() with Chem.TDTWriter(sio) as w: for m in mols: w.write(m) txt = sio.getvalue() self.assertEqual(txt.count('$SMI'), len(mols)) with self.assertRaises(RuntimeError): w.write(mols[0]) fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', '1CRN.pdb') m = Chem.MolFromPDBFile(fileN) mols = [m, m] sio = StringIO() with Chem.PDBWriter(sio) as w: for m in mols: w.write(m) txt = sio.getvalue() self.assertEqual(txt.count('COMPND CRAMBIN'), len(mols)) with self.assertRaises(RuntimeError): w.write(mols[0]) if hasattr(Chem, 'MaeMolSupplier'): fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'NCI_aids_few.mae') with Chem.MaeMolSupplier(fileN) as suppl: ms = [x for x in suppl if x is not None] RDLogger.EnableLog('rdApp.*') def testInsertMol(self): m = Chem.MolFromSmiles("CNO") m2 = Chem.MolFromSmiles("c1ccccc1") m3 = Chem.MolFromSmiles("C1CC1") rwmol = Chem.RWMol(m) rwmol.InsertMol(m2) rwmol.InsertMol(m3) self.assertEqual(Chem.MolToSmiles(rwmol), Chem.CanonSmiles("CNO.c1ccccc1.C1CC1")) def testBatchEdits(self): mol = Chem.MolFromSmiles("C1CCCO1") for rwmol in [Chem.EditableMol(mol), Chem.RWMol(mol)]: rwmol.BeginBatchEdit() rwmol.RemoveAtom(2) rwmol.RemoveAtom(3) rwmol.CommitBatchEdit() nmol = rwmol.GetMol() self.assertEqual(Chem.MolToSmiles(nmol), "CCO") for rwmol in [Chem.EditableMol(mol), Chem.RWMol(mol)]: rwmol = Chem.EditableMol(mol) rwmol.BeginBatchEdit() rwmol.RemoveAtom(3) rwmol.RemoveBond(4, 0) rwmol.CommitBatchEdit() nmol = rwmol.GetMol() self.assertEqual(Chem.MolToSmiles(nmol), "CCC.O") for rwmol in [Chem.EditableMol(mol), Chem.RWMol(mol)]: rwmol.BeginBatchEdit() rwmol.RemoveAtom(2) rwmol.RemoveAtom(3) rwmol.RollbackBatchEdit() nmol = rwmol.GetMol() self.assertEqual(Chem.MolToSmiles(nmol), "C1CCOC1") def testBatchEditContextManager(self): mol = Chem.MolFromSmiles("C1CCCO1") with Chem.RWMol(mol) as rwmol: rwmol.RemoveAtom(2) rwmol.RemoveAtom(3) # make sure we haven't actually changed anything yet: self.assertEqual(rwmol.GetNumAtoms(), mol.GetNumAtoms()) self.assertEqual(rwmol.GetNumAtoms(), mol.GetNumAtoms() - 2) # make sure no changes get made if we throw an exception try: with Chem.RWMol(mol) as rwmol: rwmol.RemoveAtom(2) rwmol.RemoveAtom(6) except Exception: pass self.assertEqual(rwmol.GetNumAtoms(), mol.GetNumAtoms()) def testGithub4138(self): m = Chem.MolFromSmiles('C1CCCO1') q = Chem.MolFromSmarts('') self.assertFalse(m.HasSubstructMatch(q)) self.assertEqual(m.GetSubstructMatch(q), ()) self.assertEqual(m.GetSubstructMatches(q), ()) m = Chem.MolFromSmiles('') q = Chem.MolFromSmarts('C') self.assertFalse(m.HasSubstructMatch(q)) self.assertEqual(m.GetSubstructMatch(q), ()) self.assertEqual(m.GetSubstructMatches(q), ()) def testGithub4144(self): ''' the underlying problem with #4144 was that the includeRings argument could not be passed to ClearComputedProps() from Python. Make sure that's fixed ''' m = Chem.MolFromSmiles('c1ccccc1') self.assertEqual(m.GetRingInfo().NumRings(), 1) m.ClearComputedProps(includeRings=False) self.assertEqual(m.GetRingInfo().NumRings(), 1) m.ClearComputedProps() with self.assertRaises(RuntimeError): m.GetRingInfo().NumRings() def testAddWavyBondsForStereoAny(self): m = Chem.MolFromSmiles('CC=CC') m.GetBondWithIdx(1).SetStereo(Chem.BondStereo.STEREOANY) m2 = Chem.Mol(m) Chem.AddWavyBondsForStereoAny(m2) self.assertEqual(m2.GetBondWithIdx(1).GetStereo(), Chem.BondStereo.STEREONONE) self.assertEqual(m2.GetBondWithIdx(0).GetBondDir(), Chem.BondDir.UNKNOWN) m2 = Chem.Mol(m) Chem.AddWavyBondsForStereoAny(m2, clearDoubleBondFlags=False) self.assertEqual(m2.GetBondWithIdx(1).GetStereo(), Chem.BondStereo.STEREOANY) self.assertEqual(m2.GetBondWithIdx(0).GetBondDir(), Chem.BondDir.UNKNOWN) m = Chem.MolFromSmiles("CC=CC=CC=CC") m.GetBondWithIdx(1).SetStereoAtoms(0, 3) m.GetBondWithIdx(1).SetStereo(Chem.BondStereo.STEREOCIS) m.GetBondWithIdx(3).SetStereo(Chem.BondStereo.STEREOANY) m.GetBondWithIdx(5).SetStereoAtoms(4, 7) m.GetBondWithIdx(5).SetStereo(Chem.BondStereo.STEREOCIS) m2 = Chem.Mol(m) Chem.AddWavyBondsForStereoAny(m2) self.assertEqual(m2.GetBondWithIdx(3).GetStereo(), Chem.BondStereo.STEREOANY) self.assertEqual(m2.GetBondWithIdx(0).GetBondDir(), Chem.BondDir.NONE) Chem.AddWavyBondsForStereoAny( m2, addWhenImpossible=Chem.StereoBondThresholds.DBL_BOND_SPECIFIED_STEREO) self.assertEqual(m2.GetBondWithIdx(3).GetStereo(), Chem.BondStereo.STEREONONE) self.assertEqual(m2.GetBondWithIdx(2).GetBondDir(), Chem.BondDir.UNKNOWN) def testSmartsParseParams(self): smi = "CCC |$foo;;bar$| ourname" m = Chem.MolFromSmarts(smi) self.assertTrue(m is not None) ps = Chem.SmartsParserParams() ps.allowCXSMILES = False ps.parseName = False m = Chem.MolFromSmarts(smi, ps) self.assertTrue(m is None) ps.allowCXSMILES = True ps.parseName = True m = Chem.MolFromSmarts(smi, ps) self.assertTrue(m is not None) self.assertTrue(m.GetAtomWithIdx(0).HasProp('atomLabel')) self.assertEqual(m.GetAtomWithIdx(0).GetProp('atomLabel'), "foo") self.assertTrue(m.HasProp('_Name')) self.assertEqual(m.GetProp('_Name'), "ourname") self.assertEqual(m.GetProp("_CXSMILES_Data"), "|$foo;;bar$|") def testSmilesWriteParams(self): m = Chem.MolFromSmiles('C[C@H](F)Cl') ps = Chem.SmilesWriteParams() ps.rootedAtAtom = 1 self.assertEqual(Chem.MolToSmiles(m, ps), "[C@@H](C)(F)Cl") self.assertEqual(Chem.MolToCXSmiles(m, ps), "[C@@H](C)(F)Cl") def testCXSmilesOptions(self): # just checking that the fields parameter gets used. # we've tested the individual values on the C++ side m = Chem.MolFromSmiles("OC1CCC(F)C1 |LN:1:1.3.2.6|") ps = Chem.SmilesWriteParams() ps.rootedAtAtom = 1 self.assertEqual( Chem.MolToCXSmiles(m, ps, (Chem.CXSmilesFields.CX_ALL ^ Chem.CXSmilesFields.CX_LINKNODES)), "C1(O)CCC(F)C1") self.assertTrue(hasattr(Chem.CXSmilesFields, 'CX_BOND_CFG')) self.assertTrue(hasattr(Chem.CXSmilesFields, 'CX_ALL_BUT_COORDS')) def testKekulizeIfPossible(self): m = Chem.MolFromSmiles('c1cccn1', sanitize=False) m.UpdatePropertyCache(strict=False) Chem.KekulizeIfPossible(m) for atom in m.GetAtoms(): self.assertTrue(atom.GetIsAromatic()) for bond in m.GetBonds(): self.assertTrue(bond.GetIsAromatic()) self.assertEqual(bond.GetBondType(), Chem.BondType.AROMATIC) def testHasValenceViolation(self): mol = Chem.MolFromSmiles('C(C)(C)(C)(C)C', sanitize=False) mol.UpdatePropertyCache(strict=False) self.assertTrue(any(a.HasValenceViolation() for a in mol.GetAtoms())) def testgithub4992(self): if not hasattr(Chem, "Chem.MultithreadedSDMolSupplier"): return good1 = Chem.MolFromSmiles('C') #good1.SetProp('molname', 'good1') good2 = Chem.MolFromSmiles('CC') #good2.SetProp('molname', 'good2') good3 = Chem.MolFromSmiles('CCC') #good3.SetProp('molname', 'good3') bad = Chem.MolFromSmiles('CN(C)(C)C', sanitize=False) #bad.SetProp('molname', 'bad') with Chem.SDWriter("good1_good2_good3.sdf") as w: w.write(good1) w.write(good2) w.write(good3) w.write(good1) w.write(good2) w.write(good3) with Chem.SDWriter("good1_good2_good3_bad.sdf") as w: w.write(good1) w.write(good2) w.write(good3) w.write(bad) with Chem.SDWriter("good1_good2_bad_good3.sdf") as w: w.write(good1) w.write(good2) w.write(bad) w.write(good3) with Chem.SDWriter("bad_good1_good2_good3.sdf") as w: w.write(bad) w.write(good1) w.write(good2) w.write(good3) def read_mols(supplier, filename): i = 0 with supplier(filename) as sdSuppl: count = -1 for count, mol in enumerate(sdSuppl): if mol is not None: i += 1 return i counts = [] for i, s in enumerate((Chem.SDMolSupplier, Chem.MultithreadedSDMolSupplier)): for j, f in enumerate(('good1_good2_good3.sdf', 'good1_good2_bad_good3.sdf', 'good1_good2_bad_good3.sdf', 'bad_good1_good2_good3.sdf')): print(f'---------------\n{s.__name__} {f}') if i == 0: counts.append(read_mols(s, f)) else: self.assertEqual(counts[j], read_mols(s, f)) def test_blocklogs(self): with capture_logging(logging.INFO) as log_stream: # Logging is known to be problematic with static linked libs # so let's skip the test if we can't see the expected error # log before we block logging. Chem.MolFromSmiles('garbage_0') if 'garbage_0' not in log_stream.getvalue(): self.skipTest('cannot fetch log msgs') else: log_stream.truncate(0) with rdBase.BlockLogs(): Chem.MolFromSmiles('garbage_1') self.assertNotIn('garbage_1', log_stream.getvalue()) Chem.MolFromSmiles('garbage_2') self.assertIn('garbage_2', log_stream.getvalue()) log_stream.truncate(0) block = rdBase.BlockLogs() self.assertIsNotNone(block) Chem.MolFromSmiles('garbage_3') self.assertNotIn('garbage_3', log_stream.getvalue()) del block Chem.MolFromSmiles('garbage_4') self.assertIn('garbage_4', log_stream.getvalue()) log_stream.truncate(0) def testDisableNontetrahedralStereo(self): fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'test_data', 'nontetrahedral_3d.sdf') origVal = Chem.GetAllowNontetrahedralChirality() Chem.SetAllowNontetrahedralChirality(True) suppl = Chem.SDMolSupplier(fileN, sanitize=False) for mol in suppl: Chem.AssignStereochemistryFrom3D(mol) ct = mol.GetProp("ChiralType") at = mol.GetAtomWithIdx(0) if ct == "SP": self.assertEqual(at.GetChiralTag(), Chem.ChiralType.CHI_SQUAREPLANAR) elif ct == "TB": self.assertEqual(at.GetChiralTag(), Chem.ChiralType.CHI_TRIGONALBIPYRAMIDAL) elif ct == "OH": self.assertEqual(at.GetChiralTag(), Chem.ChiralType.CHI_OCTAHEDRAL) elif ct == "TH": self.assertEqual(at.GetChiralTag(), Chem.ChiralType.CHI_TETRAHEDRAL) Chem.SetAllowNontetrahedralChirality(False) suppl = Chem.SDMolSupplier(fileN, sanitize=False) for mol in suppl: Chem.AssignStereochemistryFrom3D(mol) ct = mol.GetProp("ChiralType") at = mol.GetAtomWithIdx(0) if ct == "TH": self.assertEqual(at.GetChiralTag(), Chem.ChiralType.CHI_TETRAHEDRAL) else: self.assertEqual(at.GetChiralTag(), Chem.ChiralType.CHI_UNSPECIFIED) Chem.SetAllowNontetrahedralChirality(origVal) def test_legacyStereochemGlobal(self): origVal = Chem.GetUseLegacyStereoPerception() Chem.SetUseLegacyStereoPerception(True) m = Chem.MolFromSmiles("C[C@H]1CCC2(CC1)CC[C@H](C)C(C)C2") self.assertEqual(m.GetAtomWithIdx(1).GetChiralTag(), Chem.ChiralType.CHI_UNSPECIFIED) self.assertNotEqual(m.GetAtomWithIdx(9).GetChiralTag(), Chem.ChiralType.CHI_UNSPECIFIED) Chem.SetUseLegacyStereoPerception(False) m = Chem.MolFromSmiles("C[C@H]1CCC2(CC1)CC[C@H](C)C(C)C2") self.assertEqual(m.GetAtomWithIdx(1).GetChiralTag(), Chem.ChiralType.CHI_UNSPECIFIED) self.assertNotEqual(m.GetAtomWithIdx(9).GetChiralTag(), Chem.ChiralType.CHI_UNSPECIFIED) Chem.SetUseLegacyStereoPerception(origVal) def testValidationMrvRxn(self): Chem.SetUseLegacyStereoPerception(False) mrvBlock = "" fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'Wrap', 'test_data', 'DoubleBondRxn.mrv') with open(fileN) as f: mrvBlock = f.read() rxn = AllChem.ReactionFromMrvBlock(mrvBlock, True, True) smi = AllChem.ReactionToSmiles(rxn) self.assertEqual(smi, "CC=C1COC(C)OC1>>CC=C1COC(C)OC1") rxn = AllChem.ReactionFromMrvFile(fileN, True, True) smi = AllChem.ReactionToSmiles(rxn) self.assertEqual(smi, "CC=C1COC(C)OC1>>CC=C1COC(C)OC1") Chem.SetUseLegacyStereoPerception(True) def testValidationMrvRxn2(self): Chem.SetUseLegacyStereoPerception(False) mrvBlock = "" fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'Wrap', 'test_data', 'DoubleBondRxn2.mrv') with open(fileN) as f: mrvBlock = f.read() rxn = AllChem.ReactionFromMrvBlock(mrvBlock, True, True) smi = AllChem.ReactionToSmiles(rxn) self.assertEqual( smi, "C=C1CC(C)(C(C)(C)C)CC(=C)C1=Cc1cc(C)c(C)c(CCC)c1>>C=C1CC(C)(C(C)(C)C)CC(=C)C1=Cc1cc(C)c(Cl)c(CCC)c1" ) Chem.SetUseLegacyStereoPerception(True) def testValidationMrvRxn3(self): Chem.SetUseLegacyStereoPerception(False) mrvBlock = "" fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'Wrap', 'test_data', 'DoubleBondAndChiralRxn.mrv') with open(fileN) as f: mrvBlock = f.read() rxn = AllChem.ReactionFromMrvBlock(mrvBlock, True, True) smi = AllChem.ReactionToSmiles(rxn) self.assertEqual( smi, "C=C1C[C@](C)(C(C)(C)C)CC(=C)/C1=C/c1cc(C)c(C)c(CCC)c1>>C=C1CC(C)(C(C)(C)C)CC(=C)C1=Cc1cc(C)c(Cl)c(C[C@H](C)Cl)c1" ) Chem.SetUseLegacyStereoPerception(True) def testValidationRxn(self): Chem.SetUseLegacyStereoPerception(False) rxnBlock = "" fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'Wrap', 'test_data', 'DoubleBondAndChiralRxn.rxn') with open(fileN) as f: rxnBlock = f.read() rxn = AllChem.ReactionFromRxnBlock(rxnBlock, True, True, False) smi = AllChem.ReactionToSmiles(rxn) self.assertEqual( smi, "C=C1C[C@](C)(C(C)(C)C)CC(=C)/C1=C/c1cc(C)c(C)c(CCC)c1>>C=C1CC(C)(C(C)(C)C)CC(=C)C1=Cc1cc(C)c(Cl)c(C[C@H](C)Cl)c1" ) rxn = AllChem.ReactionFromRxnFile(fileN, True, True, False) smi = AllChem.ReactionToSmiles(rxn) self.assertEqual( smi, "C=C1C[C@](C)(C(C)(C)C)CC(=C)/C1=C/c1cc(C)c(C)c(CCC)c1>>C=C1CC(C)(C(C)(C)C)CC(=C)C1=Cc1cc(C)c(Cl)c(C[C@H](C)Cl)c1" ) Chem.SetUseLegacyStereoPerception(True) def testValidationSmiles(self): Chem.SetUseLegacyStereoPerception(False) smi = "C=C1CC(C)(C(C)(C)C)CC(=C)/C1=C/c1cc(C)c(Cl)c(C[C@H](C)Cl)c1" ps = Chem.SmilesParserParams() ps.allowCXSMILES = True ps.parseName = False ps.sanitize = True ps.removeHs = False mol = Chem.MolFromSmiles(smi, ps) self.assertIsNotNone(mol) of = Chem.SmilesWriteParams() outSmi = Chem.MolToCXSmiles(mol, of, Chem.CXSmilesFields.CX_ALL) self.assertEqual(outSmi, "C=C1CC(C)(C(C)(C)C)CC(=C)C1=Cc1cc(C)c(Cl)c(C[C@H](C)Cl)c1") smi = "C=C1C[C@](C)(C(C)(C)C)CC(=C)/C1=C/c1cc(C)c(Cl)c(C[C@H](C)Cl)c1" mol = Chem.MolFromSmiles(smi, ps) self.assertIsNotNone(mol) outSmi = Chem.MolToCXSmiles(mol, of, Chem.CXSmilesFields.CX_ALL), self.assertEqual(outSmi[0], "C=C1C[C@@](C)(C(C)(C)C)CC(=C)/C1=C\\c1cc(C)c(Cl)c(C[C@H](C)Cl)c1") Chem.SetUseLegacyStereoPerception(True) def testValidationMol(self): Chem.SetUseLegacyStereoPerception(False) molBlock = "" fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'Wrap', 'test_data', 'DoubleBond2000.mol') with open(fileN) as f: molBlock = f.read() mol = Chem.MolFromMolBlock(molBlock, True, True, False) smi = Chem.MolToSmiles(mol) self.assertEqual(smi, "C=C1CC(C)(C(C)(C)C)CC(=C)C1=Cc1cc(C)c(Cl)c(C[C@H](C)Cl)c1") rxn = Chem.MolFromMolFile(fileN, True, True, False) smi = Chem.MolToSmiles(rxn) self.assertEqual(smi, "C=C1CC(C)(C(C)(C)C)CC(=C)C1=Cc1cc(C)c(Cl)c(C[C@H](C)Cl)c1") # nowthe V3000 version fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'Wrap', 'test_data', 'DoubleBond3000.mol') with open(fileN) as f: molBlock = f.read() mol = Chem.MolFromMolBlock(molBlock, True, True, False) smi = Chem.MolToSmiles(mol) self.assertEqual(smi, "C=C1CC(C)(C(C)(C)C)CC(=C)C1=Cc1cc(C)c(Cl)c(C[C@H](C)Cl)c1") mol = Chem.MolFromMolFile(fileN, True, True, False) smi = Chem.MolToSmiles(mol) self.assertEqual(smi, "C=C1CC(C)(C(C)(C)C)CC(=C)C1=Cc1cc(C)c(Cl)c(C[C@H](C)Cl)c1") # now the chiral versions - the double bond should be chiral fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'Wrap', 'test_data', 'DoubleBond2000Chiral.mol') with open(fileN) as f: molBlock = f.read() mol = Chem.MolFromMolBlock(molBlock, True, True, False) smi = Chem.MolToSmiles(mol) self.assertEqual(smi, "C=C1C[C@@](C)(C(C)(C)C)CC(=C)/C1=C/c1cc(C)c(Cl)c(C[C@H](C)Cl)c1") rxn = Chem.MolFromMolFile(fileN, True, True, False) smi = Chem.MolToSmiles(rxn) self.assertEqual(smi, "C=C1C[C@@](C)(C(C)(C)C)CC(=C)/C1=C/c1cc(C)c(Cl)c(C[C@H](C)Cl)c1") # now the V3000 version fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'Wrap', 'test_data', 'DoubleBond3000Chiral.mol') with open(fileN) as f: molBlock = f.read() mol = Chem.MolFromMolBlock(molBlock, True, True, False) smi = Chem.MolToSmiles(mol) self.assertEqual(smi, "C=C1C[C@@](C)(C(C)(C)C)CC(=C)/C1=C/c1cc(C)c(Cl)c(C[C@H](C)Cl)c1") mol = Chem.MolFromMolFile(fileN, True, True, False) smi = Chem.MolToSmiles(mol) self.assertEqual(smi, "C=C1C[C@@](C)(C(C)(C)C)CC(=C)/C1=C/c1cc(C)c(Cl)c(C[C@H](C)Cl)c1") Chem.SetUseLegacyStereoPerception(True) def testValidationMrv(self): Chem.SetUseLegacyStereoPerception(False) mrvBlock = "" fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'Wrap', 'test_data', 'DoubleBond.mrv') with open(fileN) as f: mrvBlock = f.read() mol = Chem.MolFromMrvBlock(mrvBlock, True, True) smi = Chem.MolToSmiles(mol) self.assertEqual(smi, "C=C1CC(C)(C(C)(C)C)CC(=C)C1=Cc1cc(C)c(Cl)c(C[C@H](C)Cl)c1") mol = Chem.MolFromMrvFile(fileN, True, True) smi = Chem.MolToSmiles(mol) self.assertEqual(smi, "C=C1CC(C)(C(C)(C)C)CC(=C)C1=Cc1cc(C)c(Cl)c(C[C@H](C)Cl)c1") Chem.SetUseLegacyStereoPerception(True) def test_picklingWithAddedAttribs(self): m = Chem.MolFromSmiles("C") m.foo = 1 m.SetIntProp("bar", 2) pkl = pickle.dumps(m) nm = pickle.loads(pkl) self.assertEqual(nm.GetIntProp("bar"), 2) self.assertEqual(nm.foo, 1) def testGithubIssue6306(self): # test of unpickling props = Chem.GetDefaultPickleProperties() try: Chem.SetDefaultPickleProperties(Chem.PropertyPickleOptions.AllProps) mols = [Chem.MolFromSmiles(s) for s in ["C", "CC"]] scaffolds = [MurckoScaffold.GetScaffoldForMol(m) for m in mols] # this shouldn't throw an exception unpickler = [pickle.loads(pickle.dumps(m)) for m in mols] finally: Chem.SetDefaultPickleProperties(props) @unittest.skipIf(not hasattr(Chem, 'MaeWriter'), "not build with MAEParser support") def testMaeWriter(self): mol = Chem.MolFromSmiles("C1CCCCC1") self.assertTrue(mol) title = "random test mol" mol.SetProp('_Name', title) ofile = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'Wrap', 'test_data', 'outMaeWriter.mae') writer1 = Chem.MaeWriter(ofile) osio = StringIO() writer2 = Chem.MaeWriter(osio) for writer in (writer1, writer2): writer.write(mol) writer.close() del writer with open(ofile) as f: maefile = f.read() self.assertEqual(maefile, osio.getvalue()) self.assertIn('s_m_m2io_version', maefile) self.assertIn('f_m_ct', maefile) self.assertIn('s_m_title', maefile) self.assertIn(title, maefile) self.assertIn(f' m_atom[{mol.GetNumAtoms()}] {{', maefile) self.assertEqual(maefile.count("A0A0A0"), 6) # 6 grey-colored heavy atoms self.assertTrue(f' m_bond[{mol.GetNumBonds()}] {{', maefile) @unittest.skipIf(not hasattr(Chem, 'MaeWriter'), "not build with MAEParser support") def testMaeWriterProps(self): mol = Chem.MolFromSmiles("C1CCCCC1") self.assertTrue(mol) title = "random test mol" mol.SetProp('_Name', title) boolProp = False intProp = 123454321 realProp = 2.718282 # Mae files have a predefined precision of 6 digits! strProp = r"This is a dummy prop, yay!" ignored_prop = 'ignored_prop' str_dummy_prop = 'str_dummy_prop' mol_prop = 'mol_prop' atom_prop = 'atom_prop' bond_prop = 'bond_prop' exported_props = [str_dummy_prop, mol_prop, atom_prop, bond_prop] mol.SetIntProp(mol_prop, intProp) mol.SetProp(str_dummy_prop, strProp) mol.SetProp(ignored_prop, ignored_prop) atomIdx = 2 at = mol.GetAtomWithIdx(atomIdx) at.SetDoubleProp(atom_prop, realProp) at.SetProp(str_dummy_prop, strProp) at.SetProp(ignored_prop, ignored_prop) bondIdx = 4 b = mol.GetBondWithIdx(bondIdx) b.SetBoolProp(bond_prop, boolProp) b.SetProp(str_dummy_prop, strProp) b.SetProp(ignored_prop, ignored_prop) heavyAtomColor = "767676" osio = StringIO() with Chem.MaeWriter(osio) as w: w.SetProps(exported_props) w.write(mol, heavyAtomColor=heavyAtomColor) maestr = osio.getvalue() ctBlockStart = maestr.find('f_m_ct') atomBlockStart = maestr.find(' m_atom[') bondBlockStart = maestr.find(' m_bond[') self.assertNotEqual(ctBlockStart, -1) self.assertNotEqual(atomBlockStart, -1) self.assertNotEqual(bondBlockStart, -1) self.assertGreater(bondBlockStart, atomBlockStart) self.assertGreater(atomBlockStart, ctBlockStart) # structure properties self.assertIn(mol_prop, maestr[ctBlockStart:atomBlockStart]) self.assertIn(str(intProp), maestr[ctBlockStart:atomBlockStart]) self.assertIn(str_dummy_prop, maestr[ctBlockStart:atomBlockStart]) self.assertIn(strProp, maestr[ctBlockStart:atomBlockStart]) self.assertNotIn(ignored_prop, maestr[ctBlockStart:atomBlockStart]) # atom properties self.assertIn(atom_prop, maestr[atomBlockStart:bondBlockStart]) self.assertIn(str_dummy_prop, maestr[atomBlockStart:bondBlockStart]) self.assertNotIn(ignored_prop, maestr[atomBlockStart:bondBlockStart]) for line in maestr[atomBlockStart:bondBlockStart].split('\n'): if line.strip().startswith(str(atomIdx + 1)): break self.assertIn(str(realProp), line) self.assertIn(strProp, line) self.assertIn(heavyAtomColor, line) # bond properties self.assertIn(bond_prop, maestr[bondBlockStart:]) self.assertIn(str_dummy_prop, maestr[bondBlockStart:]) self.assertNotIn(ignored_prop, maestr[bondBlockStart:]) for line in maestr[bondBlockStart:].split('\n'): if line.strip().startswith(str(bondIdx + 1)): break self.assertIn(str(int(boolProp)), line) self.assertIn(strProp, line) @unittest.skipIf(not hasattr(Chem, 'MaeWriter'), "not build with MAEParser support") def testMaeWriterRoundtrip(self): smiles = "C1CCCCC1" mol = Chem.MolFromSmiles(smiles) self.assertTrue(mol) osio = StringIO() with Chem.MaeWriter(osio) as w: w.write(mol) isio = BytesIO(osio.getvalue().encode()) with Chem.MaeMolSupplier(isio) as r: roundtrip_mol = next(r) self.assertTrue(roundtrip_mol) self.assertEqual(Chem.MolToSmiles(roundtrip_mol), smiles) @unittest.skipIf(not hasattr(Chem, 'MaeWriter'), "not build with MAEParser support") def testMaeWriterGetText(self): smiles = "C1CCCCC1" mol = Chem.MolFromSmiles(smiles) self.assertTrue(mol) dummy_prop = 'dummy_prop' another_dummy_prop = 'another_dummy_prop' mol.SetProp(dummy_prop, dummy_prop) mol.SetProp(another_dummy_prop, another_dummy_prop) heavyAtomColor = "767676" osio = StringIO() with Chem.MaeWriter(osio) as w: w.SetProps([dummy_prop]) w.write(mol, heavyAtomColor=heavyAtomColor) iomae = osio.getvalue() ctBlockStart = iomae.find('f_m_ct') self.assertNotEqual(ctBlockStart, -1) self.assertIn(dummy_prop, iomae) self.assertNotIn(another_dummy_prop, iomae) mae = Chem.MaeWriter.GetText(mol, heavyAtomColor, -1, [dummy_prop]) self.assertEqual(mae, iomae[ctBlockStart:]) def test3dChiralMolFile(self): # FIX: these tests need to be made to work with the new stereochemistry perception origVal = Chem.GetUseLegacyStereoPerception() Chem.SetUseLegacyStereoPerception(True) fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'Cubane.sdf') with open(fileN, 'r') as inF: inD = inF.read() fileWedges = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'CubaneWedges.cxsmi') with open(fileWedges, 'r') as inF: inWedges = inF.read() fileNoWedges = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'CubaneNoWedges.cxsmi') with open(fileNoWedges, 'r') as inF: inNoWedges = inF.read() m1 = Chem.MolFromMolBlock(inD, sanitize=False, removeHs=False, strictParsing=True) self.assertTrue(m1 is not None) self.assertTrue(m1.GetNumAtoms() == 16) smi = Chem.MolToCXSmiles(m1) self.assertTrue(smi == inWedges) m1 = Chem.MolFromMolFile(fileN, sanitize=False, removeHs=False, strictParsing=True) self.assertTrue(m1 is not None) self.assertTrue(m1.GetNumAtoms() == 16) smi = Chem.MolToCXSmiles(m1) self.assertTrue(smi == inWedges) m1 = Chem.MolFromMolBlock(inD, sanitize=False, removeHs=False, strictParsing=True) Chem.RemoveNonExplicit3DChirality(m1) self.assertTrue(m1 is not None) self.assertTrue(m1.GetNumAtoms() == 16) smi = Chem.MolToCXSmiles(m1) self.assertTrue(smi == inNoWedges) m1 = Chem.MolFromMolFile(fileN, sanitize=False, removeHs=False, strictParsing=True) Chem.RemoveNonExplicit3DChirality(m1) self.assertTrue(m1 is not None) self.assertTrue(m1.GetNumAtoms() == 16) smi = Chem.MolToCXSmiles(m1) self.assertTrue(smi == inNoWedges) Chem.SetUseLegacyStereoPerception(origVal) def test3dChiralMrvFile(self): # FIX: these tests need to be made to work with the new stereochemistry perception origVal = Chem.GetUseLegacyStereoPerception() Chem.SetUseLegacyStereoPerception(True) fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'MarvinParse', 'test_data', 'Cubane.mrv') with open(fileN, 'r') as inF: inD = inF.read() fileWedges = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'MarvinParse', 'test_data', 'CubaneWedges.cxsmi') with open(fileWedges, 'r') as inF: inWedges = inF.read() fileNoWedges = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'MarvinParse', 'test_data', 'CubaneNoWedges.cxsmi') with open(fileNoWedges, 'r') as inF: inNoWedges = inF.read() m1 = Chem.MolFromMrvBlock(inD, sanitize=False, removeHs=False) self.assertTrue(m1 is not None) self.assertTrue(m1.GetNumAtoms() == 16) smi = Chem.MolToCXSmiles(m1) sys.stdout.flush() self.assertTrue(smi == inWedges) m1 = Chem.MolFromMrvFile(fileN, sanitize=False, removeHs=False) self.assertTrue(m1 is not None) self.assertTrue(m1.GetNumAtoms() == 16) smi = Chem.MolToCXSmiles(m1) self.assertTrue(smi == inWedges) m1 = Chem.MolFromMrvBlock(inD, sanitize=False, removeHs=False) Chem.RemoveNonExplicit3DChirality(m1) self.assertTrue(m1 is not None) self.assertTrue(m1.GetNumAtoms() == 16) smi = Chem.MolToCXSmiles(m1) self.assertTrue(smi == inNoWedges) m1 = Chem.MolFromMrvFile(fileN, sanitize=False, removeHs=False) Chem.RemoveNonExplicit3DChirality(m1) self.assertTrue(m1 is not None) self.assertTrue(m1.GetNumAtoms() == 16) smi = Chem.MolToCXSmiles(m1) self.assertTrue(smi == inNoWedges) Chem.SetUseLegacyStereoPerception(origVal) def test3dChiralCxsmiles(self): # FIX: these tests need to be made to work with the new stereochemistry perception origVal = Chem.GetUseLegacyStereoPerception() Chem.SetUseLegacyStereoPerception(True) fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'SmilesParse', 'test_data', 'Cubane.cxsmi') with open(fileN, 'r') as inF: inD = inF.read() fileWedges = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'SmilesParse', 'test_data', 'Cubane.cxsmi.expected3D.cxsmi') with open(fileWedges, 'r') as inF: inWedges = inF.read() fileNoWedges = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'SmilesParse', 'test_data', 'Cubane.cxsmi.expected3D2.cxsmi') with open(fileNoWedges, 'r') as inF: inNoWedges = inF.read() ps = Chem.SmilesParserParams() ps.allowCXSMILES = True ps.parseName = False ps.sanitize = False ps.removeHs = False m1 = Chem.MolFromSmiles(inD, ps) self.assertTrue(m1 is not None) self.assertEqual(m1.GetNumAtoms(), 16) smi = Chem.MolToCXSmiles(m1) sys.stdout.flush() self.assertEqual(smi, inWedges) m1 = Chem.MolFromSmiles(inD, ps) Chem.RemoveNonExplicit3DChirality(m1) self.assertTrue(m1 is not None) self.assertEqual(m1.GetNumAtoms(), 16) smi = Chem.MolToCXSmiles(m1) self.assertEqual(smi, inNoWedges) Chem.SetUseLegacyStereoPerception(origVal) def testReapplyMolBlockWedging(self): fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'MarvinParse', 'test_data', 'JDQ443_atrop1.mrv') fileReapplied = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'MarvinParse', 'test_data', 'JDQ443_atrop1.mrv.expected.sdf') with open(fileReapplied, 'r') as inF: isReapplied = inF.read() fileNotReapplied = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'MarvinParse', 'test_data', 'JDQ443_atrop1.mrv.expected2.sdf') with open(fileNotReapplied, 'r') as inF: isNotReapplied = inF.read() m = Chem.MolFromMrvFile(fileN, False, False) self.assertTrue(m is not None) self.assertTrue(m.GetNumAtoms() == 38) mBlock = Chem.MolToMolBlock(m, False, -1, True, True) sys.stdout.flush() self.assertTrue(mBlock == isNotReapplied) Chem.ReapplyMolBlockWedging(m) mBlock = Chem.MolToMolBlock(m, False, -1, True, True) sys.stdout.flush() self.assertTrue(mBlock == isReapplied) def testReapplyMolBlockWedgingAllBondTypes(self): m = Chem.MolFromMolBlock(''' Mrv2311 04232413302D 0 0 0 0 0 999 V3000 M V30 BEGIN CTAB M V30 COUNTS 5 4 0 0 0 M V30 BEGIN ATOM M V30 1 S -11.583 11.3533 0 0 M V30 2 C -12.9167 10.5833 0 0 M V30 3 O -11.583 12.8933 0 0 M V30 4 C -10.2493 10.5833 0 0 M V30 5 C -10.2493 9.0433 0 0 M V30 END ATOM M V30 BEGIN BOND M V30 1 1 4 5 M V30 2 1 2 1 M V30 3 1 1 4 M V30 4 2 1 3 CFG=1 M V30 END BOND M V30 END CTAB M END ''') self.assertEqual(m.GetBondWithIdx(3).GetBondType(), Chem.BondType.DOUBLE) Chem.ReapplyMolBlockWedging(m) self.assertEqual(m.GetBondWithIdx(3).GetBondDir(), Chem.BondDir.BEGINWEDGE) Chem.ReapplyMolBlockWedging(m, False) self.assertEqual(m.GetBondWithIdx(3).GetBondDir(), Chem.BondDir.NONE) def testAtropisomerWedging(self): m = Chem.MolFromSmiles( 'CC1=C(N2C=CC=C2[C@H](C)Cl)C(C)CCC1 |(2.679,0.4142,;1.3509,1.181,;0.0229,0.4141,;0.0229,-1.1195,;1.2645,-2.0302,;0.7901,-3.4813,;-0.7446,-3.4813,;-1.219,-2.0302,;-2.679,-1.5609,;-3.0039,-0.0556,;-3.8202,-2.595,;-1.3054,1.1809,;-2.6335,0.4141,;-1.3054,2.7145,;0.0229,3.4813,;1.3509,2.7146,),wD:2.11,wU:8.10,&1:8|' ) self.assertTrue(m is not None) self.assertTrue(m.GetNumAtoms() == 16) sys.stdout.flush() flags = Chem.CXSmilesFields.CX_COORDS | \ Chem.CXSmilesFields.CX_MOLFILE_VALUES | \ Chem.CXSmilesFields.CX_ATOM_PROPS | \ Chem.CXSmilesFields.CX_BOND_CFG | \ Chem.CXSmilesFields.CX_ENHANCEDSTEREO ps = Chem.SmilesWriteParams() ps.canonical = True smi = Chem.MolToCXSmiles(m, ps, flags, Chem.RestoreBondDirOption.RestoreBondDirOptionTrue) self.assertTrue( smi == 'CC1=C(n2cccc2[C@H](C)Cl)C(C)CCC1 |(2.679,0.4142,;1.3509,1.181,;0.0229,0.4141,;0.0229,-1.1195,;1.2645,-2.0302,;0.7901,-3.4813,;-0.7446,-3.4813,;-1.219,-2.0302,;-2.679,-1.5609,;-3.0039,-0.0556,;-3.8202,-2.595,;-1.3054,1.1809,;-2.6335,0.4141,;-1.3054,2.7145,;0.0229,3.4813,;1.3509,2.7146,),wD:2.11,wU:8.10,&1:8|' ) flags = Chem.CXSmilesFields.CX_COORDS | \ Chem.CXSmilesFields.CX_MOLFILE_VALUES | \ Chem.CXSmilesFields.CX_ATOM_PROPS | \ Chem.CXSmilesFields.CX_BOND_ATROPISOMER | \ Chem.CXSmilesFields.CX_ENHANCEDSTEREO smi = Chem.MolToCXSmiles(m, ps, flags, Chem.RestoreBondDirOption.RestoreBondDirOptionTrue) self.assertTrue( smi == 'CC1=C(n2cccc2[C@H](C)Cl)C(C)CCC1 |(2.679,0.4142,;1.3509,1.181,;0.0229,0.4141,;0.0229,-1.1195,;1.2645,-2.0302,;0.7901,-3.4813,;-0.7446,-3.4813,;-1.219,-2.0302,;-2.679,-1.5609,;-3.0039,-0.0556,;-3.8202,-2.595,;-1.3054,1.1809,;-2.6335,0.4141,;-1.3054,2.7145,;0.0229,3.4813,;1.3509,2.7146,),wD:2.11,&1:8|' ) flags = Chem.CXSmilesFields.CX_COORDS | \ Chem.CXSmilesFields.CX_MOLFILE_VALUES | \ Chem.CXSmilesFields.CX_ATOM_PROPS | \ Chem.CXSmilesFields.CX_ENHANCEDSTEREO smi = Chem.MolToCXSmiles(m, ps, flags, Chem.RestoreBondDirOption.RestoreBondDirOptionTrue) self.assertTrue( smi == 'CC1=C(n2cccc2[C@H](C)Cl)C(C)CCC1 |(2.679,0.4142,;1.3509,1.181,;0.0229,0.4141,;0.0229,-1.1195,;1.2645,-2.0302,;0.7901,-3.4813,;-0.7446,-3.4813,;-1.219,-2.0302,;-2.679,-1.5609,;-3.0039,-0.0556,;-3.8202,-2.595,;-1.3054,1.1809,;-2.6335,0.4141,;-1.3054,2.7145,;0.0229,3.4813,;1.3509,2.7146,),&1:8|' ) def testAtropisomerWedgingRigorousEnhancedStereo(self): m = Chem.MolFromSmiles( 'CC1=C(N2C=CC=C2[C@H](C)Cl)C(C)CCC1 |(2.679,0.4142,;1.3509,1.181,;0.0229,0.4141,;0.0229,-1.1195,;1.2645,-2.0302,;0.7901,-3.4813,;-0.7446,-3.4813,;-1.219,-2.0302,;-2.679,-1.5609,;-3.0039,-0.0556,;-3.8202,-2.595,;-1.3054,1.1809,;-2.6335,0.4141,;-1.3054,2.7145,;0.0229,3.4813,;1.3509,2.7146,),wD:2.11,wU:8.10,&1:8|' ) self.assertTrue(m is not None) self.assertTrue(m.GetNumAtoms() == 16) sys.stdout.flush() flags = Chem.CXSmilesFields.CX_COORDS | \ Chem.CXSmilesFields.CX_MOLFILE_VALUES | \ Chem.CXSmilesFields.CX_ATOM_PROPS | \ Chem.CXSmilesFields.CX_BOND_CFG | \ Chem.CXSmilesFields.CX_ENHANCEDSTEREO ps = Chem.SmilesWriteParams() ps.canonical = True m = Chem.CanonicalizeStereoGroups(m) smi = Chem.MolToCXSmiles(m, ps, flags, Chem.RestoreBondDirOption.RestoreBondDirOptionTrue) print('smi: ', smi) self.assertTrue( smi == 'CC1=C(n2cccc2[C@H](C)Cl)C(C)CCC1 |(2.679,0.4142,;1.3509,1.181,;0.0229,0.4141,;0.0229,-1.1195,;1.2645,-2.0302,;0.7901,-3.4813,;-0.7446,-3.4813,;-1.219,-2.0302,;-2.679,-1.5609,;-3.0039,-0.0556,;-3.8202,-2.595,;-1.3054,1.1809,;-2.6335,0.4141,;-1.3054,2.7145,;0.0229,3.4813,;1.3509,2.7146,),wD:8.9,2.11,a:2,&1:8|' ) def testEnhancedStereoExceedsLimit(self): m = Chem.MolFromSmiles( 'C[C@H]1CC[C@]2(NC1)O[C@H]1C[C@H]3[C@H]4CC=C5C[C@H](O[C@H]6O[C@H](CO)[C@H](O[C@H]7O[C@H](C)[C@H](O)[C@H](O)[C@H]7O)[C@H](O)[C@H]6O[C@H]6O[C@H](C)[C@H](O)[C@H](O)[C@H]6O)CC[C@]5(C)[C@H]4CC[C@]3(C)[C@H]1[C@H]2C |&1:1,&2:4,&3:8,&4:10,&5:11,&6:16,&7:18,&8:20,&9:23,&10:25,&11:27,&12:29,&13:31,&14:33,&15:35,&16:37,&17:39,&18:41,&19:43,&20:45,&21:47,&22:51,&23:53,&24:56,&25:58,&26:59|' ) self.assertTrue(m is not None) self.assertTrue(m.GetNumAtoms() == 61) m2 = Chem.MolFromSmiles( 'C[C@@H]1CC[C@]2(NC1)O[C@H]1C[C@H]3[C@H]4CC=C5C[C@H](O[C@H]6O[C@H](CO)[C@H](O[C@H]7O[C@H](C)[C@H](O)[C@H](O)[C@H]7O)[C@H](O)[C@H]6O[C@H]6O[C@H](C)[C@H](O)[C@H](O)[C@H]6O)CC[C@]5(C)[C@H]4CC[C@]3(C)[C@H]1[C@H]2C |&1:1,&2:4,&3:8,&4:10,&5:11,&6:16,&7:18,&8:20,&9:23,&10:25,&11:27,&12:29,&13:31,&14:33,&15:35,&16:37,&17:39,&18:41,&19:43,&20:45,&21:47,&22:51,&23:53,&24:56,&25:58,&26:59|' ) self.assertTrue(m2 is not None) self.assertTrue(m2.GetNumAtoms() == 61) sys.stdout.flush() flags = Chem.CXSmilesFields.CX_COORDS | \ Chem.CXSmilesFields.CX_MOLFILE_VALUES | \ Chem.CXSmilesFields.CX_ATOM_PROPS | \ Chem.CXSmilesFields.CX_BOND_CFG | \ Chem.CXSmilesFields.CX_ENHANCEDSTEREO ps = Chem.SmilesWriteParams() ps.canonical = True m = Chem.CanonicalizeStereoGroups(m, Chem.StereoGroupAbsOptions.OnlyIncludeWhenOtherGroupsExist, 16) smi = Chem.MolToCXSmiles(m, ps, flags, Chem.RestoreBondDirOption.RestoreBondDirOptionTrue) smi2 = Chem.MolToCXSmiles(m2, ps, flags, Chem.RestoreBondDirOption.RestoreBondDirOptionTrue) self.assertTrue(smi == smi2) def testEnhancedStereoDoesNotExceedsLimit(self): m = Chem.MolFromSmiles( 'C[C@H]1CC[C@]2(NC1)O[C@H]1C[C@H]3[C@H]4CC=C5C[C@H](O[C@H]6O[C@H](CO)[C@H](O[C@H]7O[C@H](C)[C@H](O)[C@H](O)[C@H]7O)[C@H](O)[C@H]6O[C@H]6O[C@H](C)[C@H](O)[C@H](O)[C@H]6O)CC[C@]5(C)[C@H]4CC[C@]3(C)[C@H]1[C@H]2C |&1:1,&2:4,&3:8,&4:10,&5:11,&6:16,&7:18,&8:20,&9:23,&10:25,&11:27,&12:29|' ) self.assertTrue(m is not None) self.assertTrue(m.GetNumAtoms() == 61) m2 = Chem.MolFromSmiles( 'C[C@@H]1CC[C@]2(NC1)O[C@H]1C[C@H]3[C@H]4CC=C5C[C@H](O[C@H]6O[C@H](CO)[C@H](O[C@H]7O[C@H](C)[C@H](O)[C@H](O)[C@H]7O)[C@H](O)[C@H]6O[C@H]6O[C@H](C)[C@H](O)[C@H](O)[C@H]6O)CC[C@]5(C)[C@H]4CC[C@]3(C)[C@H]1[C@H]2C |&1:1,&2:4,&3:8,&4:10,&5:11,&6:16,&7:18,&8:20,&9:23,&10:25,&11:27,&12:29|' ) self.assertTrue(m2 is not None) self.assertTrue(m2.GetNumAtoms() == 61) sys.stdout.flush() flags = Chem.CXSmilesFields.CX_COORDS | \ Chem.CXSmilesFields.CX_MOLFILE_VALUES | \ Chem.CXSmilesFields.CX_ATOM_PROPS | \ Chem.CXSmilesFields.CX_BOND_CFG | \ Chem.CXSmilesFields.CX_ENHANCEDSTEREO ps = Chem.SmilesWriteParams() ps.canonical = True m = Chem.CanonicalizeStereoGroups(m, Chem.StereoGroupAbsOptions.OnlyIncludeWhenOtherGroupsExist, 16) # m2 = Chem.CanonicalizeStereoGroups(m2) smi = Chem.MolToCXSmiles(m, ps, flags, Chem.RestoreBondDirOption.RestoreBondDirOptionTrue) smi2 = Chem.MolToCXSmiles(m2, ps, flags, Chem.RestoreBondDirOption.RestoreBondDirOptionTrue) self.assertTrue(smi != smi2) m2 = Chem.CanonicalizeStereoGroups(m2) smi2 = Chem.MolToCXSmiles(m, ps, flags, Chem.RestoreBondDirOption.RestoreBondDirOptionTrue) self.assertTrue(smi == smi2) def test_picklingWithAddedAttribs(self): m = Chem.MolFromSmiles("C") m.foo = 1 m.SetIntProp("bar", 2) pkl = pickle.dumps(m) nm = pickle.loads(pkl) self.assertEqual(nm.GetIntProp("bar"), 2) self.assertEqual(nm.foo, 1) def testGithubIssue6306(self): # test of unpickling props = Chem.GetDefaultPickleProperties() try: Chem.SetDefaultPickleProperties(Chem.PropertyPickleOptions.AllProps) mols = [Chem.MolFromSmiles(s) for s in ["C", "CC"]] scaffolds = [MurckoScaffold.GetScaffoldForMol(m) for m in mols] # this shouldn't throw an exception unpickler = [pickle.loads(pickle.dumps(m)) for m in mols] finally: Chem.SetDefaultPickleProperties(props) @unittest.skipIf(not hasattr(Chem, 'MaeWriter'), "not built with MAEParser support") def testMaeWriter(self): mol = Chem.MolFromSmiles("C1CCCCC1") self.assertTrue(mol) title = "random test mol" mol.SetProp('_Name', title) ofile = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'Wrap', 'test_data', 'outMaeWriter.mae') writer1 = Chem.MaeWriter(ofile) osio = StringIO() writer2 = Chem.MaeWriter(osio) for writer in (writer1, writer2): writer.write(mol) writer.close() del writer with open(ofile) as f: maefile = f.read() self.assertEqual(maefile, osio.getvalue()) self.assertIn('s_m_m2io_version', maefile) self.assertIn('f_m_ct', maefile) self.assertIn('s_m_title', maefile) self.assertIn(title, maefile) self.assertIn(f' m_atom[{mol.GetNumAtoms()}] {{', maefile) self.assertTrue(f' m_bond[{mol.GetNumBonds()}] {{', maefile) @unittest.skipIf(not hasattr(Chem, 'MaeWriter'), "not built with MAEParser support") def testMaeWriterProps(self): mol = Chem.MolFromSmiles("C1CCCCC1") self.assertTrue(mol) title = "random test mol" mol.SetProp('_Name', title) boolProp = False intProp = 123454321 realProp = 2.718282 # Mae files have a predefined precision of 6 digits! strProp = r"This is a dummy prop, yay!" ignored_prop = 'ignored_prop' str_dummy_prop = 'str_dummy_prop' mol_prop = 'mol_prop' atom_prop = 'atom_prop' bond_prop = 'bond_prop' exported_props = [str_dummy_prop, mol_prop, atom_prop, bond_prop] mol.SetIntProp(mol_prop, intProp) mol.SetProp(str_dummy_prop, strProp) mol.SetProp(ignored_prop, ignored_prop) atomIdx = 2 at = mol.GetAtomWithIdx(atomIdx) at.SetDoubleProp(atom_prop, realProp) at.SetProp(str_dummy_prop, strProp) at.SetProp(ignored_prop, ignored_prop) bondIdx = 4 b = mol.GetBondWithIdx(bondIdx) b.SetBoolProp(bond_prop, boolProp) b.SetProp(str_dummy_prop, strProp) b.SetProp(ignored_prop, ignored_prop) osio = StringIO() with Chem.MaeWriter(osio) as w: w.SetProps(exported_props) w.write(mol) maestr = osio.getvalue() ctBlockStart = maestr.find('f_m_ct') atomBlockStart = maestr.find(' m_atom[') bondBlockStart = maestr.find(' m_bond[') self.assertNotEqual(ctBlockStart, -1) self.assertNotEqual(atomBlockStart, -1) self.assertNotEqual(bondBlockStart, -1) self.assertGreater(bondBlockStart, atomBlockStart) self.assertGreater(atomBlockStart, ctBlockStart) # structure properties self.assertIn(mol_prop, maestr[ctBlockStart:atomBlockStart]) self.assertIn(str(intProp), maestr[ctBlockStart:atomBlockStart]) self.assertIn(str_dummy_prop, maestr[ctBlockStart:atomBlockStart]) self.assertIn(strProp, maestr[ctBlockStart:atomBlockStart]) self.assertNotIn(ignored_prop, maestr[ctBlockStart:atomBlockStart]) # atom properties self.assertIn(atom_prop, maestr[atomBlockStart:bondBlockStart]) self.assertIn(str_dummy_prop, maestr[atomBlockStart:bondBlockStart]) self.assertNotIn(ignored_prop, maestr[atomBlockStart:bondBlockStart]) for line in maestr[atomBlockStart:bondBlockStart].split('\n'): if line.strip().startswith(str(atomIdx + 1)): break self.assertIn(str(realProp), line) self.assertIn(strProp, line) # bond properties self.assertIn(bond_prop, maestr[bondBlockStart:]) self.assertIn(str_dummy_prop, maestr[bondBlockStart:]) self.assertNotIn(ignored_prop, maestr[bondBlockStart:]) for line in maestr[bondBlockStart:].split('\n'): if line.strip().startswith(str(bondIdx + 1)): break self.assertIn(str(int(boolProp)), line) self.assertIn(strProp, line) @unittest.skipIf(not hasattr(Chem, 'MaeWriter'), "not built with MAEParser support") def testMaeWriterRoundtrip(self): smiles = "C1CCCCC1" mol = Chem.MolFromSmiles(smiles) self.assertTrue(mol) osio = StringIO() with Chem.MaeWriter(osio) as w: w.write(mol) isio = BytesIO(osio.getvalue().encode()) with Chem.MaeMolSupplier(isio) as r: roundtrip_mol = next(r) self.assertTrue(roundtrip_mol) self.assertEqual(Chem.MolToSmiles(roundtrip_mol), smiles) @unittest.skipIf(not hasattr(Chem, 'MaeWriter'), "not built with MAEParser support") def testMaeWriterGetText(self): smiles = "C1CCCCC1" mol = Chem.MolFromSmiles(smiles) self.assertTrue(mol) dummy_prop = 'dummy_prop' another_dummy_prop = 'another_dummy_prop' mol.SetProp(dummy_prop, dummy_prop) mol.SetProp(another_dummy_prop, another_dummy_prop) osio = StringIO() with Chem.MaeWriter(osio) as w: w.SetProps([dummy_prop]) w.write(mol) iomae = osio.getvalue() ctBlockStart = iomae.find('f_m_ct') self.assertNotEqual(ctBlockStart, -1) self.assertIn(dummy_prop, iomae) self.assertNotIn(another_dummy_prop, iomae) mae = Chem.MaeWriter.GetText(mol, -1, [dummy_prop]) self.assertEqual(mae, iomae[ctBlockStart:]) def test_HapticBondsToDative(self): fefile = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'MolStandardize', 'test_data', 'ferrocene.mol') femol = Chem.MolFromMolFile(fefile) newfemol = Chem.rdmolops.HapticBondsToDative(femol) self.assertEqual( Chem.MolToSmiles(newfemol), '[cH]12->[Fe+2]3456789(<-[cH]1[cH]->3[cH-]->4[cH]->52)<-[cH]1[cH]->6[cH]->7[cH-]->8[cH]->91') def test_DativeBondsToHaptic(self): fefile = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'MolStandardize', 'test_data', 'ferrocene.mol') femol = Chem.MolFromMolFile(fefile) newfemol = Chem.rdmolops.HapticBondsToDative(femol) backfemol = Chem.rdmolops.DativeBondsToHaptic(newfemol) self.assertEqual(Chem.MolToSmiles(femol), Chem.MolToSmiles(backfemol)) def testTranslateChiralFlag(self): mol = Chem.MolFromSmiles("C[C@@](N)(F)C[C@](C)(O)F |a:1|") flagMol = Chem.Mol(mol) flagMol.SetIntProp("_MolFileChiralFlag", 1) Chem.TranslateChiralFlagToStereoGroups(flagMol) sgs = flagMol.GetStereoGroups() self.assertEqual(len(sgs), 1) self.assertEqual(len(sgs[0].GetAtoms()), 2) self.assertEqual(sgs[0].GetGroupType(), Chem.StereoGroupType.STEREO_ABSOLUTE) flagMol = Chem.Mol(mol) flagMol.SetIntProp("_MolFileChiralFlag", 0) Chem.TranslateChiralFlagToStereoGroups(flagMol) sgs = flagMol.GetStereoGroups() self.assertEqual(len(sgs), 2) self.assertEqual(sgs[0].GetGroupType(), Chem.StereoGroupType.STEREO_ABSOLUTE) self.assertEqual(len(sgs[0].GetAtoms()), 1) self.assertEqual(sgs[1].GetGroupType(), Chem.StereoGroupType.STEREO_AND) self.assertEqual(len(sgs[1].GetAtoms()), 1) flagMol = Chem.Mol(mol) flagMol.SetIntProp("_MolFileChiralFlag", 0) Chem.TranslateChiralFlagToStereoGroups(flagMol, Chem.StereoGroupType.STEREO_OR) sgs = flagMol.GetStereoGroups() self.assertEqual(len(sgs), 2) self.assertEqual(sgs[0].GetGroupType(), Chem.StereoGroupType.STEREO_ABSOLUTE) self.assertEqual(len(sgs[0].GetAtoms()), 1) self.assertEqual(sgs[1].GetGroupType(), Chem.StereoGroupType.STEREO_OR) self.assertEqual(len(sgs[1].GetAtoms()), 1) def testHasQueryHs(self): for sma, hasQHs in [ ("[#1]", (True, False)), ("[#1,N]", (True, True)), ("[$(C-[H])]", (True, False)), ("[$([C,#1])]", (True, True)), ("[$(c([C;!R;!$(C-[N,O,S]);!$(C-[H])](=O))1naaaa1),$(c([C;!R;!$(C-[N,O,S]);!$(C-[H])](=O))1naa[n,s,o]1)]", (True, False)) ]: pat = Chem.MolFromSmarts(sma) self.assertEqual(Chem.HasQueryHs(pat), hasQHs) def testMolHasQuery(self): m1 = Chem.MolFromSmiles("CCO") self.assertFalse(m1.HasQuery()) m2 = Chem.MolFromSmarts("[#6][#6][#8]") self.assertTrue(m2.HasQuery()) m3 = Chem.MolFromSmiles("CC~O") self.assertTrue(m3.HasQuery()) def testMrvHandling(self): fn1 = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'MarvinParse', 'test_data', 'aspirin.mrv') mol = Chem.MolFromMrvFile(fn1) self.assertIsNotNone(mol) self.assertEqual(mol.GetNumAtoms(), 13) mrv = Chem.MolToMrvBlock(mol) self.assertTrue('' in mrv) self.assertFalse('' in mrv) fName = tempfile.NamedTemporaryFile(suffix='.mrv').name self.assertFalse(os.path.exists(fName)) Chem.MolToMrvFile(mol, fName) self.assertTrue(os.path.exists(fName)) os.unlink(fName) with open(fn1, 'r') as inf: ind = inf.read() mol = Chem.MolFromMrvBlock(ind) self.assertIsNotNone(mol) self.assertEqual(mol.GetNumAtoms(), 13) def testAtomMapsInCanonicalization(self): mol = Chem.MolFromSmiles("[F:1]C([F:2])O") ranks = Chem.CanonicalRankAtoms(mol, breakTies=False, includeAtomMaps=True) self.assertNotEqual(ranks[0], ranks[2]) ranks = Chem.CanonicalRankAtoms(mol, breakTies=False, includeAtomMaps=False) self.assertEqual(ranks[0], ranks[2]) def testExpandAndCollapseAttachmentPoints(self): mol = Chem.MolFromSmarts("*CO") self.assertEqual(mol.GetNumAtoms(), 3) mol.GetAtomWithIdx(2).SetIntProp("molAttchpt", 1) Chem.ExpandAttachmentPoints(mol) self.assertEqual(mol.GetNumAtoms(), 4) self.assertTrue(mol.GetAtomWithIdx(3).HasQuery()) Chem.CollapseAttachmentPoints(mol) self.assertEqual(mol.GetNumAtoms(), 3) Chem.ExpandAttachmentPoints(mol, addAsQueries=False) self.assertEqual(mol.GetNumAtoms(), 4) self.assertFalse(mol.GetAtomWithIdx(3).HasQuery()) Chem.CollapseAttachmentPoints(mol, markedOnly=False) self.assertEqual(mol.GetNumAtoms(), 2) def testAddStereoAnnotations(self): mol = Chem.MolFromSmiles( "C[C@@H]1N[C@H](C)[C@@H]([C@H](C)[C@@H]1C)C1[C@@H](C)O[C@@H](C)[C@@H](C)[C@H]1C/C=C/C |a:5,o1:1,8,o2:14,16,&1:11,18,&2:3,6,r|" ) self.assertIsNotNone(mol) Chem.rdCIPLabeler.AssignCIPLabels(mol) Chem.AddStereoAnnotations(mol) self.assertEqual(mol.GetAtomWithIdx(5).GetProp("atomNote"), "abs (S)") self.assertEqual(mol.GetAtomWithIdx(3).GetProp("atomNote"), "and2") def testIsRingFused(self): molOrig = Chem.MolFromSmiles("C1C(C2CC3CCCCC3C12)C1CCCCC1") mol = Chem.RWMol(molOrig) ri = mol.GetRingInfo() self.assertEqual(ri.NumRings(), 4) fusedRings = [ri.IsRingFused(i) for i in range(ri.NumRings())] self.assertEqual(fusedRings.count(True), 3) self.assertEqual(fusedRings.count(False), 1) atoms = mol.GetSubstructMatch(Chem.MolFromSmarts("[$(C1CCC1)]-@[$(C1CCCCC1)]")) mol.RemoveBond(*atoms) Chem.SanitizeMol(mol) self.assertEqual(Chem.MolToSmiles(mol), "C1CCC(CC2CCC2C2CCCCC2)CC1") self.assertEqual(ri.NumRings(), 3) fusedRings = [ri.IsRingFused(i) for i in range(ri.NumRings())] self.assertEqual(fusedRings.count(True), 0) self.assertEqual(fusedRings.count(False), 3) mol = Chem.RWMol(molOrig) ri = mol.GetRingInfo() self.assertEqual(ri.NumRings(), 4) fusedRings = [ri.IsRingFused(i) for i in range(ri.NumRings())] self.assertEqual(fusedRings.count(True), 3) self.assertEqual(fusedRings.count(False), 1) fusedBonds = [ri.NumFusedBonds(i) for i in range(ri.NumRings())] self.assertEqual(fusedBonds.count(0), 1) self.assertEqual(fusedBonds.count(1), 2) self.assertEqual(fusedBonds.count(2), 1) atoms = mol.GetSubstructMatch( Chem.MolFromSmarts("[$(C1CCCCC1-!@[CX4;R1;r4])].[$(C1C(-!@[CX4;R1;r6])CC1)]")) mol.AddBond(*atoms, Chem.BondType.SINGLE) Chem.SanitizeMol(mol) self.assertEqual(Chem.MolToSmiles(mol), "C1CCC2C(C1)CC1C2C2C3CCCCC3C12") self.assertEqual(ri.NumRings(), 5) fusedRings = [ri.IsRingFused(i) for i in range(ri.NumRings())] self.assertEqual(fusedRings.count(True), 5) self.assertEqual(fusedRings.count(False), 0) fusedBonds = [ri.NumFusedBonds(i) for i in range(ri.NumRings())] self.assertEqual(fusedBonds.count(0), 0) self.assertEqual(fusedBonds.count(1), 2) self.assertEqual(fusedBonds.count(2), 3) def testNeedsHs(self): m = Chem.MolFromSmiles("CO") self.assertTrue(Chem.NeedsHs(m)) mh = Chem.AddHs(m) self.assertFalse(Chem.NeedsHs(mh)) nm = Chem.RWMol(mh) nm.RemoveAtom(3) self.assertTrue(Chem.NeedsHs(m)) def testCountAtomElec(self): m = Chem.MolFromSmiles("c1n(C)ccc1") self.assertEqual(Chem.CountAtomElec(m.GetAtomWithIdx(0)), 1) self.assertEqual(Chem.CountAtomElec(m.GetAtomWithIdx(1)), 2) def testAtomHasConjugatedBond(self): m = Chem.MolFromSmiles("c1n(C)ccc1") self.assertTrue(Chem.AtomHasConjugatedBond(m.GetAtomWithIdx(1))) self.assertFalse(Chem.AtomHasConjugatedBond(m.GetAtomWithIdx(2))) def testSmilesWriteParamsForSMARTS(self): mol = Chem.MolFromSmiles('[NH3][Fe]N') self.assertIsNotNone(mol) ps = Chem.SmilesWriteParams() sma = Chem.MolToSmarts(mol, ps) self.assertEqual(sma, '[#7]->[Fe]-[#7]') ps.includeDativeBonds = False sma = Chem.MolToSmarts(mol, ps) self.assertEqual(sma, '[#7]-[Fe]-[#7]') def testMolToV2KMolBlock(self): mol = Chem.MolFromSmiles('[NH3]->[Fe]') self.assertIsNotNone(mol) mb = Chem.MolToV2KMolBlock(mol) self.assertTrue('V2000' in mb) self.assertTrue(' 1 2 9 0' in mb) def testFindMesoCenters(self): mol = Chem.MolFromSmiles('C[C@@H](Cl)C[C@H](C)Cl') self.assertIsNotNone(mol) centers = Chem.FindMesoCenters(mol) self.assertEqual(len(centers), 1) expected = ((1, 4), ) self.assertEqual(centers, expected) mol = Chem.MolFromSmiles('[CH3:1][C@@H](C)C[C@@H](C)[CH3:1]') self.assertIsNotNone(mol) centers = Chem.FindMesoCenters(mol, includeAtomMaps=True) self.assertEqual(len(centers), 1) expected = ((1, 4), ) self.assertEqual(centers, expected) centers = Chem.FindMesoCenters(mol) self.assertEqual(centers, ()) mol = Chem.MolFromSmiles('[13CH3][C@@H](C)C[C@@H](C)[13CH3]') self.assertIsNotNone(mol) centers = Chem.FindMesoCenters(mol) self.assertEqual(len(centers), 1) expected = ((1, 4), ) self.assertEqual(centers, expected) centers = Chem.FindMesoCenters(mol, includeIsotopes=False) self.assertEqual(centers, ()) def testIgnoreAtomMapNumbers(self): mol = Chem.MolFromSmiles("[NH2:1]c1ccccc1") ps = Chem.SmilesWriteParams() ps.ignoreAtomMapNumbers = True self.assertEqual(Chem.MolToSmiles(mol, ps), "[NH2:1]c1ccccc1") self.assertEqual(Chem.MolToSmiles(mol, ignoreAtomMapNumbers=True), "[NH2:1]c1ccccc1") self.assertEqual(Chem.MolToSmiles(mol, ignoreAtomMapNumbers=False), "c1ccc([NH2:1])cc1") def github7873(self): # this used to segfault Chem.MolFromSmiles("C").GetAtomWithIdx(0).SetPDBResidueInfo(None) def testSplitMolByPDB(self): mol = Chem.MolFromSmiles("C") res2mol = Chem.SplitMolByPDBResidues(mol) self.assertEqual(len(res2mol), 1) self.assertIn("", res2mol) chain2mol = Chem.SplitMolByPDBChainId(mol) self.assertEqual(len(chain2mol), 1) self.assertIn("", chain2mol) def testGithub4570(self): # test sending None to get/set prop m = Chem.Mol() # these should not seg fault for func in ["HasProp", "GetProp", "GetIntProp", "GetDoubleProp"]: try: getattr(m, func)(None) except Exception as e: assert "Python argument types" in str(e), f"{func}: {str(e)}" for func, val in [("SetProp", ""), ("SetIntProp", 1), ("SetDoubleProp", 1.0)]: try: getattr(m, func)(None, val) except Exception as e: assert "Python argument types" in str(e), f"{func}: {str(e)}" def testGithub8467(self): m = Chem.MolFromSmiles('C[C@H](F)Cl |&1:1|') self.assertIsNotNone(m) self.assertEqual(len(m.GetStereoGroups()), 1) m.GetAtomWithIdx(1).SetChiralTag(Chem.CHI_UNSPECIFIED) Chem.CleanupStereoGroups(m) self.assertEqual(len(m.GetStereoGroups()), 0) def testClearPropertyCache(self): m = Chem.MolFromSmiles("CC") self.assertFalse(m.NeedsUpdatePropertyCache()) for atom in m.GetAtoms(): self.assertFalse(atom.NeedsUpdatePropertyCache()) m.ClearPropertyCache() self.assertTrue(m.NeedsUpdatePropertyCache()) for atom in m.GetAtoms(): self.assertTrue(atom.NeedsUpdatePropertyCache()) def testGithub8877(self): m = Chem.MolFromSmarts('CC') self.assertRaises(ValueError, lambda: m.GetAtomWithIdx(0).SetQuery(None)) self.assertRaises(ValueError, lambda: m.GetAtomWithIdx(0).ExpandQuery(None)) self.assertRaises(ValueError, lambda: m.GetBondWithIdx(0).SetQuery(None)) self.assertRaises(ValueError, lambda: m.GetBondWithIdx(0).ExpandQuery(None)) def testBondChiralityInversion(self): mol1 = Chem.MolFromSmiles("Cc1cccc(F)c1-c1c(C)cccc1Cl |wU:7.7,&1:7|") mol2 = Chem.MolFromSmiles("Cc1cccc(F)c1-c1c(C)cccc1Cl |wU:7.6,&1:7|") self.assertNotEqual(mol1.GetBonds()[7].GetStereo(), mol2.GetBonds()[7].GetStereo()) mol1.GetBonds()[7].InvertChirality() self.assertEqual(mol1.GetBonds()[7].GetStereo(), mol2.GetBonds()[7].GetStereo()) def testVerifyWedges(self): rdbase = os.environ['RDBASE'] filename = os.path.join(rdbase, 'Code/GraphMol/FileParsers/test_data/wedgeTests/StereoGroupError.mol') m = Chem.MolFromMolFile(filename) self.assertIsNotNone(m) Chem.ReapplyMolBlockWedging(m) numWedges = sum(1 for b in m.GetBonds() if b.GetBondDir() in (Chem.BondDir.BEGINWEDGE, Chem.BondDir.BEGINDASH)) self.assertEqual(numWedges, 2) Chem.ReapplyMolBlockWedging(m, verify=True) numWedges = sum(1 for b in m.GetBonds() if b.GetBondDir() in (Chem.BondDir.BEGINWEDGE, Chem.BondDir.BEGINDASH)) self.assertEqual(numWedges, 1) def testGithub9101(self): fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data', 'rdkit_chunk_boundary_bug.sdf') sdSup = Chem.SDMolSupplier(fileN) mols = list(sdSup) self.assertEqual(len(mols[0].GetPropsAsDict()["comment"]), 65369) self.assertTrue(mols[1] != None) def testGithub9125(self): m1 = Chem.MolFromSmiles('c1ncc(C)nc1') Chem.Kekulize(m1) self.assertEqual(m1.GetBondBetweenAtoms(3, 5).GetBondType(), Chem.BondType.DOUBLE) m1 = Chem.MolFromSmiles('c1ncc(C)nc1') Chem.Kekulize(m1, canonical=False) self.assertEqual(m1.GetBondBetweenAtoms(3, 5).GetBondType(), Chem.BondType.SINGLE) if __name__ == '__main__': if "RDTESTCASE" in os.environ: suite = unittest.TestSuite() testcases = os.environ["RDTESTCASE"] for name in testcases.split(':'): suite.addTest(TestCase(name)) runner = unittest.TextTestRunner() runner.run(suite) else: unittest.main()