1#
2#  Copyright (C) 2003-2021  Greg Landrum and Rational Discovery LLC
3#         All Rights Reserved
4#
5""" This is a rough coverage test of the python wrapper
6
7it's intended to be shallow, but broad
8
9"""
10
11import os, sys, tempfile, gzip, gc
12import unittest, doctest
13from datetime import datetime, timedelta
14from rdkit import RDConfig, rdBase
15from rdkit import DataStructs
16from rdkit import Chem
17import rdkit.Chem.rdDepictor
18from rdkit.Chem import rdqueries
19import tempfile
20from rdkit import __version__
21
22# Boost functions are NOT found by doctest, this "fixes" them
23#  by adding the doctests to a fake module
24import importlib.util
25spec = importlib.util.spec_from_loader("TestReplaceCore", loader=None)
26TestReplaceCore = importlib.util.module_from_spec(spec)
27code = """
28from rdkit.Chem import ReplaceCore
29def ReplaceCore(*a, **kw):
30    '''%s
31    '''
32    return Chem.ReplaceCore(*a, **kw)
33""" % "\n".join([x.lstrip() for x in Chem.ReplaceCore.__doc__.split("\n")])
34exec(code, TestReplaceCore.__dict__)
35
36
37def load_tests(loader, tests, ignore):
38  tests.addTests(doctest.DocTestSuite(TestReplaceCore))
39  return tests
40
41
42def feq(v1, v2, tol2=1e-4):
43  return abs(v1 - v2) <= tol2
44
45
46def getTotalFormalCharge(mol):
47  totalFormalCharge = 0
48  for atom in mol.GetAtoms():
49    totalFormalCharge += atom.GetFormalCharge()
50  return totalFormalCharge
51
52
53def cmpFormalChargeBondOrder(self, mol1, mol2):
54  self.assertEqual(mol1.GetNumAtoms(), mol2.GetNumAtoms())
55  self.assertEqual(mol1.GetNumBonds(), mol2.GetNumBonds())
56  for i in range(mol1.GetNumAtoms()):
57    self.assertEqual(
58      mol1.GetAtomWithIdx(i).GetFormalCharge(),
59      mol2.GetAtomWithIdx(i).GetFormalCharge())
60  for i in range(mol1.GetNumBonds()):
61    self.assertEqual(mol1.GetBondWithIdx(i).GetBondType(), mol2.GetBondWithIdx(i).GetBondType())
62
63
64def setResidueFormalCharge(mol, res, fc):
65  for query in res:
66    matches = mol.GetSubstructMatches(query)
67    for match in matches:
68      mol.GetAtomWithIdx(match[-1]).SetFormalCharge(fc)
69
70
71def getBtList2(resMolSuppl):
72  btList2 = []
73  while (not resMolSuppl.atEnd()):
74    resMol = next(resMolSuppl)
75    bt = []
76    for bond in resMol.GetBonds():
77      bt.append(int(bond.GetBondTypeAsDouble()))
78    btList2.append(bt)
79  for i in range(len(btList2)):
80    same = True
81    for j in range(len(btList2[i])):
82      if (not i):
83        continue
84      if (same):
85        same = (btList2[i][j] == btList2[i - 1][j])
86    if (i and same):
87      return None
88  return btList2
89
90
91class TestCase(unittest.TestCase):
92
93  def test0Except(self):
94
95    with self.assertRaises(IndexError):
96      Chem.tossit()
97
98  def test1Table(self):
99
100    tbl = Chem.GetPeriodicTable()
101    self.assertTrue(tbl)
102
103    self.assertTrue(feq(tbl.GetAtomicWeight(6), 12.011))
104    self.assertTrue(feq(tbl.GetAtomicWeight("C"), 12.011))
105    self.assertTrue(tbl.GetAtomicNumber('C') == 6)
106    self.assertTrue(feq(tbl.GetRvdw(6), 1.7))
107    self.assertTrue(feq(tbl.GetRvdw("C"), 1.7))
108    self.assertTrue(feq(tbl.GetRcovalent(6), 0.680))
109    self.assertTrue(feq(tbl.GetRcovalent("C"), 0.680))
110    self.assertTrue(tbl.GetDefaultValence(6) == 4)
111    self.assertTrue(tbl.GetDefaultValence("C") == 4)
112    self.assertTrue(tuple(tbl.GetValenceList(6)) == (4, ))
113    self.assertTrue(tuple(tbl.GetValenceList("C")) == (4, ))
114    self.assertTrue(tuple(tbl.GetValenceList(16)) == (2, 4, 6))
115    self.assertTrue(tuple(tbl.GetValenceList("S")) == (2, 4, 6))
116    self.assertTrue(tbl.GetNOuterElecs(6) == 4)
117    self.assertTrue(tbl.GetNOuterElecs("C") == 4)
118    self.assertTrue(tbl.GetMostCommonIsotope(6) == 12)
119    self.assertTrue(tbl.GetMostCommonIsotope('C') == 12)
120    self.assertTrue(tbl.GetMostCommonIsotopeMass(6) == 12.0)
121    self.assertTrue(tbl.GetMostCommonIsotopeMass('C') == 12.0)
122    self.assertTrue(tbl.GetAbundanceForIsotope(6, 12) == 98.93)
123    self.assertTrue(tbl.GetAbundanceForIsotope('C', 12) == 98.93)
124    self.assertTrue(feq(tbl.GetRb0(6), 0.77))
125    self.assertTrue(feq(tbl.GetRb0("C"), 0.77))
126    self.assertTrue(tbl.GetElementSymbol(6) == 'C')
127
128  def test2Atom(self):
129    atom = Chem.Atom(6)
130    self.assertTrue(atom)
131    self.assertTrue(atom.GetAtomicNum() == 6)
132    atom.SetAtomicNum(8)
133    self.assertTrue(atom.GetAtomicNum() == 8)
134
135    atom = Chem.Atom("C")
136    self.assertTrue(atom)
137    self.assertTrue(atom.GetAtomicNum() == 6)
138
139  def test3Bond(self):
140    # No longer relevant, bonds are not constructible from Python
141    pass
142
143  def test4Mol(self):
144    mol = Chem.Mol()
145    self.assertTrue(mol)
146
147  def test5Smiles(self):
148    mol = Chem.MolFromSmiles('n1ccccc1')
149    self.assertTrue(mol)
150    self.assertTrue(mol.GetNumAtoms() == 6)
151    self.assertTrue(mol.GetNumAtoms(1) == 6)
152    self.assertTrue(mol.GetNumAtoms(0) == 11)
153    at = mol.GetAtomWithIdx(2)
154    self.assertTrue(at.GetAtomicNum() == 6)
155    at = mol.GetAtomWithIdx(0)
156    self.assertTrue(at.GetAtomicNum() == 7)
157
158  def _test6Bookmarks(self):
159    mol = Chem.MolFromSmiles('n1ccccc1')
160    self.assertTrue(mol)
161
162    self.assertTrue(not mol.HasAtomBookmark(0))
163    mol.SetAtomBookmark(mol.GetAtomWithIdx(0), 0)
164    mol.SetAtomBookmark(mol.GetAtomWithIdx(1), 1)
165    self.assertTrue(mol.HasAtomBookmark(0))
166    self.assertTrue(mol.HasAtomBookmark(1))
167
168    if 1:
169      self.assertTrue(not mol.HasBondBookmark(0))
170      self.assertTrue(not mol.HasBondBookmark(1))
171      mol.SetBondBookmark(mol.GetBondWithIdx(0), 0)
172      mol.SetBondBookmark(mol.GetBondWithIdx(1), 1)
173      self.assertTrue(mol.HasBondBookmark(0))
174      self.assertTrue(mol.HasBondBookmark(1))
175
176    at = mol.GetAtomWithBookmark(0)
177    self.assertTrue(at)
178    self.assertTrue(at.GetAtomicNum() == 7)
179    mol.ClearAtomBookmark(0)
180    self.assertTrue(not mol.HasAtomBookmark(0))
181    self.assertTrue(mol.HasAtomBookmark(1))
182    mol.ClearAllAtomBookmarks()
183    self.assertTrue(not mol.HasAtomBookmark(0))
184    self.assertTrue(not mol.HasAtomBookmark(1))
185
186    mol.SetAtomBookmark(mol.GetAtomWithIdx(1), 1)
187
188    if 1:
189      self.assertTrue(mol.HasBondBookmark(0))
190      self.assertTrue(mol.HasBondBookmark(1))
191      bond = mol.GetBondWithBookmark(0)
192      self.assertTrue(bond)
193      mol.ClearBondBookmark(0)
194      self.assertTrue(not mol.HasBondBookmark(0))
195      self.assertTrue(mol.HasBondBookmark(1))
196      mol.ClearAllBondBookmarks()
197      self.assertTrue(not mol.HasBondBookmark(0))
198      self.assertTrue(not mol.HasBondBookmark(1))
199
200      self.assertTrue(mol.HasAtomBookmark(1))
201
202  def test7Atom(self):
203    mol = Chem.MolFromSmiles('n1ccccc1C[CH2-]')
204    self.assertTrue(mol)
205    Chem.SanitizeMol(mol)
206    a0 = mol.GetAtomWithIdx(0)
207    a1 = mol.GetAtomWithIdx(1)
208    a6 = mol.GetAtomWithIdx(6)
209    a7 = mol.GetAtomWithIdx(7)
210
211    self.assertTrue(a0.GetAtomicNum() == 7)
212    self.assertTrue(a0.GetSymbol() == 'N')
213    self.assertTrue(a0.GetIdx() == 0)
214
215    aList = [a0, a1, a6, a7]
216    self.assertTrue(a0.GetDegree() == 2)
217    self.assertTrue(a1.GetDegree() == 2)
218    self.assertTrue(a6.GetDegree() == 2)
219    self.assertTrue(a7.GetDegree() == 1)
220    self.assertTrue([x.GetDegree() for x in aList] == [2, 2, 2, 1])
221
222    self.assertTrue([x.GetTotalNumHs() for x in aList] == [0, 1, 2, 2])
223    self.assertTrue([x.GetNumImplicitHs() for x in aList] == [0, 1, 2, 0])
224    self.assertTrue([x.GetExplicitValence() for x in aList] == [3, 3, 2, 3])
225    self.assertTrue([x.GetImplicitValence() for x in aList] == [0, 1, 2, 0])
226    self.assertTrue([x.GetFormalCharge() for x in aList] == [0, 0, 0, -1])
227    self.assertTrue([x.GetNoImplicit() for x in aList] == [0, 0, 0, 1])
228    self.assertTrue([x.GetNumExplicitHs() for x in aList] == [0, 0, 0, 2])
229    self.assertTrue([x.GetIsAromatic() for x in aList] == [1, 1, 0, 0])
230    self.assertTrue([x.GetHybridization() for x in aList]==[Chem.HybridizationType.SP2,Chem.HybridizationType.SP2,
231                                                   Chem.HybridizationType.SP3,Chem.HybridizationType.SP3],\
232                                                   [x.GetHybridization() for x in aList])
233
234  def test8Bond(self):
235    mol = Chem.MolFromSmiles('n1ccccc1CC(=O)O')
236    self.assertTrue(mol)
237    Chem.SanitizeMol(mol)
238    # note bond numbering is funny because of ring closure
239    b0 = mol.GetBondWithIdx(0)
240    b6 = mol.GetBondWithIdx(6)
241    b7 = mol.GetBondWithIdx(7)
242    b8 = mol.GetBondWithIdx(8)
243
244    bList = [b0, b6, b7, b8]
245    self.assertTrue(
246      [x.GetBondType() for x in bList] ==
247      [Chem.BondType.AROMATIC, Chem.BondType.SINGLE, Chem.BondType.DOUBLE, Chem.BondType.SINGLE])
248    self.assertTrue([x.GetIsAromatic() for x in bList] == [1, 0, 0, 0])
249    self.assertEqual(bList[0].GetBondTypeAsDouble(), 1.5)
250    self.assertEqual(bList[1].GetBondTypeAsDouble(), 1.0)
251    self.assertEqual(bList[2].GetBondTypeAsDouble(), 2.0)
252
253    self.assertTrue([x.GetIsConjugated() != 0 for x in bList] == [1, 0, 1, 1],
254                    [x.GetIsConjugated() != 0 for x in bList])
255    self.assertTrue([x.GetBeginAtomIdx() for x in bList] == [0, 6, 7, 7],
256                    [x.GetBeginAtomIdx() for x in bList])
257    self.assertTrue([x.GetBeginAtom().GetIdx() for x in bList] == [0, 6, 7, 7])
258    self.assertTrue([x.GetEndAtomIdx() for x in bList] == [1, 7, 8, 9])
259    self.assertTrue([x.GetEndAtom().GetIdx() for x in bList] == [1, 7, 8, 9])
260
261  def test9Smarts(self):
262    query1 = Chem.MolFromSmarts('C(=O)O')
263    self.assertTrue(query1)
264    query2 = Chem.MolFromSmarts('C(=O)[O,N]')
265    self.assertTrue(query2)
266    query3 = Chem.MolFromSmarts('[$(C(=O)O)]')
267    self.assertTrue(query3)
268
269    mol = Chem.MolFromSmiles('CCC(=O)O')
270    self.assertTrue(mol)
271
272    self.assertTrue(mol.HasSubstructMatch(query1))
273    self.assertTrue(mol.HasSubstructMatch(query2))
274    self.assertTrue(mol.HasSubstructMatch(query3))
275
276    mol = Chem.MolFromSmiles('CCC(=O)N')
277    self.assertTrue(mol)
278
279    self.assertTrue(not mol.HasSubstructMatch(query1))
280    self.assertTrue(mol.HasSubstructMatch(query2))
281    self.assertTrue(not mol.HasSubstructMatch(query3))
282
283  def test10Iterators(self):
284    mol = Chem.MolFromSmiles('CCOC')
285    self.assertTrue(mol)
286
287    for atom in mol.GetAtoms():
288      self.assertTrue(atom)
289    ats = mol.GetAtoms()
290    ats[1]
291    with self.assertRaisesRegex(IndexError, ""):
292      ats[12]
293
294    for bond in mol.GetBonds():
295      self.assertTrue(bond)
296    bonds = mol.GetBonds()
297    bonds[1]
298    with self.assertRaisesRegex(IndexError, ""):
299      bonds[12]
300
301  def test11MolOps(self):
302    mol = Chem.MolFromSmiles('C1=CC=C(C=C1)P(C2=CC=CC=C2)C3=CC=CC=C3')
303    self.assertTrue(mol)
304    smi = Chem.MolToSmiles(mol)
305    Chem.SanitizeMol(mol)
306    nr = Chem.GetSymmSSSR(mol)
307
308    self.assertTrue((len(nr) == 3))
309
310  def test12Smarts(self):
311    query1 = Chem.MolFromSmarts('C(=O)O')
312    self.assertTrue(query1)
313    query2 = Chem.MolFromSmarts('C(=O)[O,N]')
314    self.assertTrue(query2)
315    query3 = Chem.MolFromSmarts('[$(C(=O)O)]')
316    self.assertTrue(query3)
317
318    mol = Chem.MolFromSmiles('CCC(=O)O')
319    self.assertTrue(mol)
320
321    self.assertTrue(mol.HasSubstructMatch(query1))
322    self.assertTrue(mol.GetSubstructMatch(query1) == (2, 3, 4))
323    self.assertTrue(mol.HasSubstructMatch(query2))
324    self.assertTrue(mol.GetSubstructMatch(query2) == (2, 3, 4))
325    self.assertTrue(mol.HasSubstructMatch(query3))
326    self.assertTrue(mol.GetSubstructMatch(query3) == (2, ))
327
328    mol = Chem.MolFromSmiles('CCC(=O)N')
329    self.assertTrue(mol)
330
331    self.assertTrue(not mol.HasSubstructMatch(query1))
332    self.assertTrue(not mol.GetSubstructMatch(query1))
333    self.assertTrue(mol.HasSubstructMatch(query2))
334    self.assertTrue(mol.GetSubstructMatch(query2) == (2, 3, 4))
335    self.assertTrue(not mol.HasSubstructMatch(query3))
336
337    mol = Chem.MolFromSmiles('OC(=O)CC(=O)O')
338    self.assertTrue(mol)
339    self.assertTrue(mol.HasSubstructMatch(query1))
340    self.assertTrue(mol.GetSubstructMatch(query1) == (1, 2, 0))
341    self.assertTrue(mol.GetSubstructMatches(query1) == ((1, 2, 0), (4, 5, 6)))
342    self.assertTrue(mol.HasSubstructMatch(query2))
343    self.assertTrue(mol.GetSubstructMatch(query2) == (1, 2, 0))
344    self.assertTrue(mol.GetSubstructMatches(query2) == ((1, 2, 0), (4, 5, 6)))
345    self.assertTrue(mol.HasSubstructMatch(query3))
346    self.assertTrue(mol.GetSubstructMatches(query3) == ((1, ), (4, )))
347
348  def test13Smarts(self):
349    # previous smarts problems:
350    query = Chem.MolFromSmarts('N(=,-C)')
351    self.assertTrue(query)
352    mol = Chem.MolFromSmiles('N#C')
353    self.assertTrue(not mol.HasSubstructMatch(query))
354    mol = Chem.MolFromSmiles('N=C')
355    self.assertTrue(mol.HasSubstructMatch(query))
356    mol = Chem.MolFromSmiles('NC')
357    self.assertTrue(mol.HasSubstructMatch(query))
358
359    query = Chem.MolFromSmarts('[Cl,$(O)]')
360    mol = Chem.MolFromSmiles('C(=O)O')
361    self.assertTrue(len(mol.GetSubstructMatches(query)) == 2)
362    mol = Chem.MolFromSmiles('C(=N)N')
363    self.assertTrue(len(mol.GetSubstructMatches(query)) == 0)
364
365    query = Chem.MolFromSmarts('[$([O,S]-[!$(*=O)])]')
366    mol = Chem.MolFromSmiles('CC(S)C(=O)O')
367    self.assertTrue(len(mol.GetSubstructMatches(query)) == 1)
368    mol = Chem.MolFromSmiles('C(=O)O')
369    self.assertTrue(len(mol.GetSubstructMatches(query)) == 0)
370
371  def test14Hs(self):
372    m = Chem.MolFromSmiles('CC(=O)[OH]')
373    self.assertEqual(m.GetNumAtoms(), 4)
374    m2 = Chem.AddHs(m)
375    self.assertEqual(m2.GetNumAtoms(), 8)
376    m2 = Chem.RemoveHs(m2)
377    self.assertEqual(m2.GetNumAtoms(), 4)
378
379    m = Chem.MolFromSmiles('CC[H]', False)
380    self.assertEqual(m.GetNumAtoms(), 3)
381    m2 = Chem.MergeQueryHs(m)
382    self.assertEqual(m2.GetNumAtoms(), 2)
383    self.assertTrue(m2.GetAtomWithIdx(1).HasQuery())
384
385    m = Chem.MolFromSmiles('CC[H]', False)
386    self.assertEqual(m.GetNumAtoms(), 3)
387    m1 = Chem.RemoveHs(m)
388    self.assertEqual(m1.GetNumAtoms(), 2)
389    self.assertEqual(m1.GetAtomWithIdx(1).GetNumExplicitHs(), 0)
390    m1 = Chem.RemoveHs(m, updateExplicitCount=True)
391    self.assertEqual(m1.GetNumAtoms(), 2)
392    self.assertEqual(m1.GetAtomWithIdx(1).GetNumExplicitHs(), 1)
393
394    # test merging of mapped hydrogens
395    m = Chem.MolFromSmiles('CC[H]', False)
396    m.GetAtomWithIdx(2).SetProp("molAtomMapNumber", "1")
397    self.assertEqual(m.GetNumAtoms(), 3)
398    m2 = Chem.MergeQueryHs(m, mergeUnmappedOnly=True)
399    self.assertTrue(m2 is not None)
400    self.assertEqual(m2.GetNumAtoms(), 3)
401    self.assertFalse(m2.GetAtomWithIdx(1).HasQuery())
402
403    # here the hydrogen is unmapped
404    #  should be the same as merging all hydrogens
405    m = Chem.MolFromSmiles('CC[H]', False)
406    m.GetAtomWithIdx(1).SetProp("molAtomMapNumber", "1")
407    self.assertEqual(m.GetNumAtoms(), 3)
408    m2 = Chem.MergeQueryHs(m, mergeUnmappedOnly=True)
409    self.assertTrue(m2 is not None)
410    self.assertEqual(m2.GetNumAtoms(), 2)
411    self.assertTrue(m2.GetAtomWithIdx(1).HasQuery())
412
413    # test github758
414    m = Chem.MolFromSmiles('CCC')
415    self.assertEqual(m.GetNumAtoms(), 3)
416    m = Chem.AddHs(m, onlyOnAtoms=(0, 2))
417    self.assertEqual(m.GetNumAtoms(), 9)
418    self.assertEqual(m.GetAtomWithIdx(0).GetDegree(), 4)
419    self.assertEqual(m.GetAtomWithIdx(2).GetDegree(), 4)
420    self.assertEqual(m.GetAtomWithIdx(1).GetDegree(), 2)
421
422  def test15Neighbors(self):
423    m = Chem.MolFromSmiles('CC(=O)[OH]')
424    self.assertTrue(m.GetNumAtoms() == 4)
425
426    a = m.GetAtomWithIdx(1)
427    ns = a.GetNeighbors()
428    self.assertTrue(len(ns) == 3)
429
430    bs = a.GetBonds()
431    self.assertTrue(len(bs) == 3)
432
433    for b in bs:
434      try:
435        a2 = b.GetOtherAtom(a)
436      except Exception:
437        a2 = None
438      self.assertTrue(a2)
439    self.assertTrue(len(bs) == 3)
440
441  def test16Pickle(self):
442    import pickle
443    m = Chem.MolFromSmiles('C1=CN=CC=C1')
444    pkl = pickle.dumps(m)
445    m2 = pickle.loads(pkl)
446    self.assertTrue(type(m2) == Chem.Mol)
447    smi1 = Chem.MolToSmiles(m)
448    smi2 = Chem.MolToSmiles(m2)
449    self.assertTrue(smi1 == smi2)
450
451    pkl = pickle.dumps(Chem.RWMol(m))
452    m2 = pickle.loads(pkl)
453    self.assertTrue(type(m2) == Chem.RWMol)
454    smi1 = Chem.MolToSmiles(m)
455    smi2 = Chem.MolToSmiles(m2)
456    self.assertTrue(smi1 == smi2)
457
458  def test16Props(self):
459    m = Chem.MolFromSmiles('C1=CN=CC=C1')
460    self.assertTrue(not m.HasProp('prop1'))
461    self.assertTrue(not m.HasProp('prop2'))
462    self.assertTrue(not m.HasProp('prop2'))
463    m.SetProp('prop1', 'foob')
464    self.assertTrue(not m.HasProp('prop2'))
465    self.assertTrue(m.HasProp('prop1'))
466    self.assertTrue(m.GetProp('prop1') == 'foob')
467    self.assertTrue(not m.HasProp('propo'))
468    try:
469      m.GetProp('prop2')
470    except KeyError:
471      ok = 1
472    else:
473      ok = 0
474    self.assertTrue(ok)
475
476    # test computed properties
477    m.SetProp('cprop1', 'foo', 1)
478    m.SetProp('cprop2', 'foo2', 1)
479
480    m.ClearComputedProps()
481    self.assertTrue(not m.HasProp('cprop1'))
482    self.assertTrue(not m.HasProp('cprop2'))
483
484    m.SetDoubleProp("a", 2.0)
485    self.assertTrue(m.GetDoubleProp("a") == 2.0)
486
487    try:
488      self.assertTrue(m.GetIntProp("a") == 2.0)
489      raise Exception("Expected runtime exception")
490    except ValueError:
491      pass
492
493    try:
494      self.assertTrue(m.GetUnsignedProp("a") == 2.0)
495      raise Exception("Expected runtime exception")
496    except ValueError:
497      pass
498
499    m.SetDoubleProp("a", -2)
500    self.assertTrue(m.GetDoubleProp("a") == -2.0)
501    m.SetIntProp("a", -2)
502    self.assertTrue(m.GetIntProp("a") == -2)
503
504    try:
505      m.SetUnsignedProp("a", -2)
506      raise Exception("Expected failure with negative unsigned number")
507    except OverflowError:
508      pass
509
510    m.SetBoolProp("a", False)
511    self.assertTrue(m.GetBoolProp("a") == False)
512
513    self.assertEqual(m.GetPropsAsDict(), {'a': False, 'prop1': 'foob'})
514    m.SetDoubleProp("b", 1000.0)
515    m.SetUnsignedProp("c", 2000)
516    m.SetIntProp("d", -2)
517    m.SetUnsignedProp("e", 2, True)
518    self.assertEqual(m.GetPropsAsDict(False, True), {
519      'a': False,
520      'c': 2000,
521      'b': 1000.0,
522      'e': 2,
523      'd': -2,
524      'prop1': 'foob'
525    })
526    m = Chem.MolFromSmiles('C1=CN=CC=C1')
527    m.SetProp("int", "1000")
528    m.SetProp("double", "10000.123")
529    self.assertEqual(m.GetPropsAsDict(), {"int": 1000, "double": 10000.123})
530
531    self.assertEqual(type(m.GetPropsAsDict()['int']), int)
532    self.assertEqual(type(m.GetPropsAsDict()['double']), float)
533
534  def test17Kekulize(self):
535    m = Chem.MolFromSmiles('c1ccccc1')
536    smi = Chem.MolToSmiles(m)
537    self.assertTrue(smi == 'c1ccccc1')
538
539    Chem.Kekulize(m)
540    smi = Chem.MolToSmiles(m)
541    self.assertTrue(smi == 'c1ccccc1')
542
543    m = Chem.MolFromSmiles('c1ccccc1')
544    smi = Chem.MolToSmiles(m)
545    self.assertTrue(smi == 'c1ccccc1')
546
547    Chem.Kekulize(m, 1)
548    smi = Chem.MolToSmiles(m)
549    self.assertTrue(smi == 'C1=CC=CC=C1', smi)
550
551  def test18Paths(self):
552
553    m = Chem.MolFromSmiles("C1CC2C1CC2")
554    #self.assertTrue(len(Chem.FindAllPathsOfLengthN(m,1,useBonds=1))==7)
555    #print(Chem.FindAllPathsOfLengthN(m,3,useBonds=0))
556    self.assertTrue(
557      len(Chem.FindAllPathsOfLengthN(m, 2, useBonds=1)) == 10,
558      Chem.FindAllPathsOfLengthN(m, 2, useBonds=1))
559    self.assertTrue(len(Chem.FindAllPathsOfLengthN(m, 3, useBonds=1)) == 14)
560
561    m = Chem.MolFromSmiles('C1CC1C')
562    self.assertTrue(m)
563    self.assertTrue(len(Chem.FindAllPathsOfLengthN(m, 1, useBonds=1)) == 4)
564    self.assertTrue(len(Chem.FindAllPathsOfLengthN(m, 2, useBonds=1)) == 5)
565    self.assertTrue(
566      len(Chem.FindAllPathsOfLengthN(m, 3, useBonds=1)) == 3,
567      Chem.FindAllPathsOfLengthN(m, 3, useBonds=1))
568    self.assertTrue(
569      len(Chem.FindAllPathsOfLengthN(m, 4, useBonds=1)) == 1,
570      Chem.FindAllPathsOfLengthN(m, 4, useBonds=1))
571    self.assertTrue(
572      len(Chem.FindAllPathsOfLengthN(m, 5, useBonds=1)) == 0,
573      Chem.FindAllPathsOfLengthN(m, 5, useBonds=1))
574
575    #
576    #  Hexane example from Hall-Kier Rev.Comp.Chem. paper
577    #  Rev. Comp. Chem. vol 2, 367-422, (1991)
578    #
579    m = Chem.MolFromSmiles("CCCCCC")
580    self.assertTrue(len(Chem.FindAllPathsOfLengthN(m, 1, useBonds=1)) == 5)
581    self.assertTrue(len(Chem.FindAllPathsOfLengthN(m, 2, useBonds=1)) == 4)
582    self.assertTrue(len(Chem.FindAllPathsOfLengthN(m, 3, useBonds=1)) == 3)
583
584    m = Chem.MolFromSmiles("CCC(C)CC")
585    self.assertTrue(len(Chem.FindAllPathsOfLengthN(m, 1, useBonds=1)) == 5)
586    self.assertTrue(len(Chem.FindAllPathsOfLengthN(m, 2, useBonds=1)) == 5)
587    self.assertTrue(
588      len(Chem.FindAllPathsOfLengthN(m, 3, useBonds=1)) == 4,
589      Chem.FindAllPathsOfLengthN(m, 3, useBonds=1))
590
591    m = Chem.MolFromSmiles("CCCC(C)C")
592    self.assertTrue(len(Chem.FindAllPathsOfLengthN(m, 1, useBonds=1)) == 5)
593    self.assertTrue(len(Chem.FindAllPathsOfLengthN(m, 2, useBonds=1)) == 5)
594    self.assertTrue(len(Chem.FindAllPathsOfLengthN(m, 3, useBonds=1)) == 3)
595
596    m = Chem.MolFromSmiles("CC(C)C(C)C")
597    self.assertTrue(len(Chem.FindAllPathsOfLengthN(m, 1, useBonds=1)) == 5)
598    self.assertTrue(len(Chem.FindAllPathsOfLengthN(m, 2, useBonds=1)) == 6)
599    self.assertTrue(len(Chem.FindAllPathsOfLengthN(m, 3, useBonds=1)) == 4)
600
601    m = Chem.MolFromSmiles("CC(C)(C)CC")
602    self.assertTrue(len(Chem.FindAllPathsOfLengthN(m, 1, useBonds=1)) == 5)
603    self.assertTrue(len(Chem.FindAllPathsOfLengthN(m, 2, useBonds=1)) == 7)
604    self.assertTrue(
605      len(Chem.FindAllPathsOfLengthN(m, 3, useBonds=1)) == 3,
606      Chem.FindAllPathsOfLengthN(m, 3, useBonds=1))
607
608    m = Chem.MolFromSmiles("C1CCCCC1")
609    self.assertTrue(len(Chem.FindAllPathsOfLengthN(m, 1, useBonds=1)) == 6)
610    self.assertTrue(len(Chem.FindAllPathsOfLengthN(m, 2, useBonds=1)) == 6)
611    self.assertTrue(len(Chem.FindAllPathsOfLengthN(m, 3, useBonds=1)) == 6)
612
613    m = Chem.MolFromSmiles("C1CC2C1CC2")
614    self.assertTrue(len(Chem.FindAllPathsOfLengthN(m, 1, useBonds=1)) == 7)
615    self.assertTrue(
616      len(Chem.FindAllPathsOfLengthN(m, 2, useBonds=1)) == 10,
617      Chem.FindAllPathsOfLengthN(m, 2, useBonds=1))
618    self.assertTrue(len(Chem.FindAllPathsOfLengthN(m, 3, useBonds=1)) == 14)
619
620    m = Chem.MolFromSmiles("CC2C1CCC12")
621    self.assertTrue(len(Chem.FindAllPathsOfLengthN(m, 1, useBonds=1)) == 7)
622    self.assertTrue(len(Chem.FindAllPathsOfLengthN(m, 2, useBonds=1)) == 11)
623    # FIX: this result disagrees with the paper (which says 13),
624    #   but it seems right
625    self.assertTrue(
626      len(Chem.FindAllPathsOfLengthN(m, 3, useBonds=1)) == 15,
627      Chem.FindAllPathsOfLengthN(m, 3, useBonds=1))
628
629  def test19Subgraphs(self):
630    m = Chem.MolFromSmiles('C1CC1C')
631    self.assertTrue(m)
632    self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 1, 0)) == 4)
633    self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 2)) == 5)
634    self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 3)) == 4)
635    self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 4)) == 1)
636    self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 5)) == 0)
637
638    #
639    #  Hexane example from Hall-Kier Rev.Comp.Chem. paper
640    #  Rev. Comp. Chem. vol 2, 367-422, (1991)
641    #
642    m = Chem.MolFromSmiles("CCCCCC")
643    self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 1)) == 5)
644    self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 2)) == 4)
645    self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 3)) == 3)
646
647    l = Chem.FindAllSubgraphsOfLengthMToN(m, 1, 3)
648    self.assertEqual(len(l), 3)
649    self.assertEqual(len(l[0]), 5)
650    self.assertEqual(len(l[1]), 4)
651    self.assertEqual(len(l[2]), 3)
652    self.assertRaises(ValueError, lambda: Chem.FindAllSubgraphsOfLengthMToN(m, 4, 3))
653
654    m = Chem.MolFromSmiles("CCC(C)CC")
655    self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 1)) == 5)
656    self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 2)) == 5)
657    self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 3)) == 5)
658
659    m = Chem.MolFromSmiles("CCCC(C)C")
660    self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 1)) == 5)
661    self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 2)) == 5)
662    self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 3)) == 4)
663
664    m = Chem.MolFromSmiles("CC(C)C(C)C")
665    self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 1)) == 5)
666    self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 2)) == 6)
667    self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 3)) == 6)
668
669    m = Chem.MolFromSmiles("CC(C)(C)CC")
670    self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 1)) == 5)
671    self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 2)) == 7)
672    self.assertTrue(
673      len(Chem.FindAllSubgraphsOfLengthN(m, 3)) == 7, Chem.FindAllSubgraphsOfLengthN(m, 3))
674
675    m = Chem.MolFromSmiles("C1CCCCC1")
676    self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 1)) == 6)
677    self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 2)) == 6)
678    self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 3)) == 6)
679    #self.assertTrue(len(Chem.FindUniqueSubgraphsOfLengthN(m,1))==1)
680    self.assertTrue(len(Chem.FindUniqueSubgraphsOfLengthN(m, 2)) == 1)
681    self.assertTrue(len(Chem.FindUniqueSubgraphsOfLengthN(m, 3)) == 1)
682
683    m = Chem.MolFromSmiles("C1CC2C1CC2")
684    self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 1)) == 7)
685    self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 2)) == 10)
686    self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 3)) == 16)
687
688    m = Chem.MolFromSmiles("CC2C1CCC12")
689    self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 1)) == 7)
690    self.assertTrue(len(Chem.FindAllSubgraphsOfLengthN(m, 2)) == 11)
691    self.assertTrue(
692      len(Chem.FindAllSubgraphsOfLengthN(m, 3)) == 18, len(Chem.FindAllSubgraphsOfLengthN(m, 3)))
693
694  def test20IsInRing(self):
695    m = Chem.MolFromSmiles('C1CCC1C')
696    self.assertTrue(m)
697    self.assertTrue(m.GetAtomWithIdx(0).IsInRingSize(4))
698    self.assertTrue(m.GetAtomWithIdx(1).IsInRingSize(4))
699    self.assertTrue(m.GetAtomWithIdx(2).IsInRingSize(4))
700    self.assertTrue(m.GetAtomWithIdx(3).IsInRingSize(4))
701    self.assertTrue(not m.GetAtomWithIdx(4).IsInRingSize(4))
702
703    self.assertTrue(not m.GetAtomWithIdx(0).IsInRingSize(3))
704    self.assertTrue(not m.GetAtomWithIdx(1).IsInRingSize(3))
705    self.assertTrue(not m.GetAtomWithIdx(2).IsInRingSize(3))
706    self.assertTrue(not m.GetAtomWithIdx(3).IsInRingSize(3))
707    self.assertTrue(not m.GetAtomWithIdx(4).IsInRingSize(3))
708
709    self.assertTrue(m.GetBondWithIdx(0).IsInRingSize(4))
710    self.assertTrue(not m.GetBondWithIdx(3).IsInRingSize(4))
711    self.assertTrue(not m.GetBondWithIdx(0).IsInRingSize(3))
712    self.assertTrue(not m.GetBondWithIdx(3).IsInRingSize(3))
713
714  def test21Robustification(self):
715    ok = False
716    # FIX: at the moment I can't figure out how to catch the
717    # actual exception that BPL is throwing when it gets
718    # invalid arguments (Boost.Python.ArgumentError)
719    try:
720      Chem.MolFromSmiles('C=O').HasSubstructMatch(Chem.MolFromSmarts('fiib'))
721    #except ValueError:
722    #  ok=True
723    except Exception:
724      ok = True
725    self.assertTrue(ok)
726
727  def test22DeleteSubstruct(self):
728    query = Chem.MolFromSmarts('C(=O)O')
729    mol = Chem.MolFromSmiles('CCC(=O)O')
730    nmol = Chem.DeleteSubstructs(mol, query)
731
732    self.assertTrue(Chem.MolToSmiles(nmol) == 'CC')
733
734    mol = Chem.MolFromSmiles('CCC(=O)O.O=CO')
735    # now delete only fragments
736    nmol = Chem.DeleteSubstructs(mol, query, 1)
737    self.assertTrue(Chem.MolToSmiles(nmol) == 'CCC(=O)O', Chem.MolToSmiles(nmol))
738
739    mol = Chem.MolFromSmiles('CCC(=O)O.O=CO')
740    nmol = Chem.DeleteSubstructs(mol, query, 0)
741    self.assertTrue(Chem.MolToSmiles(nmol) == 'CC')
742
743    mol = Chem.MolFromSmiles('CCCO')
744    nmol = Chem.DeleteSubstructs(mol, query, 0)
745    self.assertTrue(Chem.MolToSmiles(nmol) == 'CCCO')
746
747    # Issue 96 prevented this from working:
748    mol = Chem.MolFromSmiles('CCC(=O)O.O=CO')
749    nmol = Chem.DeleteSubstructs(mol, query, 1)
750    self.assertTrue(Chem.MolToSmiles(nmol) == 'CCC(=O)O')
751    nmol = Chem.DeleteSubstructs(nmol, query, 1)
752    self.assertTrue(Chem.MolToSmiles(nmol) == 'CCC(=O)O')
753    nmol = Chem.DeleteSubstructs(nmol, query, 0)
754    self.assertTrue(Chem.MolToSmiles(nmol) == 'CC')
755
756  def test23MolFileParsing(self):
757    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
758                         'triazine.mol')
759    #fileN = "../FileParsers/test_data/triazine.mol"
760    with open(fileN, 'r') as inF:
761      inD = inF.read()
762    m1 = Chem.MolFromMolBlock(inD)
763    self.assertTrue(m1 is not None)
764    self.assertTrue(m1.GetNumAtoms() == 9)
765
766    m1 = Chem.MolFromMolFile(fileN)
767    self.assertTrue(m1 is not None)
768    self.assertTrue(m1.GetNumAtoms() == 9)
769
770    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
771                         'triazine.mof')
772    self.assertRaises(IOError, lambda: Chem.MolFromMolFile(fileN))
773
774    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
775                         'list-query.mol')
776    query = Chem.MolFromMolFile(fileN)
777    smi = Chem.MolToSmiles(query)
778    self.assertEqual(smi, 'c1ccccc1')
779    smi = Chem.MolToSmarts(query)
780    self.assertEqual(smi, '[#6]1:[#6]:[#6]:[#6]:[#6]:[#6,#7,#15]:1', smi)
781
782    query = Chem.MolFromMolFile(fileN, sanitize=False)
783    smi = Chem.MolToSmiles(query)
784    self.assertEqual(smi, 'C1=CC=CC=C1')
785    query.UpdatePropertyCache()
786    smi = Chem.MolToSmarts(query)
787    self.assertEqual(smi, '[#6]1=[#6]-[#6]=[#6]-[#6]=[#6,#7,#15]-1')
788    smi = "C1=CC=CC=C1"
789    mol = Chem.MolFromSmiles(smi, 0)
790    self.assertTrue(mol.HasSubstructMatch(query))
791    Chem.SanitizeMol(mol)
792    self.assertTrue(not mol.HasSubstructMatch(query))
793
794    mol = Chem.MolFromSmiles('N1=CC=CC=C1', 0)
795    self.assertTrue(mol.HasSubstructMatch(query))
796    mol = Chem.MolFromSmiles('S1=CC=CC=C1', 0)
797    self.assertTrue(not mol.HasSubstructMatch(query))
798    mol = Chem.MolFromSmiles('P1=CC=CC=C1', 0)
799    self.assertTrue(mol.HasSubstructMatch(query))
800
801    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
802                         'issue123.mol')
803    mol = Chem.MolFromMolFile(fileN)
804    self.assertTrue(mol)
805    self.assertEqual(mol.GetNumAtoms(), 23)
806    mol = Chem.MolFromMolFile(fileN, removeHs=False)
807    self.assertTrue(mol)
808    self.assertEqual(mol.GetNumAtoms(), 39)
809
810  # test23 was for Chem.DaylightFingerprint, which is deprecated
811
812  def test24RDKFingerprint(self):
813    from rdkit import DataStructs
814    m1 = Chem.MolFromSmiles('C1=CC=CC=C1')
815    fp1 = Chem.RDKFingerprint(m1)
816    self.assertTrue(len(fp1) == 2048)
817    m2 = Chem.MolFromSmiles('C1=CC=CC=C1')
818    fp2 = Chem.RDKFingerprint(m2)
819
820    tmp = DataStructs.TanimotoSimilarity(fp1, fp2)
821    self.assertTrue(tmp == 1.0, tmp)
822
823    m2 = Chem.MolFromSmiles('C1=CC=CC=N1')
824    fp2 = Chem.RDKFingerprint(m2)
825    self.assertTrue(len(fp2) == 2048)
826    tmp = DataStructs.TanimotoSimilarity(fp1, fp2)
827    self.assertTrue(tmp < 1.0, tmp)
828    self.assertTrue(tmp > 0.0, tmp)
829
830    fp3 = Chem.RDKFingerprint(m1, tgtDensity=0.3)
831    self.assertTrue(len(fp3) < 2048)
832
833    m1 = Chem.MolFromSmiles('C1=CC=CC=C1')
834    fp1 = Chem.RDKFingerprint(m1)
835    m2 = Chem.MolFromSmiles('C1=CC=CC=N1')
836    fp2 = Chem.RDKFingerprint(m2)
837    self.assertNotEqual(fp1, fp2)
838
839    atomInvariants = [1] * 6
840    fp1 = Chem.RDKFingerprint(m1, atomInvariants=atomInvariants)
841    fp2 = Chem.RDKFingerprint(m2, atomInvariants=atomInvariants)
842    self.assertEqual(fp1, fp2)
843
844    m2 = Chem.MolFromSmiles('C1CCCCN1')
845    fp1 = Chem.RDKFingerprint(m1, atomInvariants=atomInvariants, useBondOrder=False)
846    fp2 = Chem.RDKFingerprint(m2, atomInvariants=atomInvariants, useBondOrder=False)
847    self.assertEqual(fp1, fp2)
848
849    # rooted at atom
850    m1 = Chem.MolFromSmiles('CCCCCO')
851    fp1 = Chem.RDKFingerprint(m1, 1, 4, nBitsPerHash=1, fromAtoms=[0])
852    self.assertEqual(fp1.GetNumOnBits(), 4)
853    m1 = Chem.MolFromSmiles('CCCCCO')
854    fp1 = Chem.RDKFingerprint(m1, 1, 4, nBitsPerHash=1, fromAtoms=[0, 5])
855    self.assertEqual(fp1.GetNumOnBits(), 8)
856
857    # test sf.net issue 270:
858    fp1 = Chem.RDKFingerprint(m1, atomInvariants=[x.GetAtomicNum() + 10 for x in m1.GetAtoms()])
859
860    # atomBits
861    m1 = Chem.MolFromSmiles('CCCO')
862    l = []
863    fp1 = Chem.RDKFingerprint(m1, minPath=1, maxPath=2, nBitsPerHash=1, atomBits=l)
864    self.assertEqual(fp1.GetNumOnBits(), 4)
865    self.assertEqual(len(l), m1.GetNumAtoms())
866    self.assertEqual(len(l[0]), 2)
867    self.assertEqual(len(l[1]), 3)
868    self.assertEqual(len(l[2]), 4)
869    self.assertEqual(len(l[3]), 2)
870
871  def test25SDMolSupplier(self):
872    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
873                         'NCI_aids_few.sdf')
874    #fileN = "../FileParsers/test_data/NCI_aids_few.sdf"
875    sdSup = Chem.SDMolSupplier(fileN)
876    molNames = [
877      "48", "78", "128", "163", "164", "170", "180", "186", "192", "203", "210", "211", "213",
878      "220", "229", "256"
879    ]
880
881    chgs192 = {8: 1, 11: 1, 15: -1, 18: -1, 20: 1, 21: 1, 23: -1, 25: -1}
882    i = 0
883    for mol in sdSup:
884      self.assertTrue(mol)
885      self.assertTrue(mol.GetProp("_Name") == molNames[i])
886      i += 1
887      if (mol.GetProp("_Name") == "192"):
888        # test parsed charges on one of the molecules
889        for id in chgs192.keys():
890          self.assertTrue(mol.GetAtomWithIdx(id).GetFormalCharge() == chgs192[id])
891    self.assertRaises(StopIteration, lambda: next(sdSup))
892    sdSup.reset()
893
894    ns = [mol.GetProp("_Name") for mol in sdSup]
895    self.assertTrue(ns == molNames)
896
897    sdSup = Chem.SDMolSupplier(fileN, 0)
898    for mol in sdSup:
899      self.assertTrue(not mol.HasProp("numArom"))
900
901    sdSup = Chem.SDMolSupplier(fileN)
902    self.assertTrue(len(sdSup) == 16)
903    mol = sdSup[5]
904    self.assertTrue(mol.GetProp("_Name") == "170")
905
906    # test handling of H removal:
907    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
908                         'withHs.sdf')
909    sdSup = Chem.SDMolSupplier(fileN)
910    m = next(sdSup)
911    self.assertTrue(m)
912    self.assertTrue(m.GetNumAtoms() == 23)
913    m = next(sdSup)
914    self.assertTrue(m)
915    self.assertTrue(m.GetNumAtoms() == 28)
916
917    sdSup = Chem.SDMolSupplier(fileN, removeHs=False)
918    m = next(sdSup)
919    self.assertTrue(m)
920    self.assertTrue(m.GetNumAtoms() == 39)
921    m = next(sdSup)
922    self.assertTrue(m)
923    self.assertTrue(m.GetNumAtoms() == 30)
924
925    with open(fileN, 'rb') as dFile:
926      d = dFile.read()
927    sdSup.SetData(d)
928    m = next(sdSup)
929    self.assertTrue(m)
930    self.assertTrue(m.GetNumAtoms() == 23)
931    m = next(sdSup)
932    self.assertTrue(m)
933    self.assertTrue(m.GetNumAtoms() == 28)
934
935    sdSup.SetData(d, removeHs=False)
936    m = next(sdSup)
937    self.assertTrue(m)
938    self.assertTrue(m.GetNumAtoms() == 39)
939    m = next(sdSup)
940    self.assertTrue(m)
941    self.assertTrue(m.GetNumAtoms() == 30)
942
943    # test strictParsing1:
944    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
945                         'strictLax1.sdf')
946    #strict from file
947    sdSup = Chem.SDMolSupplier(fileN, strictParsing=True)
948
949    i = 0
950    for mol in sdSup:
951      self.assertTrue(mol.HasProp("_Name"))
952      if (i == 0):
953        self.assertTrue(not mol.HasProp("ID"))
954      self.assertTrue(not mol.HasProp("ANOTHER_PROPERTY"))
955      i += 1
956    self.assertTrue(i == 2)
957
958    #lax from file
959    sdSup = Chem.SDMolSupplier(fileN, strictParsing=False)
960
961    i = 0
962    for mol in sdSup:
963      self.assertTrue(mol.HasProp("_Name"))
964      self.assertTrue(mol.HasProp("ID"))
965      self.assertTrue(mol.HasProp("ANOTHER_PROPERTY"))
966      i += 1
967    self.assertTrue(i == 2)
968
969    #strict from text
970    with open(fileN, 'rb') as dFile:
971      d = dFile.read()
972    sdSup = Chem.SDMolSupplier()
973    sdSup.SetData(d, strictParsing=True)
974
975    i = 0
976    for mol in sdSup:
977      self.assertTrue(mol.HasProp("_Name"))
978      if (i == 0):
979        self.assertTrue(not mol.HasProp("ID"))
980      self.assertTrue(not mol.HasProp("ANOTHER_PROPERTY"))
981      i += 1
982    self.assertTrue(i == 2)
983
984    #lax from text
985    sdSup = Chem.SDMolSupplier()
986    sdSup.SetData(d, strictParsing=False)
987
988    i = 0
989    for mol in sdSup:
990      self.assertTrue(mol.HasProp("_Name"))
991      self.assertTrue(mol.HasProp("ID"))
992      self.assertTrue(mol.HasProp("ANOTHER_PROPERTY"))
993      i += 1
994    self.assertTrue(i == 2)
995
996    # test strictParsing2:
997    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
998                         'strictLax2.sdf')
999    #strict from file
1000    sdSup = Chem.SDMolSupplier(fileN, strictParsing=True)
1001
1002    i = 0
1003    for mol in sdSup:
1004      self.assertTrue(mol.HasProp("_Name"))
1005      self.assertTrue(mol.HasProp("ID"))
1006      self.assertTrue(mol.GetProp("ID") == "Lig1")
1007      self.assertTrue(mol.HasProp("ANOTHER_PROPERTY"))
1008      self.assertTrue(mol.GetProp("ANOTHER_PROPERTY") == \
1009        "No blank line before dollars\n" \
1010        "$$$$\n" \
1011        "Structure1\n" \
1012        "csChFnd70/05230312262D")
1013      i += 1
1014    self.assertTrue(i == 1)
1015
1016    #lax from file
1017    sdSup = Chem.SDMolSupplier(fileN, strictParsing=False)
1018
1019    i = 0
1020    for mol in sdSup:
1021      self.assertTrue(mol.HasProp("_Name"))
1022      self.assertTrue(mol.HasProp("ID"))
1023      self.assertTrue(mol.GetProp("ID") == "Lig2")
1024      self.assertTrue(mol.HasProp("ANOTHER_PROPERTY"))
1025      self.assertTrue(mol.GetProp("ANOTHER_PROPERTY") == "Value2")
1026      i += 1
1027    self.assertTrue(i == 1)
1028
1029    #strict from text
1030    with open(fileN, 'rb') as dFile:
1031      d = dFile.read()
1032    sdSup = Chem.SDMolSupplier()
1033    sdSup.SetData(d, strictParsing=True)
1034
1035    i = 0
1036    for mol in sdSup:
1037      self.assertTrue(mol.HasProp("_Name"))
1038      self.assertTrue(mol.HasProp("ID"))
1039      self.assertTrue(mol.GetProp("ID") == "Lig1")
1040      self.assertTrue(mol.HasProp("ANOTHER_PROPERTY"))
1041      self.assertTrue(mol.GetProp("ANOTHER_PROPERTY") == \
1042        "No blank line before dollars\n" \
1043        "$$$$\n" \
1044        "Structure1\n" \
1045        "csChFnd70/05230312262D")
1046      i += 1
1047    self.assertTrue(i == 1)
1048
1049    #lax from text
1050    sdSup = Chem.SDMolSupplier()
1051    sdSup.SetData(d, strictParsing=False)
1052
1053    i = 0
1054    for mol in sdSup:
1055      self.assertTrue(mol.HasProp("_Name"))
1056      self.assertTrue(mol.HasProp("ID"))
1057      self.assertTrue(mol.GetProp("ID") == "Lig2")
1058      self.assertTrue(mol.HasProp("ANOTHER_PROPERTY"))
1059      self.assertTrue(mol.GetProp("ANOTHER_PROPERTY") == "Value2")
1060      i += 1
1061    self.assertTrue(i == 1)
1062
1063  def test26SmiMolSupplier(self):
1064    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
1065                         'first_200.tpsa.csv')
1066    #fileN = "../FileParsers/test_data/first_200.tpsa.csv"
1067    smiSup = Chem.SmilesMolSupplier(fileN, ",", 0, -1)
1068    mol = smiSup[16]
1069    self.assertTrue(mol.GetProp("TPSA") == "46.25")
1070
1071    mol = smiSup[8]
1072    self.assertTrue(mol.GetProp("TPSA") == "65.18")
1073
1074    self.assertTrue(len(smiSup) == 200)
1075
1076    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
1077                         'fewSmi.csv')
1078    #fileN = "../FileParsers/test_data/fewSmi.csv"
1079    smiSup = Chem.SmilesMolSupplier(fileN, delimiter=",", smilesColumn=1, nameColumn=0, titleLine=0)
1080    names = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
1081    i = 0
1082    for mol in smiSup:
1083      self.assertTrue(mol.GetProp("_Name") == names[i])
1084      i += 1
1085
1086    mol = smiSup[3]
1087
1088    self.assertTrue(mol.GetProp("_Name") == "4")
1089    self.assertTrue(mol.GetProp("Column_2") == "82.78")
1090
1091    # and test doing a supplier from text:
1092    with open(fileN, 'r') as inF:
1093      inD = inF.read()
1094    smiSup.SetData(inD, delimiter=",", smilesColumn=1, nameColumn=0, titleLine=0)
1095    names = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
1096    i = 0
1097    # iteration interface:
1098    for mol in smiSup:
1099      self.assertTrue(mol.GetProp("_Name") == names[i])
1100      i += 1
1101    self.assertTrue(i == 10)
1102    # random access:
1103    mol = smiSup[3]
1104    self.assertTrue(len(smiSup) == 10)
1105    self.assertTrue(mol.GetProp("_Name") == "4")
1106    self.assertTrue(mol.GetProp("Column_2") == "82.78")
1107
1108    # issue 113:
1109    smiSup.SetData(inD, delimiter=",", smilesColumn=1, nameColumn=0, titleLine=0)
1110    self.assertTrue(len(smiSup) == 10)
1111
1112    # and test failure handling:
1113    inD = """mol-1,CCC
1114mol-2,CCCC
1115mol-3,fail
1116mol-4,CCOC
1117    """
1118    smiSup.SetData(inD, delimiter=",", smilesColumn=1, nameColumn=0, titleLine=0)
1119    # there are 4 entries in the supplier:
1120    self.assertTrue(len(smiSup) == 4)
1121    # but the 3rd is a None:
1122    self.assertTrue(smiSup[2] is None)
1123
1124
1125    text="Id SMILES Column_2\n"+\
1126    "mol-1 C 1.0\n"+\
1127    "mol-2 CC 4.0\n"+\
1128    "mol-4 CCCC 16.0"
1129    smiSup.SetData(text, delimiter=" ", smilesColumn=1, nameColumn=0, titleLine=1)
1130    self.assertTrue(len(smiSup) == 3)
1131    self.assertTrue(smiSup[0])
1132    self.assertTrue(smiSup[1])
1133    self.assertTrue(smiSup[2])
1134    m = [x for x in smiSup]
1135    self.assertTrue(smiSup[2])
1136    self.assertTrue(len(m) == 3)
1137    self.assertTrue(m[0].GetProp("Column_2") == "1.0")
1138
1139    # test simple parsing and Issue 114:
1140    smis = ['CC', 'CCC', 'CCOC', 'CCCOCC', 'CCCOCCC']
1141    inD = '\n'.join(smis)
1142    smiSup.SetData(inD, delimiter=",", smilesColumn=0, nameColumn=-1, titleLine=0)
1143    self.assertTrue(len(smiSup) == 5)
1144    m = [x for x in smiSup]
1145    self.assertTrue(smiSup[4])
1146    self.assertTrue(len(m) == 5)
1147
1148    # order dependence:
1149    smiSup.SetData(inD, delimiter=",", smilesColumn=0, nameColumn=-1, titleLine=0)
1150    self.assertTrue(smiSup[4])
1151    self.assertTrue(len(smiSup) == 5)
1152
1153    # this was a nasty BC:
1154    # asking for a particular entry with a higher index than what we've
1155    # already seen resulted in a duplicate:
1156    smis = ['CC', 'CCC', 'CCOC', 'CCCCOC']
1157    inD = '\n'.join(smis)
1158    smiSup.SetData(inD, delimiter=",", smilesColumn=0, nameColumn=-1, titleLine=0)
1159    m = next(smiSup)
1160    m = smiSup[3]
1161    self.assertTrue(len(smiSup) == 4)
1162
1163    with self.assertRaisesRegex(Exception, ""):
1164      smiSup[4]
1165
1166    smiSup.SetData(inD, delimiter=",", smilesColumn=0, nameColumn=-1, titleLine=0)
1167    with self.assertRaisesRegex(Exception, ""):
1168      smiSup[4]
1169
1170    sys.stderr.write(
1171      '>>> This may result in an infinite loop.  It should finish almost instantly\n')
1172    self.assertEqual(len(smiSup), 4)
1173    sys.stderr.write('<<< OK, it finished.\n')
1174
1175  def test27SmilesWriter(self):
1176    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
1177                         'fewSmi.csv')
1178    #fileN = "../FileParsers/test_data/fewSmi.csv"
1179
1180    smiSup = Chem.SmilesMolSupplier(fileN, delimiter=",", smilesColumn=1, nameColumn=0, titleLine=0)
1181    propNames = []
1182    propNames.append("Column_2")
1183    ofile = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'Wrap', 'test_data',
1184                         'outSmiles.txt')
1185    writer = Chem.SmilesWriter(ofile)
1186    writer.SetProps(propNames)
1187    for mol in smiSup:
1188      writer.write(mol)
1189    writer.flush()
1190
1191  def test28SmilesReverse(self):
1192    names = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
1193    props = [
1194      "34.14", "25.78", "106.51", "82.78", "60.16", "87.74", "37.38", "77.28", "65.18", "0.00"
1195    ]
1196    ofile = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'Wrap', 'test_data',
1197                         'outSmiles.txt')
1198    #ofile = "test_data/outSmiles.csv"
1199    smiSup = Chem.SmilesMolSupplier(ofile)
1200    i = 0
1201    for mol in smiSup:
1202      #print([repr(x) for x in mol.GetPropNames()])
1203      self.assertTrue(mol.GetProp("_Name") == names[i])
1204      self.assertTrue(mol.GetProp("Column_2") == props[i])
1205      i += 1
1206
1207  def writerSDFile(self):
1208    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
1209                         'NCI_aids_few.sdf')
1210    #fileN = "../FileParsers/test_data/NCI_aids_few.sdf"
1211    ofile = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'Wrap', 'test_data',
1212                         'outNCI_few.sdf')
1213    writer = Chem.SDWriter(ofile)
1214    sdSup = Chem.SDMolSupplier(fileN)
1215    for mol in sdSup:
1216      writer.write(mol)
1217    writer.flush()
1218
1219  def test29SDWriterLoop(self):
1220    self.writerSDFile()
1221    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'Wrap', 'test_data',
1222                         'outNCI_few.sdf')
1223    sdSup = Chem.SDMolSupplier(fileN)
1224    molNames = [
1225      "48", "78", "128", "163", "164", "170", "180", "186", "192", "203", "210", "211", "213",
1226      "220", "229", "256"
1227    ]
1228    chgs192 = {8: 1, 11: 1, 15: -1, 18: -1, 20: 1, 21: 1, 23: -1, 25: -1}
1229    i = 0
1230
1231    for mol in sdSup:
1232      #print('mol:',mol)
1233      #print('\t',molNames[i])
1234      self.assertTrue(mol.GetProp("_Name") == molNames[i])
1235      i += 1
1236      if (mol.GetProp("_Name") == "192"):
1237        # test parsed charges on one of the molecules
1238        for id in chgs192.keys():
1239          self.assertTrue(mol.GetAtomWithIdx(id).GetFormalCharge() == chgs192[id])
1240
1241  def test30Issues109and110(self):
1242    """ issues 110 and 109 were both related to handling of explicit Hs in
1243       SMILES input.
1244
1245    """
1246    m1 = Chem.MolFromSmiles('N12[CH](SC(C)(C)[CH]1C(O)=O)[CH](C2=O)NC(=O)[CH](N)c3ccccc3')
1247    self.assertTrue(m1.GetNumAtoms() == 24)
1248    m2 = Chem.MolFromSmiles(
1249      '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')
1250    self.assertTrue(m2.GetNumAtoms() == 24)
1251
1252    smi1 = Chem.MolToSmiles(m1)
1253    smi2 = Chem.MolToSmiles(m2)
1254    self.assertTrue(smi1 == smi2)
1255
1256    m1 = Chem.MolFromSmiles('[H]CCl')
1257    self.assertTrue(m1.GetNumAtoms() == 2)
1258    self.assertTrue(m1.GetAtomWithIdx(0).GetNumExplicitHs() == 1)
1259    m1 = Chem.MolFromSmiles('[H][CH2]Cl')
1260    self.assertTrue(m1.GetNumAtoms() == 2)
1261    self.assertTrue(m1.GetAtomWithIdx(0).GetNumExplicitHs() == 3)
1262    m2 = Chem.AddHs(m1)
1263    self.assertTrue(m2.GetNumAtoms() == 5)
1264    m2 = Chem.RemoveHs(m2)
1265    self.assertTrue(m2.GetNumAtoms() == 2)
1266
1267  def test31ChiralitySmiles(self):
1268    m1 = Chem.MolFromSmiles('F[C@](Br)(I)Cl')
1269    self.assertTrue(m1 is not None)
1270    self.assertTrue(m1.GetNumAtoms() == 5)
1271    self.assertTrue(Chem.MolToSmiles(m1, 1) == 'F[C@](Cl)(Br)I', Chem.MolToSmiles(m1, 1))
1272
1273    m1 = Chem.MolFromSmiles('CC1C[C@@]1(Cl)F')
1274    self.assertTrue(m1 is not None)
1275    self.assertTrue(m1.GetNumAtoms() == 6)
1276    self.assertTrue(Chem.MolToSmiles(m1, 1) == 'CC1C[C@]1(F)Cl', Chem.MolToSmiles(m1, 1))
1277
1278    m1 = Chem.MolFromSmiles('CC1C[C@]1(Cl)F')
1279    self.assertTrue(m1 is not None)
1280    self.assertTrue(m1.GetNumAtoms() == 6)
1281    self.assertTrue(Chem.MolToSmiles(m1, 1) == 'CC1C[C@@]1(F)Cl', Chem.MolToSmiles(m1, 1))
1282
1283  def test31aChiralitySubstructs(self):
1284    m1 = Chem.MolFromSmiles('CC1C[C@@]1(Cl)F')
1285    self.assertTrue(m1 is not None)
1286    self.assertTrue(m1.GetNumAtoms() == 6)
1287    self.assertTrue(Chem.MolToSmiles(m1, 1) == 'CC1C[C@]1(F)Cl', Chem.MolToSmiles(m1, 1))
1288
1289    m2 = Chem.MolFromSmiles('CC1C[C@]1(Cl)F')
1290    self.assertTrue(m2 is not None)
1291    self.assertTrue(m2.GetNumAtoms() == 6)
1292    self.assertTrue(Chem.MolToSmiles(m2, 1) == 'CC1C[C@@]1(F)Cl', Chem.MolToSmiles(m2, 1))
1293
1294    self.assertTrue(m1.HasSubstructMatch(m1))
1295    self.assertTrue(m1.HasSubstructMatch(m2))
1296    self.assertTrue(m1.HasSubstructMatch(m1, useChirality=True))
1297    self.assertTrue(not m1.HasSubstructMatch(m2, useChirality=True))
1298
1299  def _test32MolFilesWithChirality(self):
1300    inD = """chiral1.mol
1301  ChemDraw10160313232D
1302
1303  5  4  0  0  0  0  0  0  0  0999 V2000
1304    0.0553    0.6188    0.0000 F   0  0  0  0  0  0  0  0  0  0  0  0
1305    0.0553   -0.2062    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
1306    0.7697   -0.6188    0.0000 I   0  0  0  0  0  0  0  0  0  0  0  0
1307   -0.6592   -0.6188    0.0000 Br  0  0  0  0  0  0  0  0  0  0  0  0
1308   -0.7697   -0.2062    0.0000 Cl  0  0  0  0  0  0  0  0  0  0  0  0
1309  1  2  1  0
1310  2  3  1  0
1311  2  4  1  1
1312  2  5  1  0
1313M  END
1314"""
1315    m1 = Chem.MolFromMolBlock(inD)
1316    self.assertTrue(m1 is not None)
1317    self.assertTrue(m1.GetNumAtoms() == 5)
1318    self.assertTrue(smi == 'F[C@](Cl)(Br)I', smi)
1319
1320    inD = """chiral2.cdxml
1321  ChemDraw10160314052D
1322
1323  5  4  0  0  0  0  0  0  0  0999 V2000
1324    0.0553    0.6188    0.0000 F   0  0  0  0  0  0  0  0  0  0  0  0
1325    0.0553   -0.2062    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
1326    0.7697   -0.6188    0.0000 I   0  0  0  0  0  0  0  0  0  0  0  0
1327   -0.6592   -0.6188    0.0000 Br  0  0  0  0  0  0  0  0  0  0  0  0
1328   -0.7697   -0.2062    0.0000 Cl  0  0  0  0  0  0  0  0  0  0  0  0
1329  1  2  1  0
1330  2  3  1  0
1331  2  4  1  6
1332  2  5  1  0
1333M  END
1334"""
1335    m1 = Chem.MolFromMolBlock(inD)
1336    self.assertTrue(m1 is not None)
1337    self.assertTrue(m1.GetNumAtoms() == 5)
1338    self.assertTrue(Chem.MolToSmiles(m1, 1) == 'F[C@@](Cl)(Br)I')
1339
1340    inD = """chiral1.mol
1341  ChemDraw10160313232D
1342
1343  5  4  0  0  0  0  0  0  0  0999 V2000
1344    0.0553    0.6188    0.0000 F   0  0  0  0  0  0  0  0  0  0  0  0
1345    0.0553   -0.2062    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
1346   -0.7697   -0.2062    0.0000 Cl  0  0  0  0  0  0  0  0  0  0  0  0
1347   -0.6592   -0.6188    0.0000 Br  0  0  0  0  0  0  0  0  0  0  0  0
1348    0.7697   -0.6188    0.0000 I   0  0  0  0  0  0  0  0  0  0  0  0
1349  1  2  1  0
1350  2  3  1  0
1351  2  4  1  1
1352  2  5  1  0
1353M  END
1354"""
1355    m1 = Chem.MolFromMolBlock(inD)
1356    self.assertTrue(m1 is not None)
1357    self.assertTrue(m1.GetNumAtoms() == 5)
1358    self.assertTrue(Chem.MolToSmiles(m1, 1) == 'F[C@](Cl)(Br)I')
1359
1360    inD = """chiral1.mol
1361  ChemDraw10160313232D
1362
1363  5  4  0  0  0  0  0  0  0  0999 V2000
1364    0.0553   -0.2062    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
1365   -0.7697   -0.2062    0.0000 Cl  0  0  0  0  0  0  0  0  0  0  0  0
1366   -0.6592   -0.6188    0.0000 Br  0  0  0  0  0  0  0  0  0  0  0  0
1367    0.7697   -0.6188    0.0000 I   0  0  0  0  0  0  0  0  0  0  0  0
1368    0.0553    0.6188    0.0000 F   0  0  0  0  0  0  0  0  0  0  0  0
1369  1  2  1  0
1370  1  3  1  1
1371  1  4  1  0
1372  1  5  1  0
1373M  END
1374"""
1375    m1 = Chem.MolFromMolBlock(inD)
1376    self.assertTrue(m1 is not None)
1377    self.assertTrue(m1.GetNumAtoms() == 5)
1378    self.assertTrue(Chem.MolToSmiles(m1, 1) == 'F[C@](Cl)(Br)I')
1379
1380    inD = """chiral3.mol
1381  ChemDraw10160314362D
1382
1383  4  3  0  0  0  0  0  0  0  0999 V2000
1384    0.4125    0.6188    0.0000 F   0  0  0  0  0  0  0  0  0  0  0  0
1385    0.4125   -0.2062    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
1386   -0.3020   -0.6188    0.0000 Br  0  0  0  0  0  0  0  0  0  0  0  0
1387   -0.4125   -0.2062    0.0000 Cl  0  0  0  0  0  0  0  0  0  0  0  0
1388  1  2  1  0
1389  2  3  1  1
1390  2  4  1  0
1391M  END
1392
1393"""
1394    m1 = Chem.MolFromMolBlock(inD)
1395    self.assertTrue(m1 is not None)
1396    self.assertTrue(m1.GetNumAtoms() == 4)
1397    self.assertTrue(Chem.MolToSmiles(m1, 1) == 'F[C@H](Cl)Br')
1398
1399    inD = """chiral4.mol
1400  ChemDraw10160314362D
1401
1402  4  3  0  0  0  0  0  0  0  0999 V2000
1403    0.4125    0.6188    0.0000 F   0  0  0  0  0  0  0  0  0  0  0  0
1404    0.4125   -0.2062    0.0000 N   0  0  0  0  0  0  0  0  0  0  0  0
1405   -0.3020   -0.6188    0.0000 Br  0  0  0  0  0  0  0  0  0  0  0  0
1406   -0.4125   -0.2062    0.0000 Cl  0  0  0  0  0  0  0  0  0  0  0  0
1407  1  2  1  0
1408  2  3  1  1
1409  2  4  1  0
1410M  END
1411
1412"""
1413    m1 = Chem.MolFromMolBlock(inD)
1414    self.assertTrue(m1 is not None)
1415    self.assertTrue(m1.GetNumAtoms() == 4)
1416    self.assertTrue(Chem.MolToSmiles(m1, 1) == 'FN(Cl)Br')
1417
1418    inD = """chiral5.mol
1419  ChemDraw10160314362D
1420
1421  4  3  0  0  0  0  0  0  0  0999 V2000
1422    0.4125    0.6188    0.0000 F   0  0  0  0  0  0  0  0  0  0  0  0
1423    0.4125   -0.2062    0.0000 N   0  0  0  0  0  0  0  0  0  0  0  0
1424   -0.3020   -0.6188    0.0000 Br  0  0  0  0  0  0  0  0  0  0  0  0
1425   -0.4125   -0.2062    0.0000 Cl  0  0  0  0  0  0  0  0  0  0  0  0
1426  1  2  1  0
1427  2  3  1  1
1428  2  4  1  0
1429M  CHG  1   2   1
1430M  END
1431
1432"""
1433    m1 = Chem.MolFromMolBlock(inD)
1434    self.assertTrue(m1 is not None)
1435    self.assertTrue(m1.GetNumAtoms() == 4)
1436    self.assertTrue(Chem.MolToSmiles(m1, 1) == 'F[N@H+](Cl)Br')
1437
1438    inD = """Case 10-14-3
1439  ChemDraw10140308512D
1440
1441  4  3  0  0  0  0  0  0  0  0999 V2000
1442   -0.8250   -0.4125    0.0000 F   0  0  0  0  0  0  0  0  0  0  0  0
1443    0.0000   -0.4125    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
1444    0.8250   -0.4125    0.0000 Cl  0  0  0  0  0  0  0  0  0  0  0  0
1445    0.0000    0.4125    0.0000 Br  0  0  0  0  0  0  0  0  0  0  0  0
1446  1  2  1  0
1447  2  3  1  0
1448  2  4  1  1
1449M  END
1450"""
1451    m1 = Chem.MolFromMolBlock(inD)
1452    self.assertTrue(m1 is not None)
1453    self.assertTrue(m1.GetNumAtoms() == 4)
1454    self.assertTrue(Chem.MolToSmiles(m1, 1) == 'F[C@H](Cl)Br')
1455
1456    inD = """Case 10-14-4
1457  ChemDraw10140308512D
1458
1459  4  3  0  0  0  0  0  0  0  0999 V2000
1460   -0.8250   -0.4125    0.0000 F   0  0  0  0  0  0  0  0  0  0  0  0
1461    0.0000   -0.4125    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
1462    0.8250   -0.4125    0.0000 Cl  0  0  0  0  0  0  0  0  0  0  0  0
1463    0.0000    0.4125    0.0000 Br  0  0  0  0  0  0  0  0  0  0  0  0
1464  1  2  1  0
1465  2  3  1  1
1466  2  4  1  0
1467M  END
1468
1469"""
1470    m1 = Chem.MolFromMolBlock(inD)
1471    self.assertTrue(m1 is not None)
1472    self.assertTrue(m1.GetNumAtoms() == 4)
1473    self.assertTrue(Chem.MolToSmiles(m1, 1) == 'F[C@H](Cl)Br')
1474
1475    inD = """chiral4.mol
1476  ChemDraw10160315172D
1477
1478  6  6  0  0  0  0  0  0  0  0999 V2000
1479   -0.4422    0.1402    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
1480   -0.4422   -0.6848    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
1481    0.2723   -0.2723    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
1482   -0.8547    0.8547    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
1483    0.6848    0.4422    0.0000 F   0  0  0  0  0  0  0  0  0  0  0  0
1484    0.8547   -0.8547    0.0000 Cl  0  0  0  0  0  0  0  0  0  0  0  0
1485  1  2  1  0
1486  2  3  1  0
1487  3  1  1  0
1488  1  4  1  0
1489  3  5  1  1
1490  3  6  1  0
1491M  END
1492"""
1493    m1 = Chem.MolFromMolBlock(inD)
1494    self.assertTrue(m1 is not None)
1495    self.assertTrue(m1.GetNumAtoms() == 6)
1496    self.assertTrue(Chem.MolToSmiles(m1, 1) == 'CC1C[C@@]1(F)Cl', Chem.MolToSmiles(m1, 1))
1497
1498    inD = """chiral4.mol
1499  ChemDraw10160315172D
1500
1501  6  6  0  0  0  0  0  0  0  0999 V2000
1502   -0.4422    0.1402    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
1503   -0.4422   -0.6848    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
1504    0.2723   -0.2723    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
1505   -0.8547    0.8547    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
1506    0.6848    0.4422    0.0000 F   0  0  0  0  0  0  0  0  0  0  0  0
1507    0.8547   -0.8547    0.0000 Cl  0  0  0  0  0  0  0  0  0  0  0  0
1508  1  2  1  0
1509  2  3  1  0
1510  3  1  1  0
1511  1  4  1  0
1512  3  5  1  6
1513  3  6  1  0
1514M  END
1515"""
1516    m1 = Chem.MolFromMolBlock(inD)
1517    self.assertTrue(m1 is not None)
1518    self.assertTrue(m1.GetNumAtoms() == 6)
1519    self.assertTrue(Chem.MolToSmiles(m1, 1) == 'CC1C[C@]1(F)Cl', Chem.MolToSmiles(m1, 1))
1520
1521  def test33Issue65(self):
1522    """ issue 65 relates to handling of [H] in SMARTS
1523
1524    """
1525    m1 = Chem.MolFromSmiles('OC(O)(O)O')
1526    m2 = Chem.MolFromSmiles('OC(O)O')
1527    m3 = Chem.MolFromSmiles('OCO')
1528    q1 = Chem.MolFromSmarts('OC[H]', 1)
1529    q2 = Chem.MolFromSmarts('O[C;H1]', 1)
1530    q3 = Chem.MolFromSmarts('O[C;H1][H]', 1)
1531
1532    self.assertTrue(not m1.HasSubstructMatch(q1))
1533    self.assertTrue(not m1.HasSubstructMatch(q2))
1534    self.assertTrue(not m1.HasSubstructMatch(q3))
1535
1536    self.assertTrue(m2.HasSubstructMatch(q1))
1537    self.assertTrue(m2.HasSubstructMatch(q2))
1538    self.assertTrue(m2.HasSubstructMatch(q3))
1539
1540    self.assertTrue(m3.HasSubstructMatch(q1))
1541    self.assertTrue(not m3.HasSubstructMatch(q2))
1542    self.assertTrue(not m3.HasSubstructMatch(q3))
1543
1544    m1H = Chem.AddHs(m1)
1545    m2H = Chem.AddHs(m2)
1546    m3H = Chem.AddHs(m3)
1547    q1 = Chem.MolFromSmarts('OC[H]')
1548    q2 = Chem.MolFromSmarts('O[C;H1]')
1549    q3 = Chem.MolFromSmarts('O[C;H1][H]')
1550
1551    self.assertTrue(not m1H.HasSubstructMatch(q1))
1552    self.assertTrue(not m1H.HasSubstructMatch(q2))
1553    self.assertTrue(not m1H.HasSubstructMatch(q3))
1554
1555    #m2H.Debug()
1556    self.assertTrue(m2H.HasSubstructMatch(q1))
1557    self.assertTrue(m2H.HasSubstructMatch(q2))
1558    self.assertTrue(m2H.HasSubstructMatch(q3))
1559
1560    self.assertTrue(m3H.HasSubstructMatch(q1))
1561    self.assertTrue(not m3H.HasSubstructMatch(q2))
1562    self.assertTrue(not m3H.HasSubstructMatch(q3))
1563
1564  def test34Issue124(self):
1565    """ issue 124 relates to calculation of the distance matrix
1566
1567    """
1568    m = Chem.MolFromSmiles('CC=C')
1569    d = Chem.GetDistanceMatrix(m, 0)
1570    self.assertTrue(feq(d[0, 1], 1.0))
1571    self.assertTrue(feq(d[0, 2], 2.0))
1572    # force an update:
1573    d = Chem.GetDistanceMatrix(m, 1, 0, 1)
1574    self.assertTrue(feq(d[0, 1], 1.0))
1575    self.assertTrue(feq(d[0, 2], 1.5))
1576
1577  def test35ChiralityPerception(self):
1578    """ Test perception of chirality and CIP encoding
1579    """
1580    m = Chem.MolFromSmiles('F[C@]([C@])(Cl)Br')
1581    Chem.AssignStereochemistry(m, 1)
1582    self.assertTrue(m.GetAtomWithIdx(1).HasProp('_CIPCode'))
1583    self.assertFalse(m.GetAtomWithIdx(2).HasProp('_CIPCode'))
1584    Chem.RemoveStereochemistry(m)
1585    self.assertFalse(m.GetAtomWithIdx(1).HasProp('_CIPCode'))
1586
1587    m = Chem.MolFromSmiles('F[C@H](C)C')
1588    Chem.AssignStereochemistry(m, 1)
1589    self.assertTrue(m.GetAtomWithIdx(1).GetChiralTag() == Chem.ChiralType.CHI_UNSPECIFIED)
1590    self.assertFalse(m.GetAtomWithIdx(1).HasProp('_CIPCode'))
1591
1592    m = Chem.MolFromSmiles('F\\C=C/Cl')
1593    self.assertTrue(m.GetBondWithIdx(0).GetStereo() == Chem.BondStereo.STEREONONE)
1594    self.assertTrue(m.GetBondWithIdx(1).GetStereo() == Chem.BondStereo.STEREOZ)
1595    atoms = m.GetBondWithIdx(1).GetStereoAtoms()
1596    self.assertTrue(0 in atoms)
1597    self.assertTrue(3 in atoms)
1598    self.assertTrue(m.GetBondWithIdx(2).GetStereo() == Chem.BondStereo.STEREONONE)
1599    Chem.RemoveStereochemistry(m)
1600    self.assertTrue(m.GetBondWithIdx(1).GetStereo() == Chem.BondStereo.STEREONONE)
1601
1602    m = Chem.MolFromSmiles('F\\C=CCl')
1603    self.assertTrue(m.GetBondWithIdx(1).GetStereo() == Chem.BondStereo.STEREONONE)
1604
1605  def checkDefaultBondProperties(self, m):
1606    for bond in m.GetBonds():
1607      self.assertIn(bond.GetBondType(), [Chem.BondType.SINGLE, Chem.BondType.DOUBLE])
1608      self.assertEqual(bond.GetBondDir(), Chem.BondDir.NONE)
1609      self.assertEqual(list(bond.GetStereoAtoms()), [])
1610      self.assertEqual(bond.GetStereo(), Chem.BondStereo.STEREONONE)
1611
1612  def assertHasDoubleBondStereo(self, smi):
1613    m = Chem.MolFromSmiles(smi)
1614
1615    self.checkDefaultBondProperties(m)
1616
1617    Chem.FindPotentialStereoBonds(m)
1618
1619    for bond in m.GetBonds():
1620      self.assertIn(bond.GetBondType(), [Chem.BondType.SINGLE, Chem.BondType.DOUBLE])
1621      self.assertEqual(bond.GetBondDir(), Chem.BondDir.NONE)
1622
1623      if bond.GetBondType() == Chem.BondType.DOUBLE:
1624        self.assertEqual(bond.GetStereo(), Chem.BondStereo.STEREOANY)
1625        self.assertEqual(len(list(bond.GetStereoAtoms())), 2)
1626      else:
1627        self.assertEqual(list(bond.GetStereoAtoms()), [])
1628        self.assertEqual(bond.GetStereo(), Chem.BondStereo.STEREONONE)
1629
1630  def testFindPotentialStereoBonds(self):
1631    self.assertHasDoubleBondStereo("FC=CF")
1632    self.assertHasDoubleBondStereo("FC(Cl)=C(Br)I")
1633    self.assertHasDoubleBondStereo("FC=CC=CC=CCl")
1634    self.assertHasDoubleBondStereo("C1CCCCC1C=CC1CCCCC1")
1635
1636  def assertDoesNotHaveDoubleBondStereo(self, smi):
1637    m = Chem.MolFromSmiles(smi)
1638    self.checkDefaultBondProperties(m)
1639    Chem.FindPotentialStereoBonds(m)
1640    self.checkDefaultBondProperties(m)
1641
1642  def testFindPotentialStereoBondsShouldNotFindThisDoubleBondAsStereo(self):
1643    self.assertDoesNotHaveDoubleBondStereo("FC(F)=CF")
1644    self.assertDoesNotHaveDoubleBondStereo("C=C")
1645    self.assertDoesNotHaveDoubleBondStereo("C1CCCCC1C(C1CCCCC1)=CC1CCCCC1")
1646
1647  def assertDoubleBondStereo(self, smi, stereo):
1648    mol = Chem.MolFromSmiles(smi)
1649
1650    bond = mol.GetBondWithIdx(1)
1651    self.assertEqual(bond.GetBondType(), Chem.BondType.DOUBLE)
1652    self.assertEqual(bond.GetStereo(), stereo)
1653    self.assertEqual(list(bond.GetStereoAtoms()), [0, 3])
1654
1655  def allStereoBonds(self, bonds):
1656    for bond in bonds:
1657      self.assertEqual(len(list(bond.GetStereoAtoms())), 2)
1658
1659  def testBondSetStereo(self):
1660    for testAssignStereo in [False, True]:
1661      mol = Chem.MolFromSmiles("FC=CF")
1662      Chem.FindPotentialStereoBonds(mol)
1663
1664      for bond in mol.GetBonds():
1665        if (bond.GetBondType() == Chem.BondType.DOUBLE
1666            and bond.GetStereo() == Chem.BondStereo.STEREOANY):
1667          break
1668      self.assertEqual(bond.GetBondType(), Chem.BondType.DOUBLE)
1669      self.assertEqual(bond.GetStereo(), Chem.BondStereo.STEREOANY)
1670      self.assertEqual(list(bond.GetStereoAtoms()), [0, 3])
1671
1672      bond.SetStereo(Chem.BondStereo.STEREOTRANS)
1673      self.assertEqual(bond.GetStereo(), Chem.BondStereo.STEREOTRANS)
1674      if testAssignStereo:  # should be invariant of Chem.AssignStereochemistry being called
1675        Chem.AssignStereochemistry(mol, force=True)
1676      smi = Chem.MolToSmiles(mol, isomericSmiles=True)
1677      self.allStereoBonds([bond])
1678      self.assertEqual(smi, "F/C=C/F")
1679      self.assertDoubleBondStereo(smi, Chem.BondStereo.STEREOE)
1680
1681      bond.SetStereo(Chem.BondStereo.STEREOCIS)
1682      self.assertEqual(bond.GetStereo(), Chem.BondStereo.STEREOCIS)
1683      if testAssignStereo:
1684        Chem.AssignStereochemistry(mol, force=True)
1685      smi = Chem.MolToSmiles(mol, isomericSmiles=True)
1686      self.allStereoBonds([bond])
1687      self.assertEqual(smi, r"F/C=C\F")
1688      self.assertDoubleBondStereo(smi, Chem.BondStereo.STEREOZ)
1689
1690  def recursive_enumerate_stereo_bonds(self, mol, done_bonds, bonds):
1691    if not bonds:
1692      yield done_bonds, Chem.Mol(mol)
1693      return
1694
1695    bond = bonds[0]
1696    child_bonds = bonds[1:]
1697    self.assertEqual(len(list(bond.GetStereoAtoms())), 2)
1698    bond.SetStereo(Chem.BondStereo.STEREOTRANS)
1699    for isomer in self.recursive_enumerate_stereo_bonds(mol, done_bonds + [Chem.BondStereo.STEREOE],
1700                                                        child_bonds):
1701      yield isomer
1702
1703    self.assertEqual(len(list(bond.GetStereoAtoms())), 2)
1704    bond.SetStereo(Chem.BondStereo.STEREOCIS)
1705    for isomer in self.recursive_enumerate_stereo_bonds(mol, done_bonds + [Chem.BondStereo.STEREOZ],
1706                                                        child_bonds):
1707      yield isomer
1708
1709  def testBondSetStereoDifficultCase(self):
1710    unspec_smiles = "CCC=CC(CO)=C(C)CC"
1711    mol = Chem.MolFromSmiles(unspec_smiles)
1712    Chem.FindPotentialStereoBonds(mol)
1713
1714    stereo_bonds = []
1715    for bond in mol.GetBonds():
1716      if bond.GetStereo() == Chem.BondStereo.STEREOANY:
1717        stereo_bonds.append(bond)
1718
1719    isomers = set()
1720    for bond_stereo, isomer in self.recursive_enumerate_stereo_bonds(mol, [], stereo_bonds):
1721      self.allStereoBonds(stereo_bonds)
1722      isosmi = Chem.MolToSmiles(isomer, isomericSmiles=True)
1723      self.allStereoBonds(stereo_bonds)
1724
1725      self.assertNotIn(isosmi, isomers)
1726      isomers.add(isosmi)
1727
1728      isomol = Chem.MolFromSmiles(isosmi)
1729      round_trip_stereo = [
1730        b.GetStereo() for b in isomol.GetBonds() if b.GetStereo() != Chem.BondStereo.STEREONONE
1731      ]
1732
1733      self.assertEqual(bond_stereo, round_trip_stereo)
1734
1735    self.assertEqual(len(isomers), 4)
1736
1737  def getNumUnspecifiedBondStereo(self, smi):
1738    mol = Chem.MolFromSmiles(smi)
1739    Chem.FindPotentialStereoBonds(mol)
1740
1741    count = 0
1742    for bond in mol.GetBonds():
1743      if bond.GetStereo() == Chem.BondStereo.STEREOANY:
1744        count += 1
1745
1746    return count
1747
1748  def testBondSetStereoReallyDifficultCase(self):
1749    # this one is much trickier because a double bond can gain and
1750    # lose it's stereochemistry based upon whether 2 other double
1751    # bonds have the same or different stereo chemistry.
1752
1753    unspec_smiles = "CCC=CC(C=CCC)=C(CO)CC"
1754    mol = Chem.MolFromSmiles(unspec_smiles)
1755    Chem.FindPotentialStereoBonds(mol)
1756
1757    stereo_bonds = []
1758    for bond in mol.GetBonds():
1759      if bond.GetStereo() == Chem.BondStereo.STEREOANY:
1760        stereo_bonds.append(bond)
1761
1762    self.assertEqual(len(stereo_bonds), 2)
1763
1764    isomers = set()
1765    for bond_stereo, isomer in self.recursive_enumerate_stereo_bonds(mol, [], stereo_bonds):
1766      isosmi = Chem.MolToSmiles(isomer, isomericSmiles=True)
1767      isomers.add(isosmi)
1768
1769    self.assertEqual(len(isomers), 3)
1770
1771    # one of these then gains a new stereo bond due to the
1772    # introduction of a new symmetry
1773    counts = {}
1774    for isosmi in isomers:
1775      num_unspecified = self.getNumUnspecifiedBondStereo(isosmi)
1776      counts[num_unspecified] = counts.get(num_unspecified, 0) + 1
1777
1778    # 2 of the isomers don't have any unspecified bond stereo centers
1779    # left, 1 does
1780    self.assertEqual(counts, {0: 2, 1: 1})
1781
1782  def assertBondSetStereoIsAlwaysEquivalent(self, all_smiles, desired_stereo, bond_idx):
1783    refSmiles = None
1784    for smi in all_smiles:
1785      mol = Chem.MolFromSmiles(smi)
1786
1787      doubleBond = None
1788      for bond in mol.GetBonds():
1789        if bond.GetBondType() == Chem.BondType.DOUBLE:
1790          doubleBond = bond
1791
1792      self.assertTrue(doubleBond is not None)
1793
1794      Chem.FindPotentialStereoBonds(mol)
1795      doubleBond.SetStereo(desired_stereo)
1796
1797      isosmi = Chem.MolToSmiles(mol, isomericSmiles=True)
1798
1799      if refSmiles is None:
1800        refSmiles = isosmi
1801
1802      self.assertEqual(refSmiles, isosmi)
1803
1804  def testBondSetStereoAllHalogens(self):
1805    # can't get much more brutal than this test
1806    from itertools import combinations, permutations
1807    halogens = ['F', 'Cl', 'Br', 'I']
1808
1809    # binary double bond stereo
1810    for unique_set in combinations(halogens, 2):
1811      all_smiles = []
1812      for fmt in ['%sC=C%s', 'C(%s)=C%s']:
1813        for ordering in permutations(unique_set):
1814          all_smiles.append(fmt % ordering)
1815
1816      #print(fmt, all_smiles)
1817      for desired_stereo in [Chem.BondStereo.STEREOTRANS, Chem.BondStereo.STEREOCIS]:
1818        self.assertBondSetStereoIsAlwaysEquivalent(all_smiles, desired_stereo, 1)
1819
1820    # tertiary double bond stereo
1821    for unique_set in combinations(halogens, 3):
1822      for mono_side in unique_set:
1823        halogens_left = list(unique_set)
1824        halogens_left.remove(mono_side)
1825        for binary_side in combinations(halogens_left, 2):
1826          all_smiles = []
1827
1828          for binary_side_permutation in permutations(binary_side):
1829            all_smiles.append('%sC=C(%s)%s' % ((mono_side, ) + binary_side_permutation))
1830            all_smiles.append('C(%s)=C(%s)%s' % ((mono_side, ) + binary_side_permutation))
1831
1832            all_smiles.append('%sC(%s)=C%s' % (binary_side_permutation + (mono_side, )))
1833            all_smiles.append('C(%s)(%s)=C%s' % (binary_side_permutation + (mono_side, )))
1834
1835          #print(all_smiles)
1836          for desired_stereo in [Chem.BondStereo.STEREOTRANS, Chem.BondStereo.STEREOCIS]:
1837            self.assertBondSetStereoIsAlwaysEquivalent(all_smiles, desired_stereo, 1)
1838
1839    # quaternary double bond stereo
1840    for unique_ordering in permutations(halogens):
1841      left_side = unique_ordering[:2]
1842      rght_side = unique_ordering[2:]
1843
1844      all_smiles = []
1845      for left_side_permutation in permutations(left_side):
1846        for rght_side_permutation in permutations(rght_side):
1847          for smifmt in ['%sC(%s)=C(%s)%s', 'C(%s)(%s)=C(%s)%s']:
1848            all_smiles.append(smifmt % (left_side_permutation + rght_side_permutation))
1849
1850      #print(all_smiles)
1851      for desired_stereo in [Chem.BondStereo.STEREOTRANS, Chem.BondStereo.STEREOCIS]:
1852        self.assertBondSetStereoIsAlwaysEquivalent(all_smiles, desired_stereo, 1)
1853
1854  def testBondSetStereoAtoms(self):
1855    # use this difficult molecule that only generates 4 isomers, but
1856    # assume all double bonds are stereo!
1857    unspec_smiles = "CCC=CC(C=CCC)=C(CO)CC"
1858    mol = Chem.MolFromSmiles(unspec_smiles)
1859
1860    def getNbr(atom, exclude):
1861      for nbr in atom.GetNeighbors():
1862        if nbr.GetIdx() not in exclude:
1863          return nbr
1864      raise ValueError("No neighbor found!")
1865
1866    double_bonds = []
1867    for bond in mol.GetBonds():
1868      if bond.GetBondType() == 2:
1869        double_bonds.append(bond)
1870
1871        exclude = {bond.GetBeginAtomIdx(), bond.GetEndAtomIdx()}
1872        bgnNbr = getNbr(bond.GetBeginAtom(), exclude)
1873        endNbr = getNbr(bond.GetEndAtom(), exclude)
1874
1875        bond.SetStereoAtoms(bgnNbr.GetIdx(), endNbr.GetIdx())
1876
1877    self.assertEqual(len(double_bonds), 3)
1878
1879    import itertools
1880    stereos = [Chem.BondStereo.STEREOE, Chem.BondStereo.STEREOZ]
1881    isomers = set()
1882    for stereo_config in itertools.product(stereos, repeat=len(double_bonds)):
1883      for bond, stereo in zip(double_bonds, stereo_config):
1884        bond.SetStereo(stereo)
1885      smi = Chem.MolToSmiles(mol, True)
1886      isomers.add(smi)
1887
1888    # the dependent double bond stereo isn't picked up by this, should it?
1889    self.assertEqual(len(isomers), 6)
1890
1891    # round tripping them through one more time does pick up the dependency, so meh?
1892    round_trip_isomers = set()
1893    for smi in isomers:
1894      isosmi = Chem.MolToSmiles(Chem.MolFromSmiles(smi), True)
1895      round_trip_isomers.add(isosmi)
1896
1897    self.assertEqual(len(round_trip_isomers), 4)
1898
1899  def test36SubstructMatchStr(self):
1900    """ test the _SubstructMatchStr function """
1901    query = Chem.MolFromSmarts('[n,p]1ccccc1')
1902    self.assertTrue(query)
1903    mol = Chem.MolFromSmiles('N1=CC=CC=C1')
1904    self.assertTrue(mol.HasSubstructMatch(query))
1905    self.assertTrue(Chem._HasSubstructMatchStr(mol.ToBinary(), query))
1906    mol = Chem.MolFromSmiles('S1=CC=CC=C1')
1907    self.assertTrue(not Chem._HasSubstructMatchStr(mol.ToBinary(), query))
1908    self.assertTrue(not mol.HasSubstructMatch(query))
1909    mol = Chem.MolFromSmiles('P1=CC=CC=C1')
1910    self.assertTrue(mol.HasSubstructMatch(query))
1911    self.assertTrue(Chem._HasSubstructMatchStr(mol.ToBinary(), query))
1912
1913  def test37SanitException(self):
1914    mol = Chem.MolFromSmiles('CC(C)(C)(C)C', 0)
1915    self.assertTrue(mol)
1916    self.assertRaises(ValueError, lambda: Chem.SanitizeMol(mol))
1917
1918  def test38TDTSuppliers(self):
1919    data = """$SMI<Cc1nnc(N)nc1C>
1920CAS<17584-12-2>
1921|
1922$SMI<Cc1n[nH]c(=O)nc1N>
1923CAS<~>
1924|
1925$SMI<Cc1n[nH]c(=O)[nH]c1=O>
1926CAS<932-53-6>
1927|
1928$SMI<Cc1nnc(NN)nc1O>
1929CAS<~>
1930|"""
1931    suppl = Chem.TDTMolSupplier()
1932    suppl.SetData(data, "CAS")
1933    i = 0
1934    for mol in suppl:
1935      self.assertTrue(mol)
1936      self.assertTrue(mol.GetNumAtoms())
1937      self.assertTrue(mol.HasProp("CAS"))
1938      self.assertTrue(mol.HasProp("_Name"))
1939      self.assertTrue(mol.GetProp("CAS") == mol.GetProp("_Name"))
1940      self.assertTrue(mol.GetNumConformers() == 0)
1941      i += 1
1942    self.assertTrue(i == 4)
1943    self.assertTrue(len(suppl) == 4)
1944
1945  def test38Issue266(self):
1946    """ test issue 266: generation of kekulized smiles"""
1947    mol = Chem.MolFromSmiles('c1ccccc1')
1948    Chem.Kekulize(mol)
1949    smi = Chem.MolToSmiles(mol)
1950    self.assertTrue(smi == 'c1ccccc1')
1951    smi = Chem.MolToSmiles(mol, kekuleSmiles=True)
1952    self.assertTrue(smi == 'C1=CC=CC=C1')
1953
1954  def test39Issue273(self):
1955    """ test issue 273: MolFileComments and MolFileInfo props ending up in SD files
1956
1957    """
1958    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'Wrap', 'test_data',
1959                         'outNCI_few.sdf')
1960    suppl = Chem.SDMolSupplier(fileN)
1961    ms = [x for x in suppl]
1962    for m in ms:
1963      self.assertTrue(m.HasProp('_MolFileInfo'))
1964      self.assertTrue(m.HasProp('_MolFileComments'))
1965    fName = tempfile.NamedTemporaryFile(suffix='.sdf', delete=False).name
1966    w = Chem.SDWriter(fName)
1967    w.SetProps(ms[0].GetPropNames())
1968    for m in ms:
1969      w.write(m)
1970    w = None
1971
1972    with open(fName, 'r') as txtFile:
1973      txt = txtFile.read()
1974    os.unlink(fName)
1975    self.assertTrue(txt.find('MolFileInfo') == -1)
1976    self.assertTrue(txt.find('MolFileComments') == -1)
1977
1978  def test40SmilesRootedAtAtom(self):
1979    """ test the rootAtAtom functionality
1980
1981    """
1982    smi = 'CN(C)C'
1983    m = Chem.MolFromSmiles(smi)
1984
1985    self.assertTrue(Chem.MolToSmiles(m) == 'CN(C)C')
1986    self.assertTrue(Chem.MolToSmiles(m, rootedAtAtom=1) == 'N(C)(C)C')
1987
1988  def test41SetStreamIndices(self):
1989    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
1990                         'NCI_aids_few.sdf')
1991    allIndices = []
1992    ifs = open(fileN, 'rb')
1993    addIndex = True
1994    line = True
1995    pos = 0
1996    while (line):
1997      if (addIndex):
1998        pos = ifs.tell()
1999      line = ifs.readline().decode('utf-8')
2000      if (line):
2001        if (addIndex):
2002          allIndices.append(pos)
2003        addIndex = (line[:4] == '$$$$')
2004    ifs.close()
2005    indices = allIndices
2006    sdSup = Chem.SDMolSupplier(fileN)
2007    molNames = [
2008      "48", "78", "128", "163", "164", "170", "180", "186", "192", "203", "210", "211", "213",
2009      "220", "229", "256"
2010    ]
2011
2012    sdSup._SetStreamIndices(indices)
2013    self.assertTrue(len(sdSup) == 16)
2014    mol = sdSup[5]
2015    self.assertTrue(mol.GetProp("_Name") == "170")
2016
2017    i = 0
2018    for mol in sdSup:
2019      self.assertTrue(mol)
2020      self.assertTrue(mol.GetProp("_Name") == molNames[i])
2021      i += 1
2022
2023    ns = [mol.GetProp("_Name") for mol in sdSup]
2024    self.assertTrue(ns == molNames)
2025
2026    # this can also be used to skip molecules in the file:
2027    indices = [allIndices[0], allIndices[2], allIndices[5]]
2028    sdSup._SetStreamIndices(indices)
2029    self.assertTrue(len(sdSup) == 3)
2030    mol = sdSup[2]
2031    self.assertTrue(mol.GetProp("_Name") == "170")
2032
2033    # or to reorder them:
2034    indices = [allIndices[0], allIndices[5], allIndices[2]]
2035    sdSup._SetStreamIndices(indices)
2036    self.assertTrue(len(sdSup) == 3)
2037    mol = sdSup[1]
2038    self.assertTrue(mol.GetProp("_Name") == "170")
2039
2040  def test42LifeTheUniverseAndEverything(self):
2041    self.assertTrue(True)
2042
2043  def test43TplFileParsing(self):
2044    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
2045                         'cmpd2.tpl')
2046    m1 = Chem.MolFromTPLFile(fileN)
2047    self.assertTrue(m1 is not None)
2048    self.assertTrue(m1.GetNumAtoms() == 12)
2049    self.assertTrue(m1.GetNumConformers() == 2)
2050
2051    m1 = Chem.MolFromTPLFile(fileN, skipFirstConf=True)
2052    self.assertTrue(m1 is not None)
2053    self.assertTrue(m1.GetNumAtoms() == 12)
2054    self.assertTrue(m1.GetNumConformers() == 1)
2055
2056    with open(fileN, 'r') as blockFile:
2057      block = blockFile.read()
2058    m1 = Chem.MolFromTPLBlock(block)
2059    self.assertTrue(m1 is not None)
2060    self.assertTrue(m1.GetNumAtoms() == 12)
2061    self.assertTrue(m1.GetNumConformers() == 2)
2062
2063  def test44TplFileWriting(self):
2064    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
2065                         'cmpd2.tpl')
2066    m1 = Chem.MolFromTPLFile(fileN)
2067    self.assertTrue(m1 is not None)
2068    self.assertTrue(m1.GetNumAtoms() == 12)
2069    self.assertTrue(m1.GetNumConformers() == 2)
2070
2071    block = Chem.MolToTPLBlock(m1)
2072    m1 = Chem.MolFromTPLBlock(block)
2073    self.assertTrue(m1 is not None)
2074    self.assertTrue(m1.GetNumAtoms() == 12)
2075    self.assertTrue(m1.GetNumConformers() == 2)
2076
2077  def test45RingInfo(self):
2078    """ test the RingInfo class
2079
2080    """
2081    smi = 'CNC'
2082    m = Chem.MolFromSmiles(smi)
2083    ri = m.GetRingInfo()
2084    self.assertTrue(ri)
2085    self.assertTrue(ri.NumRings() == 0)
2086    self.assertFalse(ri.IsAtomInRingOfSize(0, 3))
2087    self.assertFalse(ri.IsAtomInRingOfSize(1, 3))
2088    self.assertFalse(ri.IsAtomInRingOfSize(2, 3))
2089    self.assertFalse(ri.IsBondInRingOfSize(1, 3))
2090    self.assertFalse(ri.IsBondInRingOfSize(2, 3))
2091    if hasattr(Chem, 'FindRingFamilies'):
2092      self.assertEquals(ri.AtomRingFamilies(), ())
2093    if hasattr(Chem, 'FindRingFamilies'):
2094      self.assertEquals(ri.BondRingFamilies(), ())
2095
2096    smi = 'C1CC2C1C2'
2097    m = Chem.MolFromSmiles(smi)
2098    ri = m.GetRingInfo()
2099    self.assertTrue(ri)
2100    self.assertEquals(ri.NumRings(), 2)
2101    self.assertFalse(ri.IsAtomInRingOfSize(0, 3))
2102    self.assertTrue(ri.IsAtomInRingOfSize(0, 4))
2103    self.assertFalse(ri.IsBondInRingOfSize(0, 3))
2104    self.assertTrue(ri.IsBondInRingOfSize(0, 4))
2105    self.assertTrue(ri.IsAtomInRingOfSize(2, 4))
2106    self.assertTrue(ri.IsAtomInRingOfSize(2, 3))
2107    self.assertTrue(ri.IsBondInRingOfSize(2, 3))
2108    self.assertTrue(ri.IsBondInRingOfSize(2, 4))
2109
2110    if hasattr(Chem, 'FindRingFamilies'):
2111      ri = m.GetRingInfo()
2112      self.assertFalse(ri.AreRingFamiliesInitialized())
2113      Chem.FindRingFamilies(m)
2114      ri = m.GetRingInfo()
2115      self.assertTrue(ri.AreRingFamiliesInitialized())
2116      self.assertEquals(ri.NumRingFamilies(), 2)
2117      self.assertEquals(sorted(ri.AtomRingFamilies()), [(0, 1, 2, 3), (2, 3, 4)])
2118      self.assertEquals(sorted(ri.BondRingFamilies()), [(0, 1, 2, 4), (2, 3, 5)])
2119
2120  def test46ReplaceCore(self):
2121    """ test the ReplaceCore functionality
2122
2123    """
2124
2125    core = Chem.MolFromSmiles('C=O')
2126
2127    smi = 'CCC=O'
2128    m = Chem.MolFromSmiles(smi)
2129    r = Chem.ReplaceCore(m, core)
2130    self.assertTrue(r)
2131    self.assertEqual(Chem.MolToSmiles(r, True), '[1*]CC')
2132
2133    smi = 'C1CC(=O)CC1'
2134    m = Chem.MolFromSmiles(smi)
2135    r = Chem.ReplaceCore(m, core)
2136    self.assertTrue(r)
2137    self.assertEqual(Chem.MolToSmiles(r, True), '[1*]CCCC[2*]')
2138
2139    smi = 'C1CC(=N)CC1'
2140    m = Chem.MolFromSmiles(smi)
2141    r = Chem.ReplaceCore(m, core)
2142    self.assertFalse(r)
2143
2144    # smiles, smarts, replaceDummies, labelByIndex, useChirality
2145    expected = {
2146      ('C1O[C@@]1(OC)NC', 'C1O[C@]1(*)*', False, False, False): '[1*]OC.[2*]NC',
2147      ('C1O[C@@]1(OC)NC', 'C1O[C@]1(*)*', False, False, True): '[1*]NC.[2*]OC',
2148      ('C1O[C@@]1(OC)NC', 'C1O[C@]1(*)*', False, True, False): '[3*]OC.[4*]NC',
2149      ('C1O[C@@]1(OC)NC', 'C1O[C@]1(*)*', False, True, True): '[3*]NC.[4*]OC',
2150      ('C1O[C@@]1(OC)NC', 'C1O[C@]1(*)*', True, False, False): '[1*]C.[2*]C',
2151      ('C1O[C@@]1(OC)NC', 'C1O[C@]1(*)*', True, False, True): '[1*]C.[2*]C',
2152      ('C1O[C@@]1(OC)NC', 'C1O[C@]1(*)*', True, True, False): '[3*]C.[4*]C',
2153      ('C1O[C@@]1(OC)NC', 'C1O[C@]1(*)*', True, True, True): '[3*]C.[4*]C',
2154      ('C1O[C@]1(OC)NC', 'C1O[C@]1(*)*', False, False, False): '[1*]OC.[2*]NC',
2155      ('C1O[C@]1(OC)NC', 'C1O[C@]1(*)*', False, False, True): '[1*]OC.[2*]NC',
2156      ('C1O[C@]1(OC)NC', 'C1O[C@]1(*)*', False, True, False): '[3*]OC.[4*]NC',
2157      ('C1O[C@]1(OC)NC', 'C1O[C@]1(*)*', False, True, True): '[3*]OC.[4*]NC',
2158      ('C1O[C@]1(OC)NC', 'C1O[C@]1(*)*', True, False, False): '[1*]C.[2*]C',
2159      ('C1O[C@]1(OC)NC', 'C1O[C@]1(*)*', True, False, True): '[1*]C.[2*]C',
2160      ('C1O[C@]1(OC)NC', 'C1O[C@]1(*)*', True, True, False): '[3*]C.[4*]C',
2161      ('C1O[C@]1(OC)NC', 'C1O[C@]1(*)*', True, True, True): '[3*]C.[4*]C',
2162    }
2163
2164    for (smiles, smarts, replaceDummies, labelByIndex,
2165         useChirality), expected_smiles in expected.items():
2166      mol = Chem.MolFromSmiles(smiles)
2167      core = Chem.MolFromSmarts(smarts)
2168      nm = Chem.ReplaceCore(mol, core, replaceDummies=replaceDummies, labelByIndex=labelByIndex,
2169                            useChirality=useChirality)
2170
2171      if Chem.MolToSmiles(nm, True) != expected_smiles:
2172        print(
2173          "ReplaceCore(%r, %r, replaceDummies=%r, labelByIndex=%r, useChirality=%r" %
2174          (smiles, smarts, replaceDummies, labelByIndex, useChirality), file=sys.stderr)
2175        print("expected: %s\ngot: %s" % (expected_smiles, Chem.MolToSmiles(nm, True)),
2176              file=sys.stderr)
2177        self.assertEqual(expected_smiles, Chem.MolToSmiles(nm, True))
2178
2179      matchVect = mol.GetSubstructMatch(core, useChirality=useChirality)
2180      nm = Chem.ReplaceCore(mol, core, matchVect, replaceDummies=replaceDummies,
2181                            labelByIndex=labelByIndex)
2182      if Chem.MolToSmiles(nm, True) != expected_smiles:
2183        print(
2184          "ReplaceCore(%r, %r, %r, replaceDummies=%r, labelByIndex=%rr" %
2185          (smiles, smarts, matchVect, replaceDummies, labelByIndex), file=sys.stderr)
2186        print("expected: %s\ngot: %s" % (expected_smiles, Chem.MolToSmiles(nm, True)),
2187              file=sys.stderr)
2188        self.assertEqual(expected_smiles, Chem.MolToSmiles(nm, True))
2189
2190    mol = Chem.MolFromSmiles("C")
2191    smarts = Chem.MolFromSmarts("C")
2192    try:
2193      Chem.ReplaceCore(mol, smarts, (3, ))
2194      self.asssertFalse(True)
2195    except:
2196      pass
2197
2198    mol = Chem.MolFromSmiles("C")
2199    smarts = Chem.MolFromSmarts("C")
2200    try:
2201      Chem.ReplaceCore(mol, smarts, (0, 0))
2202      self.asssertFalse(True)
2203    except:
2204      pass
2205
2206  def test47RWMols(self):
2207    """ test the RWMol class
2208
2209    """
2210    mol = Chem.MolFromSmiles('C1CCC1')
2211    self.assertTrue(type(mol) == Chem.Mol)
2212
2213    for rwmol in [Chem.EditableMol(mol), Chem.RWMol(mol)]:
2214      self.assertTrue(type(rwmol) in [Chem.EditableMol, Chem.RWMol])
2215      newAt = Chem.Atom(8)
2216      rwmol.ReplaceAtom(0, newAt)
2217      self.assertTrue(Chem.MolToSmiles(rwmol.GetMol()) == 'C1COC1')
2218
2219      rwmol.RemoveBond(0, 1)
2220      self.assertTrue(Chem.MolToSmiles(rwmol.GetMol()) == 'CCCO')
2221      a = Chem.Atom(7)
2222      idx = rwmol.AddAtom(a)
2223      self.assertEqual(rwmol.GetMol().GetNumAtoms(), 5)
2224      self.assertEqual(idx, 4)
2225
2226      idx = rwmol.AddBond(0, 4, order=Chem.BondType.SINGLE)
2227      self.assertEqual(idx, 4)
2228
2229      self.assertTrue(Chem.MolToSmiles(rwmol.GetMol()) == 'CCCON')
2230      rwmol.AddBond(4, 1, order=Chem.BondType.SINGLE)
2231      self.assertTrue(Chem.MolToSmiles(rwmol.GetMol()) == 'C1CNOC1')
2232
2233      rwmol.RemoveAtom(3)
2234      self.assertTrue(Chem.MolToSmiles(rwmol.GetMol()) == 'CCNO')
2235
2236      # practice shooting ourselves in the foot:
2237      m = Chem.MolFromSmiles('c1ccccc1')
2238      em = Chem.EditableMol(m)
2239      em.RemoveAtom(0)
2240      m2 = em.GetMol()
2241      self.assertRaises(ValueError, lambda: Chem.SanitizeMol(m2))
2242      m = Chem.MolFromSmiles('c1ccccc1')
2243      em = Chem.EditableMol(m)
2244      em.RemoveBond(0, 1)
2245      m2 = em.GetMol()
2246      self.assertRaises(ValueError, lambda: Chem.SanitizeMol(m2))
2247
2248      # boundary cases:
2249
2250      # removing non-existent bonds:
2251      m = Chem.MolFromSmiles('c1ccccc1')
2252      em = Chem.EditableMol(m)
2253      em.RemoveBond(0, 2)
2254      m2 = em.GetMol()
2255      Chem.SanitizeMol(m2)
2256      self.assertTrue(Chem.MolToSmiles(m2) == 'c1ccccc1')
2257
2258      # removing non-existent atoms:
2259      m = Chem.MolFromSmiles('c1ccccc1')
2260      em = Chem.EditableMol(m)
2261      self.assertRaises(RuntimeError, lambda: em.RemoveAtom(12))
2262
2263      # confirm that an RWMol can be constructed without arguments
2264      m = Chem.RWMol()
2265
2266    # test replaceAtom/Bond preserving properties
2267    mol = Chem.MolFromSmiles('C1CCC1')
2268    mol2 = Chem.MolFromSmiles('C1CCC1')
2269    mol.GetAtomWithIdx(0).SetProp("foo", "bar")
2270    mol.GetBondWithIdx(0).SetProp("foo", "bar")
2271    newBond = mol2.GetBondWithIdx(0)
2272    self.assertTrue(type(mol) == Chem.Mol)
2273
2274    for rwmol in [Chem.EditableMol(mol), Chem.RWMol(mol)]:
2275      newAt = Chem.Atom(8)
2276      rwmol.ReplaceAtom(0, newAt)
2277      self.assertTrue(Chem.MolToSmiles(rwmol.GetMol()) == 'C1COC1')
2278      self.assertFalse(rwmol.GetMol().GetAtomWithIdx(0).HasProp("foo"))
2279
2280    for rwmol in [Chem.EditableMol(mol), Chem.RWMol(mol)]:
2281      newAt = Chem.Atom(8)
2282      rwmol.ReplaceAtom(0, newAt, preserveProps=True)
2283      self.assertTrue(Chem.MolToSmiles(rwmol.GetMol()) == 'C1COC1')
2284      self.assertTrue(rwmol.GetMol().GetAtomWithIdx(0).HasProp("foo"))
2285      self.assertEqual(rwmol.GetMol().GetAtomWithIdx(0).GetProp("foo"), "bar")
2286
2287    for rwmol in [Chem.EditableMol(mol), Chem.RWMol(mol)]:
2288      rwmol.ReplaceBond(0, newBond)
2289      self.assertTrue(Chem.MolToSmiles(rwmol.GetMol()) == 'C1CCC1')
2290      self.assertFalse(rwmol.GetMol().GetBondWithIdx(0).HasProp("foo"))
2291
2292    for rwmol in [Chem.EditableMol(mol), Chem.RWMol(mol)]:
2293      rwmol.ReplaceBond(0, newBond, preserveProps=True)
2294      self.assertTrue(Chem.MolToSmiles(rwmol.GetMol()) == 'C1CCC1')
2295      self.assertTrue(rwmol.GetMol().GetBondWithIdx(0).HasProp("foo"))
2296      self.assertEqual(rwmol.GetMol().GetBondWithIdx(0).GetProp("foo"), "bar")
2297
2298  def test47SmartsPieces(self):
2299    """ test the GetAtomSmarts and GetBondSmarts functions
2300
2301    """
2302    m = Chem.MolFromSmarts("[C,N]C")
2303    self.assertTrue(m.GetAtomWithIdx(0).GetSmarts() == '[C,N]')
2304    self.assertTrue(m.GetAtomWithIdx(1).GetSmarts() == 'C')
2305    self.assertEqual(m.GetBondBetweenAtoms(0, 1).GetSmarts(), '')
2306
2307    m = Chem.MolFromSmarts("[$(C=O)]-O")
2308    self.assertTrue(m.GetAtomWithIdx(0).GetSmarts() == '[$(C=O)]')
2309    self.assertTrue(m.GetAtomWithIdx(1).GetSmarts() == 'O')
2310    self.assertTrue(m.GetBondBetweenAtoms(0, 1).GetSmarts() == '-')
2311
2312    m = Chem.MolFromSmiles("CO")
2313    self.assertTrue(m.GetAtomWithIdx(0).GetSmarts() == 'C')
2314    self.assertTrue(m.GetAtomWithIdx(1).GetSmarts() == 'O')
2315    self.assertTrue(m.GetBondBetweenAtoms(0, 1).GetSmarts() == '')
2316    self.assertTrue(m.GetBondBetweenAtoms(0, 1).GetSmarts(allBondsExplicit=True) == '-')
2317
2318    m = Chem.MolFromSmiles("C=O")
2319    self.assertTrue(m.GetAtomWithIdx(0).GetSmarts() == 'C')
2320    self.assertTrue(m.GetAtomWithIdx(1).GetSmarts() == 'O')
2321    self.assertTrue(m.GetBondBetweenAtoms(0, 1).GetSmarts() == '=')
2322
2323    m = Chem.MolFromSmiles('C[C@H](F)[15NH3+]')
2324    self.assertEqual(m.GetAtomWithIdx(0).GetSmarts(), 'C')
2325    self.assertEqual(m.GetAtomWithIdx(0).GetSmarts(allHsExplicit=True), '[CH3]')
2326    self.assertEqual(m.GetAtomWithIdx(3).GetSmarts(), '[15NH3+]')
2327    self.assertEqual(m.GetAtomWithIdx(3).GetSmarts(allHsExplicit=True), '[15NH3+]')
2328
2329  def test48Issue1928819(self):
2330    """ test a crash involving looping directly over mol suppliers
2331    """
2332    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
2333                         'NCI_aids_few.sdf')
2334    ms = [x for x in Chem.SDMolSupplier(fileN)]
2335    self.assertEqual(len(ms), 16)
2336    count = 0
2337    for m in Chem.SDMolSupplier(fileN):
2338      count += 1
2339    self.assertEqual(count, 16)
2340
2341    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
2342                         'fewSmi.csv')
2343    count = 0
2344    for m in Chem.SmilesMolSupplier(fileN, titleLine=False, smilesColumn=1, delimiter=','):
2345      count += 1
2346    self.assertEqual(count, 10)
2347
2348    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
2349                         'acd_few.tdt')
2350    count = 0
2351    for m in Chem.TDTMolSupplier(fileN):
2352      count += 1
2353    self.assertEqual(count, 10)
2354
2355  def test49Issue1932365(self):
2356    """ test aromatic Se and Te from smiles/smarts
2357    """
2358    m = Chem.MolFromSmiles('c1ccc[se]1')
2359    self.assertTrue(m)
2360    self.assertTrue(m.GetAtomWithIdx(0).GetIsAromatic())
2361    self.assertTrue(m.GetAtomWithIdx(4).GetIsAromatic())
2362    m = Chem.MolFromSmiles('c1ccc[te]1')
2363    self.assertTrue(m)
2364    self.assertTrue(m.GetAtomWithIdx(0).GetIsAromatic())
2365    self.assertTrue(m.GetAtomWithIdx(4).GetIsAromatic())
2366    m = Chem.MolFromSmiles('C1=C[Se]C=C1')
2367    self.assertTrue(m)
2368    self.assertTrue(m.GetAtomWithIdx(0).GetIsAromatic())
2369    self.assertTrue(m.GetAtomWithIdx(2).GetIsAromatic())
2370    m = Chem.MolFromSmiles('C1=C[Te]C=C1')
2371    self.assertTrue(m)
2372    self.assertTrue(m.GetAtomWithIdx(0).GetIsAromatic())
2373    self.assertTrue(m.GetAtomWithIdx(2).GetIsAromatic())
2374
2375    p = Chem.MolFromSmarts('[se]')
2376    self.assertTrue(Chem.MolFromSmiles('c1ccc[se]1').HasSubstructMatch(p))
2377    self.assertFalse(Chem.MolFromSmiles('C1=CCC[Se]1').HasSubstructMatch(p))
2378
2379    p = Chem.MolFromSmarts('[te]')
2380    self.assertTrue(Chem.MolFromSmiles('c1ccc[te]1').HasSubstructMatch(p))
2381    self.assertFalse(Chem.MolFromSmiles('C1=CCC[Te]1').HasSubstructMatch(p))
2382
2383  def test50Issue1968608(self):
2384    """ test sf.net issue 1968608
2385    """
2386    smarts = Chem.MolFromSmarts("[r5]")
2387    mol = Chem.MolFromSmiles("N12CCC36C1CC(C(C2)=CCOC4CC5=O)C4C3N5c7ccccc76")
2388    count = len(mol.GetSubstructMatches(smarts, uniquify=0))
2389    self.assertTrue(count == 9)
2390
2391  def test51RadicalHandling(self):
2392    """ test handling of atoms with radicals
2393    """
2394    mol = Chem.MolFromSmiles("[C]C")
2395    self.assertTrue(mol)
2396    atom = mol.GetAtomWithIdx(0)
2397    self.assertTrue(atom.GetNumRadicalElectrons() == 3)
2398    self.assertTrue(atom.GetNoImplicit())
2399    atom.SetNoImplicit(False)
2400    atom.SetNumRadicalElectrons(1)
2401    mol.UpdatePropertyCache()
2402    self.assertTrue(atom.GetNumRadicalElectrons() == 1)
2403    self.assertTrue(atom.GetNumImplicitHs() == 2)
2404
2405    mol = Chem.MolFromSmiles("[c]1ccccc1")
2406    self.assertTrue(mol)
2407    atom = mol.GetAtomWithIdx(0)
2408    self.assertTrue(atom.GetNumRadicalElectrons() == 1)
2409    self.assertTrue(atom.GetNoImplicit())
2410
2411    mol = Chem.MolFromSmiles("[n]1ccccc1")
2412    self.assertTrue(mol)
2413    atom = mol.GetAtomWithIdx(0)
2414    self.assertTrue(atom.GetNumRadicalElectrons() == 0)
2415    self.assertTrue(atom.GetNoImplicit())
2416
2417  def test52MolFrags(self):
2418    """ test GetMolFrags functionality
2419    """
2420    mol = Chem.MolFromSmiles("C.CC")
2421    self.assertTrue(mol)
2422    fs = Chem.GetMolFrags(mol)
2423    self.assertTrue(len(fs) == 2)
2424    self.assertTrue(len(fs[0]) == 1)
2425    self.assertTrue(tuple(fs[0]) == (0, ))
2426    self.assertTrue(len(fs[1]) == 2)
2427    self.assertTrue(tuple(fs[1]) == (1, 2))
2428
2429    fs = Chem.GetMolFrags(mol, True)
2430    self.assertTrue(len(fs) == 2)
2431    self.assertTrue(fs[0].GetNumAtoms() == 1)
2432    self.assertTrue(fs[1].GetNumAtoms() == 2)
2433
2434    mol = Chem.MolFromSmiles("CCC")
2435    self.assertTrue(mol)
2436    fs = Chem.GetMolFrags(mol)
2437    self.assertTrue(len(fs) == 1)
2438    self.assertTrue(len(fs[0]) == 3)
2439    self.assertTrue(tuple(fs[0]) == (0, 1, 2))
2440    fs = Chem.GetMolFrags(mol, True)
2441    self.assertTrue(len(fs) == 1)
2442    self.assertTrue(fs[0].GetNumAtoms() == 3)
2443
2444    mol = Chem.MolFromSmiles("CO")
2445    em = Chem.EditableMol(mol)
2446    em.RemoveBond(0, 1)
2447    nm = em.GetMol()
2448    fs = Chem.GetMolFrags(nm, asMols=True)
2449    self.assertEqual([x.GetNumAtoms(onlyExplicit=False) for x in fs], [5, 3])
2450    fs = Chem.GetMolFrags(nm, asMols=True, sanitizeFrags=False)
2451    self.assertEqual([x.GetNumAtoms(onlyExplicit=False) for x in fs], [4, 2])
2452
2453    mol = Chem.MolFromSmiles("CC.CCC")
2454    fs = Chem.GetMolFrags(mol, asMols=True)
2455    self.assertEqual([x.GetNumAtoms() for x in fs], [2, 3])
2456    frags = []
2457    fragsMolAtomMapping = []
2458    fs = Chem.GetMolFrags(mol, asMols=True, frags=frags, fragsMolAtomMapping=fragsMolAtomMapping)
2459    self.assertEqual(mol.GetNumAtoms(onlyExplicit=True), len(frags))
2460    fragsCheck = []
2461    for i, f in enumerate(fs):
2462      fragsCheck.extend([i] * f.GetNumAtoms(onlyExplicit=True))
2463    self.assertEqual(frags, fragsCheck)
2464    fragsMolAtomMappingCheck = []
2465    i = 0
2466    for f in fs:
2467      n = f.GetNumAtoms(onlyExplicit=True)
2468      fragsMolAtomMappingCheck.append(tuple(range(i, i + n)))
2469      i += n
2470    self.assertEqual(fragsMolAtomMapping, fragsMolAtomMappingCheck)
2471
2472  def test53Matrices(self):
2473    """ test adjacency and distance matrices
2474
2475    """
2476    m = Chem.MolFromSmiles('CC=C')
2477    d = Chem.GetDistanceMatrix(m, 0)
2478    self.assertTrue(feq(d[0, 1], 1.0))
2479    self.assertTrue(feq(d[0, 2], 2.0))
2480    self.assertTrue(feq(d[1, 0], 1.0))
2481    self.assertTrue(feq(d[2, 0], 2.0))
2482    a = Chem.GetAdjacencyMatrix(m, 0)
2483    self.assertTrue(a[0, 1] == 1)
2484    self.assertTrue(a[0, 2] == 0)
2485    self.assertTrue(a[1, 2] == 1)
2486    self.assertTrue(a[1, 0] == 1)
2487    self.assertTrue(a[2, 0] == 0)
2488
2489    m = Chem.MolFromSmiles('C1CC1')
2490    d = Chem.GetDistanceMatrix(m, 0)
2491    self.assertTrue(feq(d[0, 1], 1.0))
2492    self.assertTrue(feq(d[0, 2], 1.0))
2493    a = Chem.GetAdjacencyMatrix(m, 0)
2494    self.assertTrue(a[0, 1] == 1)
2495    self.assertTrue(a[0, 2] == 1)
2496    self.assertTrue(a[1, 2] == 1)
2497
2498    m = Chem.MolFromSmiles('CC.C')
2499    d = Chem.GetDistanceMatrix(m, 0)
2500    self.assertTrue(feq(d[0, 1], 1.0))
2501    self.assertTrue(d[0, 2] > 1000)
2502    self.assertTrue(d[1, 2] > 1000)
2503    a = Chem.GetAdjacencyMatrix(m, 0)
2504    self.assertTrue(a[0, 1] == 1)
2505    self.assertTrue(a[0, 2] == 0)
2506    self.assertTrue(a[1, 2] == 0)
2507
2508  def test54Mol2Parser(self):
2509    """ test the mol2 parser
2510    """
2511    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
2512                         'pyrazole_pyridine.mol2')
2513    m = Chem.MolFromMol2File(fileN)
2514    self.assertTrue(m.GetNumAtoms() == 5)
2515    self.assertTrue(Chem.MolToSmiles(m) == 'c1cn[nH]c1', Chem.MolToSmiles(m))
2516
2517    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
2518                         '3505.mol2')
2519    m = Chem.MolFromMol2File(fileN)
2520    self.assertTrue(m.GetBondBetweenAtoms(3, 12) is not None)
2521    self.assertEqual(m.GetBondBetweenAtoms(3, 12).GetBondType(), Chem.BondType.SINGLE)
2522    self.assertEqual(m.GetAtomWithIdx(12).GetFormalCharge(), 0)
2523
2524    m = Chem.MolFromMol2File(fileN, cleanupSubstructures=False)
2525    self.assertTrue(m.GetBondBetweenAtoms(3, 12) is not None)
2526    self.assertEqual(m.GetBondBetweenAtoms(3, 12).GetBondType(), Chem.BondType.DOUBLE)
2527    self.assertEqual(m.GetAtomWithIdx(12).GetFormalCharge(), 1)
2528
2529  def test55LayeredFingerprint(self):
2530    m1 = Chem.MolFromSmiles('CC(C)C')
2531    fp1 = Chem.LayeredFingerprint(m1)
2532    self.assertEqual(len(fp1), 2048)
2533    atomCounts = [0] * m1.GetNumAtoms()
2534    fp2 = Chem.LayeredFingerprint(m1, atomCounts=atomCounts)
2535    self.assertEqual(fp1, fp2)
2536    self.assertEqual(atomCounts, [4, 7, 4, 4])
2537
2538    fp2 = Chem.LayeredFingerprint(m1, atomCounts=atomCounts)
2539    self.assertEqual(fp1, fp2)
2540    self.assertEqual(atomCounts, [8, 14, 8, 8])
2541
2542    pbv = DataStructs.ExplicitBitVect(2048)
2543    fp3 = Chem.LayeredFingerprint(m1, setOnlyBits=pbv)
2544    self.assertEqual(fp3.GetNumOnBits(), 0)
2545
2546    fp3 = Chem.LayeredFingerprint(m1, setOnlyBits=fp2)
2547    self.assertEqual(fp3, fp2)
2548
2549    m2 = Chem.MolFromSmiles('CC')
2550    fp4 = Chem.LayeredFingerprint(m2)
2551    atomCounts = [0] * m1.GetNumAtoms()
2552    fp3 = Chem.LayeredFingerprint(m1, setOnlyBits=fp4, atomCounts=atomCounts)
2553    self.assertEqual(atomCounts, [1, 3, 1, 1])
2554
2555    m2 = Chem.MolFromSmiles('CCC')
2556    fp4 = Chem.LayeredFingerprint(m2)
2557    atomCounts = [0] * m1.GetNumAtoms()
2558    fp3 = Chem.LayeredFingerprint(m1, setOnlyBits=fp4, atomCounts=atomCounts)
2559    self.assertEqual(atomCounts, [3, 6, 3, 3])
2560
2561  def test56LazySDMolSupplier(self):
2562    if not hasattr(Chem, 'CompressedSDMolSupplier'):
2563      return
2564
2565    self.assertRaises(ValueError, lambda: Chem.CompressedSDMolSupplier('nosuchfile.sdf.gz'))
2566
2567    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
2568                         'NCI_aids_few.sdf.gz')
2569    sdSup = Chem.CompressedSDMolSupplier(fileN)
2570    molNames = [
2571      "48", "78", "128", "163", "164", "170", "180", "186", "192", "203", "210", "211", "213",
2572      "220", "229", "256"
2573    ]
2574
2575    chgs192 = {8: 1, 11: 1, 15: -1, 18: -1, 20: 1, 21: 1, 23: -1, 25: -1}
2576    i = 0
2577    for mol in sdSup:
2578      self.assertTrue(mol)
2579      self.assertTrue(mol.GetProp("_Name") == molNames[i])
2580      i += 1
2581      if (mol.GetProp("_Name") == "192"):
2582        # test parsed charges on one of the molecules
2583        for id in chgs192.keys():
2584          self.assertTrue(mol.GetAtomWithIdx(id).GetFormalCharge() == chgs192[id])
2585    self.assertEqual(i, 16)
2586
2587    sdSup = Chem.CompressedSDMolSupplier(fileN)
2588    ns = [mol.GetProp("_Name") for mol in sdSup]
2589    self.assertTrue(ns == molNames)
2590
2591    sdSup = Chem.CompressedSDMolSupplier(fileN, 0)
2592    for mol in sdSup:
2593      self.assertTrue(not mol.HasProp("numArom"))
2594
2595  def test57AddRecursiveQuery(self):
2596    q1 = Chem.MolFromSmiles('CC')
2597    q2 = Chem.MolFromSmiles('CO')
2598    Chem.AddRecursiveQuery(q1, q2, 1)
2599
2600    m1 = Chem.MolFromSmiles('OCC')
2601    self.assertTrue(m1.HasSubstructMatch(q2))
2602    self.assertTrue(m1.HasSubstructMatch(q1))
2603    self.assertTrue(m1.HasSubstructMatch(q1))
2604    self.assertTrue(m1.GetSubstructMatch(q1) == (2, 1))
2605
2606    q3 = Chem.MolFromSmiles('CS')
2607    Chem.AddRecursiveQuery(q1, q3, 1)
2608
2609    self.assertFalse(m1.HasSubstructMatch(q3))
2610    self.assertFalse(m1.HasSubstructMatch(q1))
2611
2612    m2 = Chem.MolFromSmiles('OC(S)C')
2613    self.assertTrue(m2.HasSubstructMatch(q1))
2614    self.assertTrue(m2.GetSubstructMatch(q1) == (3, 1))
2615
2616    m3 = Chem.MolFromSmiles('SCC')
2617    self.assertTrue(m3.HasSubstructMatch(q3))
2618    self.assertFalse(m3.HasSubstructMatch(q1))
2619
2620    q1 = Chem.MolFromSmiles('CC')
2621    Chem.AddRecursiveQuery(q1, q2, 1)
2622    Chem.AddRecursiveQuery(q1, q3, 1, False)
2623    self.assertTrue(m3.HasSubstructMatch(q1))
2624    self.assertTrue(m3.GetSubstructMatch(q1) == (2, 1))
2625
2626  def test58Issue2983794(self):
2627    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'Wrap', 'test_data',
2628                         'issue2983794.sdf')
2629    m1 = Chem.MolFromMolFile(fileN)
2630    self.assertTrue(m1)
2631    em = Chem.EditableMol(m1)
2632    em.RemoveAtom(0)
2633    m2 = em.GetMol()
2634    Chem.Kekulize(m2)
2635
2636  def test59Issue3007178(self):
2637    m = Chem.MolFromSmiles('CCC')
2638    a = m.GetAtomWithIdx(0)
2639    m = None
2640    self.assertEqual(Chem.MolToSmiles(a.GetOwningMol()), 'CCC')
2641    a = None
2642    m = Chem.MolFromSmiles('CCC')
2643    b = m.GetBondWithIdx(0)
2644    m = None
2645    self.assertEqual(Chem.MolToSmiles(b.GetOwningMol()), 'CCC')
2646
2647  def test60SmilesWriterClose(self):
2648    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
2649                         'fewSmi.csv')
2650    smiSup = Chem.SmilesMolSupplier(fileN, delimiter=",", smilesColumn=1, nameColumn=0, titleLine=0)
2651    ms = [x for x in smiSup]
2652
2653    ofile = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'Wrap', 'test_data',
2654                         'outSmiles.txt')
2655    writer = Chem.SmilesWriter(ofile)
2656    for mol in ms:
2657      writer.write(mol)
2658    writer.close()
2659
2660    newsup = Chem.SmilesMolSupplier(ofile)
2661    newms = [x for x in newsup]
2662    self.assertEqual(len(ms), len(newms))
2663
2664  def test61PathToSubmol(self):
2665    m = Chem.MolFromSmiles('CCCCCC1C(O)CC(O)N1C=CCO')
2666    env = Chem.FindAtomEnvironmentOfRadiusN(m, 2, 11)
2667    self.assertEqual(len(env), 8)
2668    amap = {}
2669    submol = Chem.PathToSubmol(m, env, atomMap=amap)
2670    self.assertEqual(submol.GetNumAtoms(), len(amap.keys()))
2671    self.assertEqual(submol.GetNumAtoms(), 9)
2672    smi = Chem.MolToSmiles(submol, rootedAtAtom=amap[11])
2673    self.assertEqual(smi[0], 'N')
2674    refsmi = Chem.MolToSmiles(Chem.MolFromSmiles('N(C=C)(C(C)C)C(O)C'))
2675    csmi = Chem.MolToSmiles(Chem.MolFromSmiles(smi))
2676    self.assertEqual(refsmi, csmi)
2677
2678  def test62SmilesAndSmartsReplacements(self):
2679    mol = Chem.MolFromSmiles('C{branch}C', replacements={'{branch}': 'C1(CC1)'})
2680    self.assertEqual(mol.GetNumAtoms(), 5)
2681    mol = Chem.MolFromSmarts('C{branch}C', replacements={'{branch}': 'C1(CC1)'})
2682    self.assertEqual(mol.GetNumAtoms(), 5)
2683    mol = Chem.MolFromSmiles('C{branch}C{acid}', replacements={
2684      '{branch}': 'C1(CC1)',
2685      '{acid}': "C(=O)O"
2686    })
2687    self.assertEqual(mol.GetNumAtoms(), 8)
2688
2689  def test63Issue3313539(self):
2690    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
2691                         'rgroups1.mol')
2692    m = Chem.MolFromMolFile(fileN)
2693    self.assertTrue(m is not None)
2694    at = m.GetAtomWithIdx(3)
2695    self.assertTrue(at is not None)
2696    self.assertTrue(at.HasProp('_MolFileRLabel'))
2697    p = at.GetProp('_MolFileRLabel')
2698    self.assertEqual(p, '2')
2699    self.assertEqual(Chem.GetAtomRLabel(at), 2)
2700
2701    at = m.GetAtomWithIdx(4)
2702    self.assertTrue(at is not None)
2703    self.assertTrue(at.HasProp('_MolFileRLabel'))
2704    p = at.GetProp('_MolFileRLabel')
2705    self.assertEqual(p, '1')
2706    self.assertEqual(Chem.GetAtomRLabel(at), 1)
2707
2708  def test64MoleculeCleanup(self):
2709    m = Chem.MolFromSmiles('CN(=O)=O', False)
2710    self.assertTrue(m)
2711    self.assertTrue(m.GetAtomWithIdx(1).GetFormalCharge()==0 and \
2712                      m.GetAtomWithIdx(2).GetFormalCharge()==0 and \
2713                      m.GetAtomWithIdx(3).GetFormalCharge()==0)
2714    self.assertTrue(m.GetBondBetweenAtoms(1,3).GetBondType()==Chem.BondType.DOUBLE and \
2715                      m.GetBondBetweenAtoms(1,2).GetBondType()==Chem.BondType.DOUBLE )
2716    Chem.Cleanup(m)
2717    m.UpdatePropertyCache()
2718    self.assertTrue(m.GetAtomWithIdx(1).GetFormalCharge()==1 and \
2719                      (m.GetAtomWithIdx(2).GetFormalCharge()==-1 or \
2720                         m.GetAtomWithIdx(3).GetFormalCharge()==-1))
2721    self.assertTrue(m.GetBondBetweenAtoms(1,3).GetBondType()==Chem.BondType.SINGLE or \
2722                      m.GetBondBetweenAtoms(1,2).GetBondType()==Chem.BondType.SINGLE )
2723
2724  def test65StreamSupplier(self):
2725    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
2726                         'NCI_aids_few.sdf.gz')
2727    molNames = [
2728      "48", "78", "128", "163", "164", "170", "180", "186", "192", "203", "210", "211", "213",
2729      "220", "229", "256"
2730    ]
2731    inf = gzip.open(fileN)
2732    if 0:
2733      sb = Chem.streambuf(inf)
2734      suppl = Chem.ForwardSDMolSupplier(sb)
2735    else:
2736      suppl = Chem.ForwardSDMolSupplier(inf)
2737
2738    i = 0
2739    while not suppl.atEnd():
2740      mol = next(suppl)
2741      self.assertTrue(mol)
2742      self.assertTrue(mol.GetProp("_Name") == molNames[i])
2743      i += 1
2744    self.assertEqual(i, 16)
2745
2746    # make sure we have object ownership preserved
2747    inf = gzip.open(fileN)
2748    suppl = Chem.ForwardSDMolSupplier(inf)
2749    inf = None
2750    i = 0
2751    while not suppl.atEnd():
2752      mol = next(suppl)
2753      self.assertTrue(mol)
2754      self.assertTrue(mol.GetProp("_Name") == molNames[i])
2755      i += 1
2756    self.assertEqual(i, 16)
2757
2758  @unittest.skipIf(not hasattr(Chem, 'MaeMolSupplier'), "not build with MAEParser support")
2759  def testMaeStreamSupplier(self):
2760    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
2761                         'NCI_aids_few.maegz')
2762    molNames = [
2763      "48", "78", "128", "163", "164", "170", "180", "186", "192", "203", "210", "211", "213",
2764      "220", "229", "256"
2765    ]
2766    inf = gzip.open(fileN)
2767    suppl = Chem.MaeMolSupplier(inf)
2768
2769    i = 0
2770    while not suppl.atEnd():
2771      mol = next(suppl)
2772      self.assertTrue(mol)
2773      self.assertTrue(mol.GetProp("_Name") == molNames[i])
2774      i += 1
2775    self.assertEqual(i, 16)
2776
2777    # make sure we have object ownership preserved
2778    inf = gzip.open(fileN)
2779    suppl = Chem.MaeMolSupplier(inf)
2780    inf = None
2781    i = 0
2782    while not suppl.atEnd():
2783      mol = next(suppl)
2784      self.assertTrue(mol)
2785      self.assertTrue(mol.GetProp("_Name") == molNames[i])
2786      i += 1
2787    self.assertEqual(i, 16)
2788
2789  @unittest.skipIf(not hasattr(Chem, 'MaeMolSupplier'), "not build with MAEParser support")
2790  def testMaeFileSupplier(self):
2791    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
2792                         'NCI_aids_few.mae')
2793    molNames = [
2794      "48", "78", "128", "163", "164", "170", "180", "186", "192", "203", "210", "211", "213",
2795      "220", "229", "256"
2796    ]
2797    suppl = Chem.MaeMolSupplier(fileN)
2798
2799    i = 0
2800    while not suppl.atEnd():
2801      mol = next(suppl)
2802      self.assertTrue(mol)
2803      self.assertTrue(mol.GetProp("_Name") == molNames[i])
2804      i += 1
2805    self.assertEqual(i, 16)
2806
2807  @unittest.skipIf(not hasattr(Chem, 'MaeMolSupplier'), "not build with MAEParser support")
2808  def testMaeFileSupplierException(self):
2809    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
2810                         'bad_ppty.mae')
2811    err_msg_substr = "Bad format for property"
2812
2813    ok = False
2814    suppl = Chem.MaeMolSupplier(fileN)
2815    for i in range(5):
2816      try:
2817        mol = next(suppl)
2818      except RuntimeError as e:
2819        self.assertEqual(i, 1)
2820        self.assertTrue(err_msg_substr in str(e))
2821        ok = True
2822        break
2823      else:
2824        self.assertTrue(mol)
2825        self.assertTrue(mol.HasProp("_Name"))
2826        self.assertTrue(mol.GetNumAtoms() == 1)
2827
2828    self.assertFalse(suppl.atEnd())
2829    self.assertTrue(ok)
2830
2831  def test66StreamSupplierIter(self):
2832    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
2833                         'NCI_aids_few.sdf.gz')
2834    inf = gzip.open(fileN)
2835    if 0:
2836      sb = Chem.streambuf(inf)
2837      suppl = Chem.ForwardSDMolSupplier(sb)
2838    else:
2839      suppl = Chem.ForwardSDMolSupplier(inf)
2840
2841    molNames = [
2842      "48", "78", "128", "163", "164", "170", "180", "186", "192", "203", "210", "211", "213",
2843      "220", "229", "256"
2844    ]
2845    i = 0
2846    for mol in suppl:
2847      self.assertTrue(mol)
2848      self.assertTrue(mol.GetProp("_Name") == molNames[i])
2849      i += 1
2850    self.assertEqual(i, 16)
2851
2852  def test67StreamSupplierStringIO(self):
2853    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
2854                         'NCI_aids_few.sdf.gz')
2855    from io import BytesIO
2856    sio = BytesIO(gzip.open(fileN).read())
2857    suppl = Chem.ForwardSDMolSupplier(sio)
2858    molNames = [
2859      "48", "78", "128", "163", "164", "170", "180", "186", "192", "203", "210", "211", "213",
2860      "220", "229", "256"
2861    ]
2862    i = 0
2863    for mol in suppl:
2864      self.assertTrue(mol)
2865      self.assertTrue(mol.GetProp("_Name") == molNames[i])
2866      i += 1
2867    self.assertEqual(i, 16)
2868
2869  def test68ForwardSupplierUsingFilename(self):
2870    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
2871                         'NCI_aids_few.sdf')
2872    suppl = Chem.ForwardSDMolSupplier(fileN)
2873    molNames = [
2874      "48", "78", "128", "163", "164", "170", "180", "186", "192", "203", "210", "211", "213",
2875      "220", "229", "256"
2876    ]
2877    i = 0
2878    for mol in suppl:
2879      self.assertTrue(mol)
2880      self.assertTrue(mol.GetProp("_Name") == molNames[i])
2881      i += 1
2882    self.assertEqual(i, 16)
2883
2884    self.assertRaises(IOError, lambda: Chem.ForwardSDMolSupplier('nosuchfile.sdf'))
2885
2886  def test69StreamSupplierStreambuf(self):
2887    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
2888                         'NCI_aids_few.sdf.gz')
2889    sb = rdBase.streambuf(gzip.open(fileN))
2890    suppl = Chem.ForwardSDMolSupplier(sb)
2891
2892    molNames = [
2893      "48", "78", "128", "163", "164", "170", "180", "186", "192", "203", "210", "211", "213",
2894      "220", "229", "256"
2895    ]
2896    i = 0
2897    for mol in suppl:
2898      self.assertTrue(mol)
2899      self.assertTrue(mol.GetProp("_Name") == molNames[i])
2900      i += 1
2901    self.assertEqual(i, 16)
2902
2903  def test70StreamSDWriter(self):
2904    from io import BytesIO, StringIO
2905
2906    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
2907                         'NCI_aids_few.sdf.gz')
2908    inf = gzip.open(fileN)
2909    suppl = Chem.ForwardSDMolSupplier(inf)
2910    osio = StringIO()
2911    w = Chem.SDWriter(osio)
2912    molNames = [
2913      "48", "78", "128", "163", "164", "170", "180", "186", "192", "203", "210", "211", "213",
2914      "220", "229", "256"
2915    ]
2916    i = 0
2917    for mol in suppl:
2918      self.assertTrue(mol)
2919      self.assertTrue(mol.GetProp("_Name") == molNames[i])
2920      w.write(mol)
2921      i += 1
2922    self.assertEqual(i, 16)
2923    w.flush()
2924    w = None
2925    txt = osio.getvalue().encode()
2926    isio = BytesIO(txt)
2927    suppl = Chem.ForwardSDMolSupplier(isio)
2928    i = 0
2929    for mol in suppl:
2930      self.assertTrue(mol)
2931      self.assertTrue(mol.GetProp("_Name") == molNames[i])
2932      i += 1
2933    self.assertEqual(i, 16)
2934
2935  def test71StreamSmilesWriter(self):
2936    from io import StringIO
2937    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
2938                         'esters.sdf')
2939    suppl = Chem.ForwardSDMolSupplier(fileN)
2940    osio = StringIO()
2941    w = Chem.SmilesWriter(osio)
2942    ms = [x for x in suppl]
2943    w.SetProps(ms[0].GetPropNames())
2944    i = 0
2945    for mol in ms:
2946      self.assertTrue(mol)
2947      w.write(mol)
2948      i += 1
2949    self.assertEqual(i, 6)
2950    w.flush()
2951    w = None
2952    txt = osio.getvalue()
2953    self.assertEqual(txt.count('ID'), 1)
2954    self.assertEqual(txt.count('\n'), 7)
2955
2956  def test72StreamTDTWriter(self):
2957    from io import StringIO
2958    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
2959                         'esters.sdf')
2960    suppl = Chem.ForwardSDMolSupplier(fileN)
2961    osio = StringIO()
2962    w = Chem.TDTWriter(osio)
2963    ms = [x for x in suppl]
2964    w.SetProps(ms[0].GetPropNames())
2965    i = 0
2966    for mol in ms:
2967      self.assertTrue(mol)
2968      w.write(mol)
2969      i += 1
2970    self.assertEqual(i, 6)
2971    w.flush()
2972    w = None
2973    txt = osio.getvalue()
2974    self.assertEqual(txt.count('ID'), 6)
2975    self.assertEqual(txt.count('NAME'), 6)
2976
2977  def test73SanitizationOptions(self):
2978    m = Chem.MolFromSmiles('c1ccccc1', sanitize=False)
2979    res = Chem.SanitizeMol(m, catchErrors=True)
2980    self.assertEqual(res, 0)
2981
2982    m = Chem.MolFromSmiles('c1cccc1', sanitize=False)
2983    res = Chem.SanitizeMol(m, catchErrors=True)
2984    self.assertEqual(res, Chem.SanitizeFlags.SANITIZE_KEKULIZE)
2985
2986    m = Chem.MolFromSmiles('CC(C)(C)(C)C', sanitize=False)
2987    res = Chem.SanitizeMol(m, catchErrors=True)
2988    self.assertEqual(res, Chem.SanitizeFlags.SANITIZE_PROPERTIES)
2989
2990    m = Chem.MolFromSmiles('c1cccc1', sanitize=False)
2991    res = Chem.SanitizeMol(
2992      m, sanitizeOps=Chem.SanitizeFlags.SANITIZE_ALL ^ Chem.SanitizeFlags.SANITIZE_KEKULIZE,
2993      catchErrors=True)
2994    self.assertEqual(res, Chem.SanitizeFlags.SANITIZE_NONE)
2995
2996  def test74Issue3510149(self):
2997    mol = Chem.MolFromSmiles("CCC1CNCC1CC")
2998    atoms = mol.GetAtoms()
2999    mol = None
3000    for atom in atoms:
3001      idx = atom.GetIdx()
3002      p = atom.GetOwningMol().GetNumAtoms()
3003
3004    mol = Chem.MolFromSmiles("CCC1CNCC1CC")
3005    bonds = mol.GetBonds()
3006    mol = None
3007    for bond in bonds:
3008      idx = bond.GetIdx()
3009      p = atom.GetOwningMol().GetNumAtoms()
3010
3011    mol = Chem.MolFromSmiles("CCC1CNCC1CC")
3012    bond = mol.GetBondBetweenAtoms(0, 1)
3013    mol = None
3014    idx = bond.GetBeginAtomIdx()
3015    p = bond.GetOwningMol().GetNumAtoms()
3016
3017    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
3018                         'NCI_aids_few.sdf')
3019    sdSup = Chem.SDMolSupplier(fileN)
3020    mol = next(sdSup)
3021    nats = mol.GetNumAtoms()
3022    conf = mol.GetConformer()
3023    mol = None
3024    self.assertEqual(nats, conf.GetNumAtoms())
3025    conf.GetOwningMol().GetProp("_Name")
3026
3027  def test75AllBondsExplicit(self):
3028    m = Chem.MolFromSmiles("CCC")
3029    smi = Chem.MolToSmiles(m)
3030    self.assertEqual(smi, "CCC")
3031    smi = Chem.MolToSmiles(m, allBondsExplicit=True)
3032    self.assertEqual(smi, "C-C-C")
3033
3034    m = Chem.MolFromSmiles("c1ccccc1")
3035    smi = Chem.MolToSmiles(m)
3036    self.assertEqual(smi, "c1ccccc1")
3037    smi = Chem.MolToSmiles(m, allBondsExplicit=True)
3038    self.assertEqual(smi, "c1:c:c:c:c:c:1")
3039
3040  def test76VeryLargeMolecule(self):
3041    # this is sf.net issue 3524984
3042    smi = '[C@H](F)(Cl)' + 'c1cc[nH]c1' * 500 + '[C@H](F)(Cl)'
3043    m = Chem.MolFromSmiles(smi)
3044    self.assertTrue(m)
3045    self.assertEqual(m.GetNumAtoms(), 2506)
3046    scs = Chem.FindMolChiralCenters(m)
3047    self.assertEqual(len(scs), 2)
3048
3049  def test77MolFragmentToSmiles(self):
3050    smi = "OC1CC1CC"
3051    m = Chem.MolFromSmiles(smi)
3052    fsmi = Chem.MolFragmentToSmiles(m, [1, 2, 3])
3053    self.assertEqual(fsmi, "C1CC1")
3054    fsmi = Chem.MolFragmentToSmiles(m, [1, 2, 3], bondsToUse=[1, 2, 5])
3055    self.assertEqual(fsmi, "C1CC1")
3056    fsmi = Chem.MolFragmentToSmiles(m, [1, 2, 3], bondsToUse=[1, 2])
3057    self.assertEqual(fsmi, "CCC")
3058    fsmi = Chem.MolFragmentToSmiles(m, [1, 2, 3], atomSymbols=["", "[A]", "[C]", "[B]", "", ""])
3059    self.assertEqual(fsmi, "[A]1[B][C]1")
3060    fsmi = Chem.MolFragmentToSmiles(m, [1, 2, 3], bondSymbols=["", "%", "%", "", "", "%"])
3061    self.assertEqual(fsmi, "C1%C%C%1")
3062
3063    smi = "c1ccccc1C"
3064    m = Chem.MolFromSmiles(smi)
3065    fsmi = Chem.MolFragmentToSmiles(m, range(6))
3066    self.assertEqual(fsmi, "c1ccccc1")
3067    Chem.Kekulize(m)
3068    fsmi = Chem.MolFragmentToSmiles(m, range(6), kekuleSmiles=True)
3069    self.assertEqual(fsmi, "C1=CC=CC=C1")
3070    fsmi = Chem.MolFragmentToSmiles(m, range(6), atomSymbols=["[C]"] * 7, kekuleSmiles=True)
3071    self.assertEqual(fsmi, "[C]1=[C][C]=[C][C]=[C]1")
3072
3073    self.assertRaises(ValueError, lambda: Chem.MolFragmentToSmiles(m, []))
3074
3075  def test78AtomAndBondProps(self):
3076    m = Chem.MolFromSmiles('c1ccccc1')
3077    at = m.GetAtomWithIdx(0)
3078    self.assertFalse(at.HasProp('foo'))
3079    at.SetProp('foo', 'bar')
3080    self.assertTrue(at.HasProp('foo'))
3081    self.assertEqual(at.GetProp('foo'), 'bar')
3082    bond = m.GetBondWithIdx(0)
3083    self.assertFalse(bond.HasProp('foo'))
3084    bond.SetProp('foo', 'bar')
3085    self.assertTrue(bond.HasProp('foo'))
3086    self.assertEqual(bond.GetProp('foo'), 'bar')
3087
3088  def test79AddRecursiveStructureQueries(self):
3089    qs = {'carbonyl': Chem.MolFromSmiles('CO'), 'amine': Chem.MolFromSmiles('CN')}
3090    q = Chem.MolFromSmiles('CCC')
3091    q.GetAtomWithIdx(0).SetProp('query', 'carbonyl,amine')
3092    Chem.MolAddRecursiveQueries(q, qs, 'query')
3093    m = Chem.MolFromSmiles('CCCO')
3094    self.assertTrue(m.HasSubstructMatch(q))
3095    m = Chem.MolFromSmiles('CCCN')
3096    self.assertTrue(m.HasSubstructMatch(q))
3097    m = Chem.MolFromSmiles('CCCC')
3098    self.assertFalse(m.HasSubstructMatch(q))
3099
3100  def test80ParseMolQueryDefFile(self):
3101    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'ChemTransforms', 'testData',
3102                         'query_file1.txt')
3103    d = Chem.ParseMolQueryDefFile(fileN, standardize=False)
3104    self.assertTrue('CarboxylicAcid' in d)
3105    m = Chem.MolFromSmiles('CC(=O)O')
3106    self.assertTrue(m.HasSubstructMatch(d['CarboxylicAcid']))
3107    self.assertFalse(m.HasSubstructMatch(d['CarboxylicAcid.Aromatic']))
3108
3109    d = Chem.ParseMolQueryDefFile(fileN)
3110    self.assertTrue('carboxylicacid' in d)
3111    self.assertFalse('CarboxylicAcid' in d)
3112
3113  def test81Issue275(self):
3114    smi = Chem.MolToSmiles(Chem.MurckoDecompose(
3115      Chem.MolFromSmiles('CCCCC[C@H]1CC[C@H](C(=O)O)CC1')))
3116    self.assertEqual(smi, 'C1CCCCC1')
3117
3118  def test82Issue288(self):
3119    m = Chem.MolFromSmiles('CC*')
3120    m.GetAtomWithIdx(2).SetProp('molAtomMapNumber', '30')
3121
3122    smi = Chem.MolToSmiles(m)
3123    self.assertEqual(smi, 'CC[*:30]')
3124    # try newer api
3125    m = Chem.MolFromSmiles('CC*')
3126    m.GetAtomWithIdx(2).SetAtomMapNum(30)
3127    smi = Chem.MolToSmiles(m)
3128    self.assertEqual(smi, 'CC[*:30]')
3129
3130  def test83GitHubIssue19(self):
3131    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
3132                         'empty2.sdf')
3133    with self.assertRaises(OSError):
3134      sdSup = Chem.SDMolSupplier(fileN)
3135
3136    sdSup = Chem.SDMolSupplier()
3137    sdSup.SetData('')
3138    self.assertTrue(sdSup.atEnd())
3139    self.assertRaises(IndexError, lambda: sdSup[0])
3140
3141    sdSup.SetData('')
3142    self.assertRaises(IndexError, lambda: sdSup[0])
3143    self.assertEqual(len(sdSup), 0)
3144
3145  def test84PDBBasics(self):
3146    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
3147                         '1CRN.pdb')
3148    m = Chem.MolFromPDBFile(fileN, proximityBonding=False)
3149    self.assertEqual(m.GetNumAtoms(), 327)
3150    self.assertEqual(m.GetNumBonds(), 3)
3151    m = Chem.MolFromPDBFile(fileN)
3152    self.assertTrue(m is not None)
3153    self.assertEqual(m.GetNumAtoms(), 327)
3154    self.assertEqual(m.GetNumBonds(), 337)
3155    self.assertTrue(m.GetAtomWithIdx(0).GetPDBResidueInfo())
3156    self.assertEqual(m.GetAtomWithIdx(0).GetPDBResidueInfo().GetName(), " N  ")
3157    self.assertEqual(m.GetAtomWithIdx(0).GetPDBResidueInfo().GetResidueName(), "THR")
3158    self.assertAlmostEqual(m.GetAtomWithIdx(0).GetPDBResidueInfo().GetTempFactor(), 13.79, 2)
3159    m = Chem.MolFromPDBBlock(Chem.MolToPDBBlock(m))
3160    self.assertEqual(m.GetNumAtoms(), 327)
3161    self.assertEqual(m.GetNumBonds(), 337)
3162    self.assertTrue(m.GetAtomWithIdx(0).GetPDBResidueInfo())
3163    self.assertEqual(m.GetAtomWithIdx(0).GetPDBResidueInfo().GetName(), " N  ")
3164    self.assertEqual(m.GetAtomWithIdx(0).GetPDBResidueInfo().GetResidueName(), "THR")
3165    self.assertAlmostEqual(m.GetAtomWithIdx(0).GetPDBResidueInfo().GetTempFactor(), 13.79, 2)
3166    # test multivalent Hs
3167    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
3168                         '2c92_hypervalentH.pdb')
3169    mol = Chem.MolFromPDBFile(fileN, sanitize=False, removeHs=False)
3170    atom = mol.GetAtomWithIdx(84)
3171    self.assertEqual(atom.GetAtomicNum(), 1)  # is it H
3172    self.assertEqual(atom.GetDegree(), 1)  # H should have 1 bond
3173    for n in atom.GetNeighbors():  # Check if neighbor is from the same residue
3174      self.assertEqual(atom.GetPDBResidueInfo().GetResidueName(),
3175                       n.GetPDBResidueInfo().GetResidueName())
3176    # test unbinding metals (ZN)
3177    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
3178                         '1ps3_zn.pdb')
3179    mol = Chem.MolFromPDBFile(fileN, sanitize=False, removeHs=False)
3180    atom = mol.GetAtomWithIdx(40)
3181    self.assertEqual(atom.GetAtomicNum(), 30)  # is it Zn
3182    self.assertEqual(atom.GetDegree(), 4)  # Zn should have 4 zero-order bonds
3183    self.assertEqual(atom.GetExplicitValence(), 0)
3184    bonds_order = [bond.GetBondType() for bond in atom.GetBonds()]
3185    self.assertEqual(bonds_order, [Chem.BondType.ZERO] * atom.GetDegree())
3186
3187    # test metal bonds without proximity bonding
3188    mol = Chem.MolFromPDBFile(fileN, sanitize=False, removeHs=False, proximityBonding=False)
3189    atom = mol.GetAtomWithIdx(40)
3190    self.assertEqual(atom.GetAtomicNum(), 30)  # is it Zn
3191    self.assertEqual(atom.GetDegree(), 4)  # Zn should have 4 zero-order bonds
3192    self.assertEqual(atom.GetExplicitValence(), 0)
3193    bonds_order = [bond.GetBondType() for bond in atom.GetBonds()]
3194    self.assertEqual(bonds_order, [Chem.BondType.ZERO] * atom.GetDegree())
3195    # test unbinding HOHs
3196    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
3197                         '2vnf_bindedHOH.pdb')
3198    mol = Chem.MolFromPDBFile(fileN, sanitize=False, removeHs=False)
3199    atom = mol.GetAtomWithIdx(10)
3200    self.assertEqual(atom.GetPDBResidueInfo().GetResidueName(), 'HOH')
3201    self.assertEqual(atom.GetDegree(), 0)  # HOH should have no bonds
3202    # test metal bonding in ligand
3203    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
3204                         '2dej_APW.pdb')
3205    mol = Chem.MolFromPDBFile(fileN, sanitize=False, removeHs=False)
3206    atom = mol.GetAtomWithIdx(6)
3207    self.assertEqual(atom.GetAtomicNum(), 12)
3208    self.assertEqual(atom.GetDegree(), 2)
3209    atom = mol.GetAtomWithIdx(35)
3210    self.assertEqual(atom.GetPDBResidueInfo().GetResidueName(), 'HOH')
3211    self.assertEqual(atom.GetDegree(), 0)
3212
3213  def test85AtomCopying(self):
3214    """Can a copied atom be added to a molecule?"""
3215    import copy
3216    m = Chem.MolFromSmiles('C1CC1')
3217    a = m.GetAtomWithIdx(0)
3218    a_copy1 = copy.copy(a)
3219    a_copy2 = Chem.Atom(a)
3220    m = None
3221    a = None
3222
3223    def assert_is_valid_atom(a):
3224      new_m = Chem.RWMol()
3225      new_m.AddAtom(a)
3226      # This will not match if the owning mol is unset for a_copy,
3227      # or if there has been a clean up.
3228      self.assertEqual(new_m.GetAtomWithIdx(0).GetIdx(), 0)
3229
3230    assert_is_valid_atom(a_copy1)
3231    assert_is_valid_atom(a_copy2)
3232
3233  def test85MolCopying(self):
3234    m = Chem.MolFromSmiles('C1CC1[C@H](F)Cl')
3235    m.SetProp('foo', 'bar')
3236    m2 = Chem.Mol(m)
3237    self.assertEqual(Chem.MolToSmiles(m, True), Chem.MolToSmiles(m2, True))
3238    self.assertTrue(m2.HasProp('foo'))
3239    self.assertEqual(m2.GetProp('foo'), 'bar')
3240    ri = m2.GetRingInfo()
3241    self.assertTrue(ri)
3242    self.assertEqual(ri.NumRings(), 1)
3243
3244  def test85MolCopying2(self):
3245    import copy
3246    m1 = Chem.MolFromSmiles('CC')
3247    m1.SetProp('Foo', 'bar')
3248    m1.foo = [1]
3249    m2 = copy.copy(m1)
3250    m3 = copy.copy(m2)
3251    m4 = copy.deepcopy(m1)
3252    m5 = copy.deepcopy(m2)
3253    m6 = copy.deepcopy(m4)
3254
3255    self.assertEqual(m1.GetProp('Foo'), 'bar')
3256    self.assertEqual(m2.GetProp('Foo'), 'bar')
3257    self.assertEqual(m3.GetProp('Foo'), 'bar')
3258    self.assertEqual(m4.GetProp('Foo'), 'bar')
3259    self.assertEqual(m5.GetProp('Foo'), 'bar')
3260    self.assertEqual(m6.GetProp('Foo'), 'bar')
3261
3262    m2.foo.append(4)
3263    self.assertEqual(m1.foo, [1, 4])
3264    self.assertEqual(m2.foo, [1, 4])
3265    self.assertEqual(m3.foo, [1, 4])
3266    self.assertEqual(m4.foo, [1])
3267    self.assertEqual(m5.foo, [1])
3268    self.assertEqual(m6.foo, [1])
3269
3270    m7 = Chem.RWMol(m1)
3271    self.assertFalse(hasattr(m7, 'foo'))
3272    m7.foo = [1]
3273    m8 = copy.copy(m7)
3274    m9 = copy.deepcopy(m7)
3275    m8.foo.append(4)
3276    self.assertEqual(m7.GetProp('Foo'), 'bar')
3277    self.assertEqual(m8.GetProp('Foo'), 'bar')
3278    self.assertEqual(m9.GetProp('Foo'), 'bar')
3279    self.assertEqual(m8.foo, [1, 4])
3280    self.assertEqual(m9.foo, [1])
3281
3282  def test86MolRenumbering(self):
3283    import random
3284    m = Chem.MolFromSmiles('C[C@H]1CC[C@H](C/C=C/[C@H](F)Cl)CC1')
3285    cSmi = Chem.MolToSmiles(m, True)
3286    for i in range(m.GetNumAtoms()):
3287      ans = list(range(m.GetNumAtoms()))
3288      random.shuffle(ans)
3289      m2 = Chem.RenumberAtoms(m, ans)
3290      nSmi = Chem.MolToSmiles(m2, True)
3291      self.assertEqual(cSmi, nSmi)
3292
3293  def test87FragmentOnBonds(self):
3294    m = Chem.MolFromSmiles('CC1CC(O)C1CCC1CC1')
3295    bis = m.GetSubstructMatches(Chem.MolFromSmarts('[!R][R]'))
3296    bs = []
3297    labels = []
3298    for bi in bis:
3299      b = m.GetBondBetweenAtoms(bi[0], bi[1])
3300      if b.GetBeginAtomIdx() == bi[0]:
3301        labels.append((10, 1))
3302      else:
3303        labels.append((1, 10))
3304      bs.append(b.GetIdx())
3305    nm = Chem.FragmentOnBonds(m, bs)
3306    frags = Chem.GetMolFrags(nm)
3307    self.assertEqual(len(frags), 5)
3308    self.assertEqual(frags,
3309                     ((0, 12), (1, 2, 3, 5, 11, 14, 16), (4, 13), (6, 7, 15, 18), (8, 9, 10, 17)))
3310    smi = Chem.MolToSmiles(nm, True)
3311    self.assertEqual(smi, '*C1CC([4*])C1[6*].[1*]C.[3*]O.[5*]CC[8*].[7*]C1CC1')
3312
3313    nm = Chem.FragmentOnBonds(m, bs, dummyLabels=labels)
3314    frags = Chem.GetMolFrags(nm)
3315    self.assertEqual(len(frags), 5)
3316    self.assertEqual(frags,
3317                     ((0, 12), (1, 2, 3, 5, 11, 14, 16), (4, 13), (6, 7, 15, 18), (8, 9, 10, 17)))
3318    smi = Chem.MolToSmiles(nm, True)
3319    self.assertEqual(smi, '[1*]C.[1*]CC[1*].[1*]O.[10*]C1CC([10*])C1[10*].[10*]C1CC1')
3320
3321    m = Chem.MolFromSmiles('CCC(=O)CC(=O)C')
3322    bis = m.GetSubstructMatches(Chem.MolFromSmarts('C=O'))
3323    bs = []
3324    for bi in bis:
3325      b = m.GetBondBetweenAtoms(bi[0], bi[1])
3326      bs.append(b.GetIdx())
3327    bts = [Chem.BondType.DOUBLE] * len(bs)
3328    nm = Chem.FragmentOnBonds(m, bs, bondTypes=bts)
3329    frags = Chem.GetMolFrags(nm)
3330    self.assertEqual(len(frags), 3)
3331    smi = Chem.MolToSmiles(nm, True)
3332    self.assertEqual(smi, '[2*]=O.[3*]=C(CC)CC(=[6*])C.[5*]=O')
3333
3334    # github issue 430:
3335    m = Chem.MolFromSmiles('OCCCCN')
3336    self.assertRaises(ValueError, lambda: Chem.FragmentOnBonds(m, ()))
3337
3338  def test88QueryAtoms(self):
3339    from rdkit.Chem import rdqueries
3340    m = Chem.MolFromSmiles('c1nc(C)n(CC)c1')
3341
3342    qa = rdqueries.ExplicitDegreeEqualsQueryAtom(3)
3343    l = tuple([x.GetIdx() for x in m.GetAtomsMatchingQuery(qa)])
3344    self.assertEqual(l, (2, 4))
3345
3346    qa.ExpandQuery(rdqueries.AtomNumEqualsQueryAtom(6, negate=True))
3347    l = tuple([x.GetIdx() for x in m.GetAtomsMatchingQuery(qa)])
3348    self.assertEqual(l, (4, ))
3349
3350    qa = rdqueries.ExplicitDegreeEqualsQueryAtom(3)
3351    qa.ExpandQuery(rdqueries.AtomNumEqualsQueryAtom(6, negate=True),
3352                   how=Chem.CompositeQueryType.COMPOSITE_OR)
3353    l = tuple([x.GetIdx() for x in m.GetAtomsMatchingQuery(qa)])
3354    self.assertEqual(l, (1, 2, 4))
3355
3356    qa = rdqueries.ExplicitDegreeEqualsQueryAtom(3)
3357    qa.ExpandQuery(rdqueries.AtomNumEqualsQueryAtom(6, negate=True),
3358                   how=Chem.CompositeQueryType.COMPOSITE_XOR)
3359    l = tuple([x.GetIdx() for x in m.GetAtomsMatchingQuery(qa)])
3360    self.assertEqual(l, (1, 2))
3361
3362    qa = rdqueries.ExplicitDegreeGreaterQueryAtom(2)
3363    l = tuple([x.GetIdx() for x in m.GetAtomsMatchingQuery(qa)])
3364    self.assertEqual(l, (2, 4))
3365
3366    qa = rdqueries.ExplicitDegreeLessQueryAtom(2)
3367    l = tuple([x.GetIdx() for x in m.GetAtomsMatchingQuery(qa)])
3368    self.assertEqual(l, (3, 6))
3369
3370    m = Chem.MolFromSmiles('N[CH][CH]')
3371    qa = rdqueries.NumRadicalElectronsGreaterQueryAtom(0)
3372    l = tuple([x.GetIdx() for x in m.GetAtomsMatchingQuery(qa)])
3373    self.assertEqual(l, (1, 2))
3374    qa = rdqueries.NumRadicalElectronsGreaterQueryAtom(1)
3375    l = tuple([x.GetIdx() for x in m.GetAtomsMatchingQuery(qa)])
3376    self.assertEqual(l, (2, ))
3377
3378    m = Chem.MolFromSmiles('F[C@H](Cl)C')
3379    qa = rdqueries.HasChiralTagQueryAtom()
3380    l = tuple([x.GetIdx() for x in m.GetAtomsMatchingQuery(qa)])
3381    self.assertEqual(l, (1, ))
3382    qa = rdqueries.MissingChiralTagQueryAtom()
3383    l = tuple([x.GetIdx() for x in m.GetAtomsMatchingQuery(qa)])
3384    self.assertEqual(l, ())
3385
3386    m = Chem.MolFromSmiles('F[CH](Cl)C')
3387    qa = rdqueries.HasChiralTagQueryAtom()
3388    l = tuple([x.GetIdx() for x in m.GetAtomsMatchingQuery(qa)])
3389    self.assertEqual(l, ())
3390    qa = rdqueries.MissingChiralTagQueryAtom()
3391    l = tuple([x.GetIdx() for x in m.GetAtomsMatchingQuery(qa)])
3392    self.assertEqual(l, (1, ))
3393
3394    m = Chem.MolFromSmiles('CNCON')
3395    qa = rdqueries.NumHeteroatomNeighborsEqualsQueryAtom(2)
3396    l = tuple([x.GetIdx() for x in m.GetAtomsMatchingQuery(qa)])
3397    self.assertEqual(l, (2, ))
3398    qa = rdqueries.NumHeteroatomNeighborsGreaterQueryAtom(0)
3399    l = tuple([x.GetIdx() for x in m.GetAtomsMatchingQuery(qa)])
3400    self.assertEqual(l, (0, 2, 3, 4))
3401
3402  def test89UnicodeInput(self):
3403    m = Chem.MolFromSmiles(u'c1ccccc1')
3404    self.assertTrue(m is not None)
3405    self.assertEqual(m.GetNumAtoms(), 6)
3406    m = Chem.MolFromSmarts(u'c1ccccc1')
3407    self.assertTrue(m is not None)
3408    self.assertEqual(m.GetNumAtoms(), 6)
3409
3410  def test90FragmentOnSomeBonds(self):
3411    m = Chem.MolFromSmiles('OCCCCN')
3412    pieces = Chem.FragmentOnSomeBonds(m, (0, 2, 4), 2)
3413    self.assertEqual(len(pieces), 3)
3414
3415    frags = Chem.GetMolFrags(pieces[0])
3416    self.assertEqual(len(frags), 3)
3417    self.assertEqual(len(frags[0]), 2)
3418    self.assertEqual(len(frags[1]), 4)
3419    self.assertEqual(len(frags[2]), 4)
3420
3421    frags = Chem.GetMolFrags(pieces[1])
3422    self.assertEqual(len(frags), 3)
3423    self.assertEqual(len(frags[0]), 2)
3424    self.assertEqual(len(frags[1]), 6)
3425    self.assertEqual(len(frags[2]), 2)
3426
3427    frags = Chem.GetMolFrags(pieces[2])
3428    self.assertEqual(len(frags), 3)
3429    self.assertEqual(len(frags[0]), 4)
3430    self.assertEqual(len(frags[1]), 4)
3431    self.assertEqual(len(frags[2]), 2)
3432
3433    pieces, cpa = Chem.FragmentOnSomeBonds(m, (0, 2, 4), 2, returnCutsPerAtom=True)
3434    self.assertEqual(len(pieces), 3)
3435    self.assertEqual(len(cpa), 3)
3436    self.assertEqual(len(cpa[0]), m.GetNumAtoms())
3437
3438    # github issue 430:
3439    m = Chem.MolFromSmiles('OCCCCN')
3440    self.assertRaises(ValueError, lambda: Chem.FragmentOnSomeBonds(m, ()))
3441
3442    pieces = Chem.FragmentOnSomeBonds(m, (0, 2, 4), 0)
3443    self.assertEqual(len(pieces), 0)
3444
3445  def test91RankAtoms(self):
3446    m = Chem.MolFromSmiles('ONCS.ONCS')
3447    ranks = Chem.CanonicalRankAtoms(m, breakTies=False)
3448    self.assertEqual(list(ranks[0:4]), list(ranks[4:]))
3449
3450    m = Chem.MolFromSmiles("c1ccccc1")
3451    ranks = Chem.CanonicalRankAtoms(m, breakTies=False)
3452    for x in ranks:
3453      self.assertEqual(x, 0)
3454
3455    m = Chem.MolFromSmiles("C1NCN1")
3456    ranks = Chem.CanonicalRankAtoms(m, breakTies=False)
3457    self.assertEqual(ranks[0], ranks[2])
3458    self.assertEqual(ranks[1], ranks[3])
3459
3460  def test92RankAtomsInFragment(self):
3461    m = Chem.MolFromSmiles('ONCS.ONCS')
3462    ranks = Chem.CanonicalRankAtomsInFragment(m, [0, 1, 2, 3], [0, 1, 2])
3463
3464    ranks2 = Chem.CanonicalRankAtomsInFragment(m, [4, 5, 6, 7], [3, 4, 5])
3465    self.assertEqual(list(ranks[0:4]), list(ranks2[4:]))
3466    self.assertEqual(list(ranks[4:]), [-1] * 4)
3467    self.assertEqual(list(ranks2[0:4]), [-1] * 4)
3468
3469    # doc tests
3470    mol = Chem.MolFromSmiles('C1NCN1.C1NCN1')
3471    self.assertEqual(
3472      list(Chem.CanonicalRankAtomsInFragment(mol, atomsToUse=range(0, 4), breakTies=False)),
3473      [4, 6, 4, 6, -1, -1, -1, -1])
3474    self.assertNotEqual(
3475      list(Chem.CanonicalRankAtomsInFragment(mol, atomsToUse=range(0, 4), breakTies=True)),
3476      [4, 6, 4, 6, -1, -1, -1, -1])
3477    self.assertEqual(
3478      list(Chem.CanonicalRankAtomsInFragment(mol, atomsToUse=range(4, 8), breakTies=False)),
3479      [-1, -1, -1, -1, 4, 6, 4, 6])
3480    self.assertNotEqual(
3481      list(Chem.CanonicalRankAtomsInFragment(mol, atomsToUse=range(4, 8), breakTies=True)),
3482      [-1, -1, -1, -1, 4, 6, 4, 6])
3483
3484  def test93RWMolsAsROMol(self):
3485    """ test the RWMol class as a proper ROMol
3486
3487    """
3488    mol = Chem.MolFromSmiles('C1CCC1')
3489    self.assertTrue(type(mol) == Chem.Mol)
3490    rwmol = Chem.RWMol(mol)
3491    self.assertEqual(Chem.MolToSmiles(rwmol, True), Chem.MolToSmiles(rwmol.GetMol()))
3492    newAt = Chem.Atom(8)
3493    rwmol.ReplaceAtom(0, newAt)
3494    self.assertEqual(Chem.MolToSmiles(rwmol, True), Chem.MolToSmiles(rwmol.GetMol()))
3495
3496  def test94CopyWithConfs(self):
3497    """ test copying Mols with some conformers
3498
3499    """
3500    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
3501                         'cmpd2.tpl')
3502    m1 = Chem.MolFromTPLFile(fileN)
3503    self.assertTrue(m1 is not None)
3504    self.assertEqual(m1.GetNumAtoms(), 12)
3505    self.assertEqual(m1.GetNumConformers(), 2)
3506    self.assertEqual(m1.GetConformer(0).GetNumAtoms(), 12)
3507    self.assertEqual(m1.GetConformer(1).GetNumAtoms(), 12)
3508
3509    m2 = Chem.Mol(m1)
3510    self.assertEqual(m2.GetNumAtoms(), 12)
3511    self.assertEqual(m2.GetNumConformers(), 2)
3512    self.assertEqual(m2.GetConformer(0).GetNumAtoms(), 12)
3513    self.assertEqual(m2.GetConformer(1).GetNumAtoms(), 12)
3514
3515    m2 = Chem.Mol(m1, False, 0)
3516    self.assertEqual(m2.GetNumAtoms(), 12)
3517    self.assertEqual(m2.GetNumConformers(), 1)
3518    self.assertEqual(m2.GetConformer(0).GetNumAtoms(), 12)
3519
3520    m2 = Chem.Mol(m1, False, 1)
3521    self.assertEqual(m2.GetNumAtoms(), 12)
3522    self.assertEqual(m2.GetNumConformers(), 1)
3523    self.assertEqual(m2.GetConformer(1).GetNumAtoms(), 12)
3524
3525    m2 = Chem.Mol(m1, True)
3526    self.assertTrue(m2.GetNumAtoms() == 12)
3527    self.assertTrue(m2.GetNumConformers() == 0)
3528
3529    m2 = Chem.RWMol(m1)
3530    self.assertEqual(m2.GetNumAtoms(), 12)
3531    self.assertEqual(m2.GetNumConformers(), 2)
3532    self.assertEqual(m2.GetConformer(0).GetNumAtoms(), 12)
3533    self.assertEqual(m2.GetConformer(1).GetNumAtoms(), 12)
3534
3535    m2 = Chem.RWMol(m1, False, 0)
3536    self.assertEqual(m2.GetNumAtoms(), 12)
3537    self.assertEqual(m2.GetNumConformers(), 1)
3538    self.assertEqual(m2.GetConformer(0).GetNumAtoms(), 12)
3539
3540    m2 = Chem.RWMol(m1, False, 1)
3541    self.assertEqual(m2.GetNumAtoms(), 12)
3542    self.assertEqual(m2.GetNumConformers(), 1)
3543    self.assertEqual(m2.GetConformer(1).GetNumAtoms(), 12)
3544
3545    m2 = Chem.RWMol(m1, True)
3546    self.assertTrue(m2.GetNumAtoms() == 12)
3547    self.assertTrue(m2.GetNumConformers() == 0)
3548
3549  def testAtomPropQueries(self):
3550    """ test the property queries
3551    """
3552    from rdkit.Chem import rdqueries
3553
3554    m = Chem.MolFromSmiles("C" * 14)
3555    atoms = m.GetAtoms()
3556    atoms[0].SetProp("hah", "hah")
3557    atoms[1].SetIntProp("bar", 1)
3558    atoms[2].SetIntProp("bar", 2)
3559    atoms[3].SetBoolProp("baz", True)
3560    atoms[4].SetBoolProp("baz", False)
3561    atoms[5].SetProp("boo", "hoo")
3562    atoms[6].SetProp("boo", "-urns")
3563    atoms[7].SetDoubleProp("boot", 1.0)
3564    atoms[8].SetDoubleProp("boot", 4.0)
3565    atoms[9].SetDoubleProp("number", 4.0)
3566    atoms[10].SetIntProp("number", 4)
3567
3568    tests = ((rdqueries.HasIntPropWithValueQueryAtom, "bar", {
3569      1: [1],
3570      2: [2]
3571    }), (rdqueries.HasBoolPropWithValueQueryAtom, "baz", {
3572      True: [3],
3573      False: [4]
3574    }), (rdqueries.HasStringPropWithValueQueryAtom, "boo", {
3575      "hoo": [5],
3576      "-urns": [6]
3577    }), (rdqueries.HasDoublePropWithValueQueryAtom, "boot", {
3578      1.0: [7],
3579      4.0: [8]
3580    }))
3581
3582    for query, name, lookups in tests:
3583      for t, v in lookups.items():
3584        q = query(name, t)
3585        self.assertEqual(v, [x.GetIdx() for x in m.GetAtomsMatchingQuery(q)])
3586        q = query(name, t, negate=True)
3587        self.assertEqual(sorted(set(range(14)) - set(v)),
3588                         [x.GetIdx() for x in m.GetAtomsMatchingQuery(q)])
3589
3590    # check tolerances
3591    self.assertEqual([
3592      x.GetIdx() for x in m.GetAtomsMatchingQuery(
3593        rdqueries.HasDoublePropWithValueQueryAtom("boot", 1.0, tolerance=3.))
3594    ], [7, 8])
3595
3596    # numbers are numbers?, i.e. int!=double
3597    self.assertEqual([
3598      x.GetIdx()
3599      for x in m.GetAtomsMatchingQuery(rdqueries.HasIntPropWithValueQueryAtom("number", 4))
3600    ], [10])
3601
3602  def testBondPropQueries(self):
3603    """ test the property queries
3604    """
3605    from rdkit.Chem import rdqueries
3606
3607    m = Chem.MolFromSmiles("C" * 14)
3608    bonds = m.GetBonds()
3609    bonds[0].SetProp("hah", "hah")
3610    bonds[1].SetIntProp("bar", 1)
3611    bonds[2].SetIntProp("bar", 2)
3612    bonds[3].SetBoolProp("baz", True)
3613    bonds[4].SetBoolProp("baz", False)
3614    bonds[5].SetProp("boo", "hoo")
3615    bonds[6].SetProp("boo", "-urns")
3616    bonds[7].SetDoubleProp("boot", 1.0)
3617    bonds[8].SetDoubleProp("boot", 4.0)
3618    bonds[9].SetDoubleProp("number", 4.0)
3619    bonds[10].SetIntProp("number", 4)
3620
3621    tests = ((rdqueries.HasIntPropWithValueQueryBond, "bar", {
3622      1: [1],
3623      2: [2]
3624    }), (rdqueries.HasBoolPropWithValueQueryBond, "baz", {
3625      True: [3],
3626      False: [4]
3627    }), (rdqueries.HasStringPropWithValueQueryBond, "boo", {
3628      "hoo": [5],
3629      "-urns": [6]
3630    }), (rdqueries.HasDoublePropWithValueQueryBond, "boot", {
3631      1.0: [7],
3632      4.0: [8]
3633    }))
3634
3635    for query, name, lookups in tests:
3636      for t, v in lookups.items():
3637        q = query(name, t)
3638        self.assertEqual(v, [x.GetIdx() for x in m.GetBonds() if q.Match(x)])
3639        q = query(name, t, negate=True)
3640        self.assertEqual(sorted(set(range(13)) - set(v)),
3641                         [x.GetIdx() for x in m.GetBonds() if q.Match(x)])
3642
3643    # check tolerances
3644    q = rdqueries.HasDoublePropWithValueQueryBond("boot", 1.0, tolerance=3.)
3645    self.assertEqual([x.GetIdx() for x in m.GetBonds() if q.Match(x)], [7, 8])
3646
3647    # numbers are numbers?, i.e. int!=double
3648    q = rdqueries.HasIntPropWithValueQueryBond("number", 4)
3649    self.assertEqual([x.GetIdx() for x in m.GetBonds() if q.Match(x)], [10])
3650
3651  def testGetShortestPath(self):
3652    """ test the GetShortestPath() wrapper
3653    """
3654    smi = "CC(OC1C(CCCC3)C3C(CCCC2)C2C1OC(C)=O)=O"
3655    m = Chem.MolFromSmiles(smi)
3656    path = Chem.GetShortestPath(m, 1, 20)
3657    self.assertEqual(path, (1, 2, 3, 16, 17, 18, 20))
3658
3659  def testGithub497(self):
3660    with tempfile.TemporaryFile() as tmp, gzip.open(tmp) as outf:
3661      with self.assertRaises(ValueError):
3662        w = Chem.SDWriter(outf)
3663
3664  def testGithub498(self):
3665    if (sys.version_info < (3, 0)):
3666      mode = 'w+'
3667    else:
3668      mode = 'wt+'
3669    m = Chem.MolFromSmiles('C')
3670    with tempfile.NamedTemporaryFile() as tmp, gzip.open(tmp, mode) as outf:
3671      w = Chem.SDWriter(outf)
3672      w.write(m)
3673      w.close()
3674
3675  def testReplaceBond(self):
3676    origmol = Chem.RWMol(Chem.MolFromSmiles("CC"))
3677    bonds = list(origmol.GetBonds())
3678    self.assertEqual(len(bonds), 1)
3679    singlebond = bonds[0]
3680    self.assertEqual(singlebond.GetBondType(), Chem.BondType.SINGLE)
3681
3682    # this is the only way we create a bond, is take it from another molecule
3683    doublebonded = Chem.MolFromSmiles("C=C")
3684    doublebond = list(doublebonded.GetBonds())[0]
3685
3686    # make sure replacing the bond changes the smiles
3687    self.assertEqual(Chem.MolToSmiles(origmol), "CC")
3688    origmol.ReplaceBond(singlebond.GetIdx(), doublebond)
3689    Chem.SanitizeMol(origmol)
3690
3691    self.assertEqual(Chem.MolToSmiles(origmol), "C=C")
3692
3693  def testAdjustQueryProperties(self):
3694    m = Chem.MolFromSmarts('C1CCC1*')
3695    am = Chem.AdjustQueryProperties(m)
3696    self.assertTrue(Chem.MolFromSmiles('C1CCC1C').HasSubstructMatch(m))
3697    self.assertTrue(Chem.MolFromSmiles('C1CCC1C').HasSubstructMatch(am))
3698    self.assertTrue(Chem.MolFromSmiles('C1CC(C)C1C').HasSubstructMatch(m))
3699    self.assertFalse(Chem.MolFromSmiles('C1CC(C)C1C').HasSubstructMatch(am))
3700
3701    m = Chem.MolFromSmiles('C1CCC1*')
3702    am = Chem.AdjustQueryProperties(m)
3703    self.assertFalse(Chem.MolFromSmiles('C1CCC1C').HasSubstructMatch(m))
3704    self.assertTrue(Chem.MolFromSmiles('C1CCC1C').HasSubstructMatch(am))
3705    qps = Chem.AdjustQueryParameters()
3706    qps.makeDummiesQueries = False
3707    am = Chem.AdjustQueryProperties(m, qps)
3708    self.assertFalse(Chem.MolFromSmiles('C1CCC1C').HasSubstructMatch(am))
3709
3710    m = Chem.MolFromSmiles('C1=CC=CC=C1', sanitize=False)
3711    am = Chem.AdjustQueryProperties(m)
3712    self.assertTrue(Chem.MolFromSmiles('c1ccccc1').HasSubstructMatch(am))
3713    qp = Chem.AdjustQueryParameters()
3714    qp.aromatizeIfPossible = False
3715    am = Chem.AdjustQueryProperties(m, qp)
3716    self.assertFalse(Chem.MolFromSmiles('c1ccccc1').HasSubstructMatch(am))
3717
3718    m = Chem.MolFromSmiles('C1CCC1OC')
3719    qps = Chem.AdjustQueryParameters()
3720    qps.makeAtomsGeneric = True
3721    am = Chem.AdjustQueryProperties(m, qps)
3722    self.assertEqual(Chem.MolToSmarts(am), '*1-*-*-*-1-*-*')
3723    qps.makeAtomsGenericFlags = Chem.ADJUST_IGNORERINGS
3724    am = Chem.AdjustQueryProperties(m, qps)
3725    self.assertEqual(Chem.MolToSmarts(am), '[#6&D2]1-[#6&D2]-[#6&D2]-[#6&D3]-1-*-*')
3726
3727    qps = Chem.AdjustQueryParameters()
3728    qps.makeBondsGeneric = True
3729    am = Chem.AdjustQueryProperties(m, qps)
3730    self.assertEqual(Chem.MolToSmarts(am), '[#6&D2]1~[#6&D2]~[#6&D2]~[#6&D3]~1~[#8]~[#6]')
3731    qps.makeBondsGenericFlags = Chem.ADJUST_IGNORERINGS
3732    am = Chem.AdjustQueryProperties(m, qps)
3733    self.assertEqual(Chem.MolToSmarts(am), '[#6&D2]1-[#6&D2]-[#6&D2]-[#6&D3]-1~[#8]~[#6]')
3734
3735  def testMolFragmentSmarts(self):
3736    m = Chem.MolFromSmiles('C1CCC1OC')
3737    self.assertEqual(Chem.MolFragmentToSmarts(m, [0, 1, 2]), '[#6]-[#6]-[#6]')
3738    # if bondsToUse is honored, the ring won't show up
3739    self.assertEqual(Chem.MolFragmentToSmarts(m, [0, 1, 2, 3], bondsToUse=[0, 1, 2, 3]),
3740                     '[#6]-[#6]-[#6]-[#6]')
3741
3742    # Does MolFragmentToSmarts accept output of AdjustQueryProperties?
3743    qps = Chem.AdjustQueryParameters()
3744    qps.makeAtomsGeneric = True
3745    am = Chem.AdjustQueryProperties(m, qps)
3746    self.assertEqual(Chem.MolFragmentToSmarts(am, [0, 1, 2]), '*-*-*')
3747
3748  def testAdjustQueryPropertiesgithubIssue1474(self):
3749    core = Chem.MolFromSmiles('[*:1]C1N([*:2])C([*:3])O1')
3750    core.GetAtomWithIdx(0).SetProp('foo', 'bar')
3751    core.GetAtomWithIdx(1).SetProp('foo', 'bar')
3752
3753    ap = Chem.AdjustQueryProperties(core)
3754    self.assertEqual(ap.GetAtomWithIdx(0).GetPropsAsDict()["foo"], "bar")
3755    self.assertEqual(ap.GetAtomWithIdx(1).GetPropsAsDict()["foo"], "bar")
3756
3757  def testGithubIssue579(self):
3758    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
3759                         'NCI_aids_few.sdf.gz')
3760    inf = gzip.open(fileN)
3761    suppl = Chem.ForwardSDMolSupplier(inf)
3762    m0 = next(suppl)
3763    self.assertIsNot(m0, None)
3764    inf.close()
3765    del suppl
3766
3767  def testSequenceBasics(self):
3768    " very basic round-tripping of the sequence reader/writer support "
3769    helm = 'PEPTIDE1{C.Y.I.Q.N.C.P.L.G}$$$$'
3770    seq = 'CYIQNCPLG'
3771    fasta = '>\nCYIQNCPLG\n'
3772    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'
3773
3774    m = Chem.MolFromSequence(seq)
3775    self.assertTrue(m is not None)
3776    self.assertEqual(Chem.MolToSequence(m), seq)
3777    self.assertEqual(Chem.MolToHELM(m), helm)
3778    self.assertEqual(Chem.MolToFASTA(m), fasta)
3779    self.assertEqual(Chem.MolToSmiles(m, isomericSmiles=True), smi)
3780
3781    m = Chem.MolFromHELM(helm)
3782    self.assertTrue(m is not None)
3783    self.assertEqual(Chem.MolToSequence(m), seq)
3784    self.assertEqual(Chem.MolToHELM(m), helm)
3785    self.assertEqual(Chem.MolToFASTA(m), fasta)
3786    self.assertEqual(Chem.MolToSmiles(m, isomericSmiles=True), smi)
3787
3788    m = Chem.MolFromFASTA(fasta)
3789    self.assertTrue(m is not None)
3790    self.assertEqual(Chem.MolToSequence(m), seq)
3791    self.assertEqual(Chem.MolToHELM(m), helm)
3792    self.assertEqual(Chem.MolToFASTA(m), fasta)
3793    self.assertEqual(Chem.MolToSmiles(m, isomericSmiles=True), smi)
3794
3795    seq = "CGCGAATTACCGCG"
3796    m = Chem.MolFromSequence(seq, flavor=6)  # DNA
3797    self.assertEqual(Chem.MolToSequence(m), 'CGCGAATTACCGCG')
3798    self.assertEqual(
3799      Chem.MolToHELM(m),
3800      '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)}$$$$'
3801    )
3802    seq = "CGCGAAUUACCGCG"
3803    m = Chem.MolFromSequence(seq, flavor=2)  # RNA
3804    self.assertEqual(Chem.MolToSequence(m), 'CGCGAAUUACCGCG')
3805    self.assertEqual(
3806      Chem.MolToHELM(m),
3807      '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)}$$$$'
3808    )
3809    m = Chem.MolFromSequence(seq, flavor=3)  # RNA - 5' cap
3810    self.assertEqual(Chem.MolToSequence(m), 'CGCGAAUUACCGCG')
3811    self.assertEqual(
3812      Chem.MolToHELM(m),
3813      '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)}$$$$'
3814    )
3815
3816  def testResMolSupplier(self):
3817    mol = Chem.MolFromSmiles('CC')
3818    resMolSuppl = Chem.ResonanceMolSupplier(mol)
3819    del resMolSuppl
3820    resMolSuppl = Chem.ResonanceMolSupplier(mol)
3821    self.assertEqual(resMolSuppl.GetNumConjGrps(), 0)
3822    self.assertEqual(len(resMolSuppl), 1)
3823    self.assertEqual(resMolSuppl.GetNumConjGrps(), 0)
3824
3825    mol = Chem.MolFromSmiles('NC(=[NH2+])c1ccc(cc1)C(=O)[O-]')
3826    totalFormalCharge = getTotalFormalCharge(mol)
3827
3828    resMolSuppl = Chem.ResonanceMolSupplier(mol)
3829    self.assertFalse(resMolSuppl.GetIsEnumerated())
3830    self.assertEqual(len(resMolSuppl), 4)
3831    self.assertTrue(resMolSuppl.GetIsEnumerated())
3832
3833    resMolSuppl = Chem.ResonanceMolSupplier(mol)
3834    self.assertFalse(resMolSuppl.GetIsEnumerated())
3835    resMolSuppl.Enumerate()
3836    self.assertTrue(resMolSuppl.GetIsEnumerated())
3837    self.assertTrue((resMolSuppl[0].GetBondBetweenAtoms(0, 1).GetBondType() \
3838      != resMolSuppl[1].GetBondBetweenAtoms(0, 1).GetBondType())
3839      or (resMolSuppl[0].GetBondBetweenAtoms(9, 10).GetBondType() \
3840      != resMolSuppl[1].GetBondBetweenAtoms(9, 10).GetBondType()))
3841
3842    resMolSuppl = Chem.ResonanceMolSupplier(mol, Chem.KEKULE_ALL)
3843    self.assertEqual(len(resMolSuppl), 8)
3844    bondTypeSet = set()
3845    # check that we actually have two alternate Kekule structures
3846    bondTypeSet.add(resMolSuppl[0].GetBondBetweenAtoms(3, 4).GetBondType())
3847    bondTypeSet.add(resMolSuppl[1].GetBondBetweenAtoms(3, 4).GetBondType())
3848    self.assertEqual(len(bondTypeSet), 2)
3849
3850    bondTypeDict = {}
3851    resMolSuppl = Chem.ResonanceMolSupplier(mol,
3852      Chem.ALLOW_INCOMPLETE_OCTETS \
3853      | Chem.UNCONSTRAINED_CATIONS \
3854      | Chem.UNCONSTRAINED_ANIONS)
3855    self.assertEqual(len(resMolSuppl), 32)
3856    for i in range(len(resMolSuppl)):
3857      resMol = resMolSuppl[i]
3858      self.assertEqual(getTotalFormalCharge(resMol), totalFormalCharge)
3859    while (not resMolSuppl.atEnd()):
3860      resMol = next(resMolSuppl)
3861      self.assertEqual(getTotalFormalCharge(resMol), totalFormalCharge)
3862    resMolSuppl.reset()
3863    cmpFormalChargeBondOrder(self, resMolSuppl[0], next(resMolSuppl))
3864
3865    resMolSuppl = Chem.ResonanceMolSupplier(mol,
3866      Chem.ALLOW_INCOMPLETE_OCTETS \
3867      | Chem.UNCONSTRAINED_CATIONS \
3868      | Chem.UNCONSTRAINED_ANIONS, 10)
3869    self.assertEqual(len(resMolSuppl), 10)
3870
3871    crambinPdb = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
3872                              '1CRN.pdb')
3873    mol = Chem.MolFromPDBFile(crambinPdb)
3874    resMolSuppl = Chem.ResonanceMolSupplier(mol)
3875    self.assertEqual(len(resMolSuppl), 1)
3876    resMolSuppl = Chem.ResonanceMolSupplier(mol, Chem.KEKULE_ALL)
3877    self.assertEqual(len(resMolSuppl), 8)
3878
3879  def testSubstructMatchAcetate(self):
3880    mol = Chem.MolFromSmiles('CC(=O)[O-]')
3881    query = Chem.MolFromSmarts('C(=O)[O-]')
3882
3883    resMolSuppl = Chem.ResonanceMolSupplier(mol)
3884    matches = mol.GetSubstructMatches(query)
3885    self.assertEqual(len(matches), 1)
3886    self.assertEqual(matches, ((1, 2, 3), ))
3887    matches = mol.GetSubstructMatches(query, uniquify=True)
3888    self.assertEqual(len(matches), 1)
3889    self.assertEqual(matches, ((1, 2, 3), ))
3890    matches = mol.GetSubstructMatches(query, uniquify=False)
3891    self.assertEqual(len(matches), 1)
3892    self.assertEqual(matches, ((1, 2, 3), ))
3893    matches = resMolSuppl.GetSubstructMatches(query)
3894    self.assertEqual(len(matches), 2)
3895    self.assertEqual(matches, ((1, 2, 3), (1, 3, 2)))
3896    matches = resMolSuppl.GetSubstructMatches(query, uniquify=True)
3897    self.assertEqual(len(matches), 1)
3898    self.assertEqual(matches, ((1, 2, 3), ))
3899    matches = resMolSuppl.GetSubstructMatches(query, uniquify=False)
3900    self.assertEqual(len(matches), 2)
3901    self.assertEqual(matches, ((1, 2, 3), (1, 3, 2)))
3902    query = Chem.MolFromSmarts('C(~O)~O')
3903    matches = mol.GetSubstructMatches(query, uniquify=False)
3904    self.assertEqual(len(matches), 2)
3905    self.assertEqual(matches, ((1, 2, 3), (1, 3, 2)))
3906    matches = mol.GetSubstructMatches(query, uniquify=True)
3907    self.assertEqual(len(matches), 1)
3908    self.assertEqual(matches, ((1, 2, 3), ))
3909    matches = resMolSuppl.GetSubstructMatches(query, uniquify=False)
3910    self.assertEqual(len(matches), 2)
3911    self.assertEqual(matches, ((1, 2, 3), (1, 3, 2)))
3912    matches = resMolSuppl.GetSubstructMatches(query, uniquify=True)
3913    self.assertEqual(len(matches), 1)
3914    self.assertEqual(matches, ((1, 2, 3), ))
3915
3916  def testSubstructMatchDMAP(self):
3917    mol = Chem.MolFromSmiles('C(C)Nc1cc[nH+]cc1')
3918    query = Chem.MolFromSmarts('[#7+]')
3919
3920    resMolSuppl = Chem.ResonanceMolSupplier(mol)
3921    matches = mol.GetSubstructMatches(query, False, False, False)
3922    self.assertEqual(len(matches), 1)
3923    p = matches[0]
3924    self.assertEqual(p[0], 6)
3925    matches = resMolSuppl.GetSubstructMatches(query, False, False, False)
3926    self.assertEqual(len(matches), 2)
3927    v = []
3928    p = matches[0]
3929    v.append(p[0])
3930    p = matches[1]
3931    v.append(p[0])
3932    v.sort()
3933    self.assertEqual(v[0], 2)
3934    self.assertEqual(v[1], 6)
3935
3936  def testCrambin(self):
3937    crambinPdb = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
3938                              '1CRN.pdb')
3939    crambin = Chem.MolFromPDBFile(crambinPdb)
3940    res = []
3941    # protonate NH2
3942    res.append(Chem.MolFromSmarts('[Nh2][Ch;Ch2]'))
3943    # protonate Arg
3944    res.append(Chem.MolFromSmarts('[Nh][C]([Nh2])=[Nh]'))
3945    setResidueFormalCharge(crambin, res, 1)
3946    res = []
3947    # deprotonate COOH
3948    res.append(Chem.MolFromSmarts('C(=O)[Oh]'))
3949    setResidueFormalCharge(crambin, res, -1)
3950    res = []
3951    resMolSupplST = Chem.ResonanceMolSupplier(crambin)
3952    # crambin has 2 Arg (3 resonance structures each); 1 Asp, 1 Glu
3953    # and 1 terminal COO- (2 resonance structures each)
3954    # so possible resonance structures are 3^2 * 2^3 = 72
3955    self.assertEqual(len(resMolSupplST), 72)
3956    self.assertEqual(resMolSupplST.GetNumConjGrps(), 56)
3957    carboxylateQuery = Chem.MolFromSmarts('C(=O)[O-]')
3958    guanidiniumQuery = Chem.MolFromSmarts('NC(=[NH2+])N')
3959    matches = crambin.GetSubstructMatches(carboxylateQuery)
3960    self.assertEqual(len(matches), 3)
3961    matches = crambin.GetSubstructMatches(carboxylateQuery, uniquify=False)
3962    self.assertEqual(len(matches), 3)
3963    matches = crambin.GetSubstructMatches(guanidiniumQuery)
3964    self.assertEqual(len(matches), 0)
3965    matches = crambin.GetSubstructMatches(guanidiniumQuery, uniquify=False)
3966    self.assertEqual(len(matches), 0)
3967    matches = resMolSupplST.GetSubstructMatches(carboxylateQuery)
3968    self.assertEqual(len(matches), 6)
3969    self.assertEqual(matches, ((166, 167, 168), (166, 168, 167), (298, 299, 300), (298, 300, 299),
3970                               (320, 321, 326), (320, 326, 321)))
3971    matches = resMolSupplST.GetSubstructMatches(carboxylateQuery, uniquify=True)
3972    self.assertEqual(len(matches), 3)
3973    self.assertEqual(matches, ((166, 167, 168), (298, 299, 300), (320, 321, 326)))
3974    matches = resMolSupplST.GetSubstructMatches(guanidiniumQuery)
3975    self.assertEqual(len(matches), 8)
3976    self.assertEqual(matches, ((66, 67, 68, 69), (66, 67, 69, 68), (68, 67, 69, 66),
3977                               (69, 67, 68, 66), (123, 124, 125, 126), (123, 124, 126, 125),
3978                               (125, 124, 126, 123), (126, 124, 125, 123)))
3979    matches = resMolSupplST.GetSubstructMatches(guanidiniumQuery, uniquify=True)
3980    self.assertEqual(len(matches), 2)
3981    self.assertEqual(matches, ((66, 67, 69, 68), (123, 124, 126, 125)))
3982    btList2ST = getBtList2(resMolSupplST)
3983    self.assertTrue(btList2ST)
3984    resMolSupplMT = Chem.ResonanceMolSupplier(crambin)
3985    resMolSupplMT.SetNumThreads(0)
3986    self.assertEqual(len(resMolSupplST), len(resMolSupplMT))
3987    btList2MT = getBtList2(resMolSupplMT)
3988    self.assertTrue(btList2MT)
3989    self.assertEqual(len(btList2ST), len(btList2MT))
3990    for i in range(len(btList2ST)):
3991      for j in range(len(btList2ST)):
3992        self.assertEqual(btList2ST[i][j], btList2MT[i][j])
3993    for suppl in [resMolSupplST, resMolSupplMT]:
3994      matches = suppl.GetSubstructMatches(carboxylateQuery, numThreads=0)
3995      self.assertEqual(len(matches), 6)
3996      self.assertEqual(matches, ((166, 167, 168), (166, 168, 167), (298, 299, 300), (298, 300, 299),
3997                                 (320, 321, 326), (320, 326, 321)))
3998      matches = suppl.GetSubstructMatches(carboxylateQuery, uniquify=True, numThreads=0)
3999      self.assertEqual(len(matches), 3)
4000      self.assertEqual(matches, ((166, 167, 168), (298, 299, 300), (320, 321, 326)))
4001      matches = suppl.GetSubstructMatches(guanidiniumQuery, numThreads=0)
4002      self.assertEqual(len(matches), 8)
4003      self.assertEqual(matches, ((66, 67, 68, 69), (66, 67, 69, 68), (68, 67, 69, 66),
4004                                 (69, 67, 68, 66), (123, 124, 125, 126), (123, 124, 126, 125),
4005                                 (125, 124, 126, 123), (126, 124, 125, 123)))
4006      matches = suppl.GetSubstructMatches(guanidiniumQuery, uniquify=True, numThreads=0)
4007      self.assertEqual(len(matches), 2)
4008      self.assertEqual(matches, ((66, 67, 69, 68), (123, 124, 126, 125)))
4009
4010  def testGitHub1166(self):
4011    mol = Chem.MolFromSmiles('NC(=[NH2+])c1ccc(cc1)C(=O)[O-]')
4012    resMolSuppl = Chem.ResonanceMolSupplier(mol, Chem.KEKULE_ALL)
4013    self.assertEqual(len(resMolSuppl), 8)
4014    # check that formal charges on odd indices are in the same position
4015    # as on even indices
4016    for i in range(0, len(resMolSuppl), 2):
4017      self.assertEqual(resMolSuppl[i].GetNumAtoms(), resMolSuppl[i + 1].GetNumAtoms())
4018      for atomIdx in range(resMolSuppl[i].GetNumAtoms()):
4019        self.assertEqual(resMolSuppl[i].GetAtomWithIdx(atomIdx).GetFormalCharge(),
4020                         resMolSuppl[i + 1].GetAtomWithIdx(atomIdx).GetFormalCharge())
4021      # check that bond orders are alternate on aromatic bonds between
4022      # structures on odd indices and structures on even indices
4023      self.assertEqual(resMolSuppl[i].GetNumBonds(), resMolSuppl[i + 1].GetNumBonds())
4024      for bondIdx in range(resMolSuppl[i].GetNumBonds()):
4025        self.assertTrue(
4026          ((not resMolSuppl[i].GetBondWithIdx(bondIdx).GetIsAromatic()) and
4027           (not resMolSuppl[i + 1].GetBondWithIdx(bondIdx).GetIsAromatic()) and
4028           (resMolSuppl[i].GetBondWithIdx(bondIdx).GetBondType()
4029            == resMolSuppl[i + 1].GetBondWithIdx(bondIdx).GetBondType()))
4030          or (resMolSuppl[i].GetBondWithIdx(bondIdx).GetIsAromatic()
4031              and resMolSuppl[i + 1].GetBondWithIdx(bondIdx).GetIsAromatic() and (int(
4032                round(resMolSuppl[i].GetBondWithIdx(bondIdx).GetBondTypeAsDouble() +
4033                      resMolSuppl[i + 1].GetBondWithIdx(bondIdx).GetBondTypeAsDouble())) == 3)))
4034
4035  def testConjGrpPerception(self):
4036    mol1 = Chem.MolFromMolBlock("""\
4037
4038     RDKit          2D
4039
4040 14 15  0  0  0  0  0  0  0  0999 V2000
4041    3.7539   -1.2744    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
4042    2.4317   -0.5660    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
4043    1.1571   -1.3568    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
4044   -0.1651   -0.6484    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
4045   -1.4397   -1.4393    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
4046   -1.3921   -2.9385    0.0000 F   0  0  0  0  0  0  0  0  0  0  0  0
4047   -2.7619   -0.7309    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
4048   -2.8095    0.7684    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
4049   -4.1316    1.4768    0.0000 F   0  0  0  0  0  0  0  0  0  0  0  0
4050   -1.5349    1.5592    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
4051   -0.2127    0.8508    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
4052    1.0619    1.6417    0.0000 N   0  0  0  0  0  0  0  0  0  0  0  0
4053    2.3841    0.9333    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
4054    3.6587    1.7241    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
4055  1  2  1  0
4056  2  3  4  0
4057  3  4  4  0
4058  4  5  4  0
4059  5  6  1  0
4060  5  7  4  0
4061  7  8  4  0
4062  8  9  1  0
4063  8 10  4  0
4064 10 11  4  0
4065 11 12  4  0
4066 12 13  4  0
4067 13 14  1  0
4068 13  2  4  0
4069 11  4  4  0
4070M  END
4071$$$$
4072""")
4073    mol2 = Chem.MolFromMolBlock("""\
4074
4075     RDKit          2D
4076
4077 14 15  0  0  0  0  0  0  0  0999 V2000
4078    1.0619   -1.6417    0.0000 N   0  0  0  0  0  0  0  0  0  0  0  0
4079   -0.2127   -0.8508    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
4080   -1.5349   -1.5592    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
4081   -2.8095   -0.7684    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
4082   -2.7619    0.7309    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
4083   -1.4397    1.4393    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
4084   -0.1651    0.6484    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
4085    1.1571    1.3568    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
4086    2.4317    0.5660    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
4087    3.7539    1.2744    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
4088    2.3841   -0.9333    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
4089    3.6587   -1.7241    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
4090   -4.1316   -1.4768    0.0000 F   0  0  0  0  0  0  0  0  0  0  0  0
4091   -1.3921    2.9385    0.0000 F   0  0  0  0  0  0  0  0  0  0  0  0
4092  1  2  4  0
4093  3  4  4  0
4094  4  5  4  0
4095  5  6  4  0
4096  2  3  4  0
4097  2  7  4  0
4098  7  8  4  0
4099  8  9  4  0
4100  9 10  1  0
4101  9 11  4  0
4102 11 12  1  0
4103 11  1  4  0
4104  6  7  4  0
4105  4 13  1  0
4106  6 14  1  0
4107M  END
4108$$$$
4109""")
4110    resMolSuppl1 = Chem.ResonanceMolSupplier(mol1, Chem.KEKULE_ALL)
4111    self.assertEqual(len(resMolSuppl1), 3)
4112    resMolSuppl2 = Chem.ResonanceMolSupplier(mol2, Chem.KEKULE_ALL)
4113    self.assertEqual(len(resMolSuppl2), 3)
4114
4115  def testGitHub2597(self):
4116
4117    class MyBrokenCallBack(Chem.ResonanceMolSupplier):
4118
4119      def __call__(self):
4120        return True
4121
4122    class MyBrokenCallBack2(Chem.ResonanceMolSupplierCallback):
4123      pass
4124
4125    class ExceedNumStructures(Chem.ResonanceMolSupplierCallback):
4126
4127      def __init__(self, parent):
4128        super().__init__()
4129        self._parent = parent
4130
4131      def __call__(self):
4132        self._parent.assertEqual(self.GetNumConjGrps(), 1)
4133        return (self.GetNumStructures(0) < 12)
4134
4135    class ExceedNumDiverseStructures(Chem.ResonanceMolSupplierCallback):
4136
4137      def __init__(self, parent):
4138        super().__init__()
4139        self._parent = parent
4140
4141      def __call__(self):
4142        self._parent.assertEqual(self.GetNumConjGrps(), 1)
4143        return (self.GetNumDiverseStructures(0) < 8)
4144
4145    class ExceedTimeout(Chem.ResonanceMolSupplierCallback):
4146
4147      def __init__(self, parent):
4148        super().__init__()
4149        self.start_time = None
4150        self.timeout = timedelta(seconds=3)
4151        self._parent = parent
4152
4153      def __call__(self):
4154        if (self.start_time is None):
4155          self.start_time = datetime.now()
4156        return (datetime.now() - self.start_time < self.timeout)
4157
4158    mol = Chem.MolFromSmiles(
4159      "ClC1=NC(NC2=CC=CC3=C2C(=O)C2=CC=CC=C2C3=O)=NC(NC2=CC=CC3=C2C(=O)C2=CC=CC=C2C3=O)=N1")
4160    resMolSuppl = Chem.ResonanceMolSupplier(mol)
4161    self.assertEqual(len(resMolSuppl), 1)
4162    resMolSuppl = Chem.ResonanceMolSupplier(mol, Chem.KEKULE_ALL)
4163
4164    self.assertEqual(len(resMolSuppl), 32)
4165    resMolSuppl = Chem.ResonanceMolSupplier(mol, Chem.ALLOW_CHARGE_SEPARATION, 10)
4166    self.assertEqual(len(resMolSuppl), 10)
4167    self.assertFalse(resMolSuppl.WasCanceled())
4168    resMolSuppl = Chem.ResonanceMolSupplier(mol, Chem.ALLOW_CHARGE_SEPARATION)
4169    callback = resMolSuppl.GetProgressCallback()
4170    self.assertIsNone(callback)
4171    resMolSuppl.SetProgressCallback(ExceedNumStructures(self))
4172    callback = resMolSuppl.GetProgressCallback()
4173    self.assertTrue(isinstance(callback, ExceedNumStructures))
4174    resMolSuppl.SetProgressCallback(None)
4175    callback = resMolSuppl.GetProgressCallback()
4176    self.assertIsNone(callback)
4177    resMolSuppl.SetProgressCallback(ExceedNumStructures(self))
4178    self.assertEqual(len(resMolSuppl), 12)
4179    self.assertTrue(resMolSuppl.WasCanceled())
4180    resMolSuppl = Chem.ResonanceMolSupplier(mol, Chem.ALLOW_CHARGE_SEPARATION)
4181    with self.assertRaises(TypeError):
4182      resMolSuppl.SetProgressCallback(MyBrokenCallBack())
4183    with self.assertRaises(AttributeError):
4184      resMolSuppl.SetProgressCallback(MyBrokenCallBack2())
4185    resMolSuppl.SetProgressCallback(ExceedNumDiverseStructures(self))
4186    self.assertEqual(len(resMolSuppl), 9)
4187    self.assertTrue(resMolSuppl.WasCanceled())
4188    resMolSuppl = Chem.ResonanceMolSupplier(
4189      mol, Chem.UNCONSTRAINED_CATIONS | Chem.UNCONSTRAINED_ANIONS | Chem.KEKULE_ALL)
4190    resMolSuppl.SetProgressCallback(ExceedTimeout(self))
4191    resMolSuppl.Enumerate()
4192    print(len(resMolSuppl))
4193    self.assertTrue(resMolSuppl.WasCanceled())
4194
4195  def testAtomBondProps(self):
4196    m = Chem.MolFromSmiles('c1ccccc1')
4197    for atom in m.GetAtoms():
4198      d = atom.GetPropsAsDict()
4199      self.assertEqual(set(d.keys()), set(['_CIPRank', '__computedProps']))
4200      self.assertEqual(d['_CIPRank'], 0)
4201      self.assertEqual(list(d['__computedProps']), ['_CIPRank'])
4202
4203    for bond in m.GetBonds():
4204      self.assertEqual(bond.GetPropsAsDict(), {})
4205
4206  def testSDProps(self):
4207    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
4208                         'NCI_aids_few.sdf')
4209    #fileN = "../FileParsers/test_data/NCI_aids_few.sdf"
4210    sddata = [
4211      {
4212        '_MolFileInfo': 'BBtclserve11129916382D 0   0.00000     0.00000    48',
4213        'NSC': 48,
4214        'NCI_AIDS_Antiviral_Screen_IC50': '2.00E-04\tM\t=\t2.46E-05\t3',
4215        '_Name': 48,
4216        'CAS_RN': '15716-70-8',
4217        '_MolFileComments': '15716-70-8',
4218        'NCI_AIDS_Antiviral_Screen_EC50': '2.00E-04\tM\t>\t2.00E-04\t3',
4219        'NCI_AIDS_Antiviral_Screen_Conclusion': 'CI'
4220      },
4221      {
4222        '_MolFileInfo': 'BBtclserve11129916382D 0   0.00000     0.00000    78',
4223        'NSC': 78,
4224        'NCI_AIDS_Antiviral_Screen_IC50': '2.00E-04\tM\t=\t9.80E-05\t3',
4225        '_Name': 78,
4226        'CAS_RN': '6290-84-2',
4227        '_MolFileComments': '6290-84-2',
4228        'NCI_AIDS_Antiviral_Screen_EC50': '2.00E-04\tM\t>\t2.00E-04\t3',
4229        'NCI_AIDS_Antiviral_Screen_Conclusion': 'CI'
4230      },
4231      {
4232        '_MolFileInfo': 'BBtclserve11129916382D 0   0.00000     0.00000   128',
4233        'NSC': 128,
4234        'NCI_AIDS_Antiviral_Screen_IC50': '2.00E-04\tM\t=\t4.60E-05\t4',
4235        '_Name': 128,
4236        'CAS_RN': '5395-10-8',
4237        '_MolFileComments': '5395-10-8',
4238        'NCI_AIDS_Antiviral_Screen_EC50': '2.00E-04\tM\t>\t2.00E-04\t4',
4239        'NCI_AIDS_Antiviral_Screen_Conclusion': 'CI'
4240      },
4241      {
4242        '_MolFileInfo': 'BBtclserve11129916382D 0   0.00000     0.00000   163',
4243        'NSC': 163,
4244        'NCI_AIDS_Antiviral_Screen_IC50': '6.75E-04\tM\t>\t6.75E-04\t2',
4245        '_Name': 163,
4246        'CAS_RN': '81-11-8',
4247        '_MolFileComments': '81-11-8',
4248        'NCI_AIDS_Antiviral_Screen_EC50': '6.75E-04\tM\t>\t6.75E-04\t2',
4249        'NCI_AIDS_Antiviral_Screen_Conclusion': 'CI'
4250      },
4251      {
4252        '_MolFileInfo': 'BBtclserve11129916382D 0   0.00000     0.00000   164',
4253        'NSC': 164,
4254        'NCI_AIDS_Antiviral_Screen_IC50': '2.00E-04\tM\t>\t2.00E-04\t2',
4255        '_Name': 164,
4256        'CAS_RN': '5325-43-9',
4257        '_MolFileComments': '5325-43-9',
4258        'NCI_AIDS_Antiviral_Screen_EC50': '2.00E-04\tM\t>\t2.00E-04\t2',
4259        'NCI_AIDS_Antiviral_Screen_Conclusion': 'CI'
4260      },
4261      {
4262        '_MolFileInfo': 'BBtclserve11129916382D 0   0.00000     0.00000   170',
4263        'NSC': 170,
4264        '_Name': 170,
4265        'CAS_RN': '999-99-9',
4266        '_MolFileComments': '999-99-9',
4267        'NCI_AIDS_Antiviral_Screen_EC50': '9.47E-04\tM\t>\t9.47E-04\t1',
4268        'NCI_AIDS_Antiviral_Screen_Conclusion': 'CI'
4269      },
4270      {
4271        '_MolFileInfo': 'BBtclserve11129916382D 0   0.00000     0.00000   180',
4272        'NSC': 180,
4273        'NCI_AIDS_Antiviral_Screen_IC50':
4274        '6.46E-04\tM\t=\t5.80E-04\t2\n1.81E-03\tM\t=\t6.90E-04\t2',
4275        '_Name': 180,
4276        'CAS_RN': '69-72-7',
4277        '_MolFileComments': '69-72-7',
4278        'NCI_AIDS_Antiviral_Screen_EC50':
4279        '6.46E-04\tM\t>\t6.46E-04\t2\n1.81E-03\tM\t>\t1.81E-03\t2',
4280        'NCI_AIDS_Antiviral_Screen_Conclusion': 'CI'
4281      },
4282      {
4283        '_MolFileInfo': 'BBtclserve11129916382D 0   0.00000     0.00000   186',
4284        'NSC': 186,
4285        'NCI_AIDS_Antiviral_Screen_IC50': '1.44E-04\tM\t=\t2.49E-05\t2',
4286        '_Name': 186,
4287        'CAS_RN': '518-75-2',
4288        '_MolFileComments': '518-75-2',
4289        'NCI_AIDS_Antiviral_Screen_EC50': '1.44E-04\tM\t>\t1.44E-04\t2',
4290        'NCI_AIDS_Antiviral_Screen_Conclusion': 'CI'
4291      },
4292      {
4293        '_MolFileInfo': 'BBtclserve11129916382D 0   0.00000     0.00000   192',
4294        'NSC': 192,
4295        'NCI_AIDS_Antiviral_Screen_IC50': '2.00E-04\tM\t=\t3.38E-06\t2',
4296        '_Name': 192,
4297        'CAS_RN': '2217-55-2',
4298        '_MolFileComments': '2217-55-2',
4299        'NCI_AIDS_Antiviral_Screen_EC50': '2.00E-04\tM\t>\t2.00E-04\t2',
4300        'NCI_AIDS_Antiviral_Screen_Conclusion': 'CI'
4301      },
4302      {
4303        '_MolFileInfo': 'BBtclserve11129916382D 0   0.00000     0.00000   203',
4304        'NSC': 203,
4305        '_Name': 203,
4306        'CAS_RN': '1155-00-6',
4307        '_MolFileComments': '1155-00-6',
4308        'NCI_AIDS_Antiviral_Screen_Conclusion': 'CI'
4309      },
4310      {
4311        '_MolFileInfo': 'BBtclserve11129916382D 0   0.00000     0.00000   210',
4312        'NSC': 210,
4313        'NCI_AIDS_Antiviral_Screen_IC50': '1.33E-03\tM\t>\t1.33E-03\t2',
4314        '_Name': 210,
4315        'CAS_RN': '5325-75-7',
4316        '_MolFileComments': '5325-75-7',
4317        'NCI_AIDS_Antiviral_Screen_EC50': '1.33E-03\tM\t>\t1.33E-03\t2',
4318        'NCI_AIDS_Antiviral_Screen_Conclusion': 'CI'
4319      },
4320      {
4321        '_MolFileInfo': 'BBtclserve11129916382D 0   0.00000     0.00000   211',
4322        'NSC': 211,
4323        'NCI_AIDS_Antiviral_Screen_IC50':
4324        '2.00E-04\tM\t>\t2.00E-04\t8\n2.00E-03\tM\t=\t1.12E-03\t2',
4325        '_Name': 211,
4326        'CAS_RN': '5325-76-8',
4327        '_MolFileComments': '5325-76-8',
4328        'NCI_AIDS_Antiviral_Screen_EC50':
4329        '2.00E-04\tM\t>\t7.42E-05\t8\n2.00E-03\tM\t=\t6.35E-05\t2',
4330        'NCI_AIDS_Antiviral_Screen_Conclusion': 'CM'
4331      },
4332      {
4333        '_MolFileInfo': 'BBtclserve11129916382D 0   0.00000     0.00000   213',
4334        'NSC': 213,
4335        'NCI_AIDS_Antiviral_Screen_IC50': '2.00E-04\tM\t>\t2.00E-04\t4',
4336        '_Name': 213,
4337        'CAS_RN': '119-80-2',
4338        '_MolFileComments': '119-80-2',
4339        'NCI_AIDS_Antiviral_Screen_EC50': '2.00E-04\tM\t>\t2.00E-04\t4',
4340        'NCI_AIDS_Antiviral_Screen_Conclusion': 'CI'
4341      },
4342      {
4343        '_MolFileInfo': 'BBtclserve11129916382D 0   0.00000     0.00000   220',
4344        'NSC': 220,
4345        'NCI_AIDS_Antiviral_Screen_IC50': '2.00E-04\tM\t>\t2.00E-04\t4',
4346        '_Name': 220,
4347        'CAS_RN': '5325-83-7',
4348        '_MolFileComments': '5325-83-7',
4349        'NCI_AIDS_Antiviral_Screen_EC50': '2.00E-04\tM\t>\t2.00E-04\t4',
4350        'NCI_AIDS_Antiviral_Screen_Conclusion': 'CI'
4351      },
4352      {
4353        '_MolFileInfo': 'BBtclserve11129916382D 0   0.00000     0.00000   229',
4354        'NSC': 229,
4355        'NCI_AIDS_Antiviral_Screen_IC50': '2.00E-04\tM\t>\t2.00E-04\t2',
4356        '_Name': 229,
4357        'CAS_RN': '5325-88-2',
4358        '_MolFileComments': '5325-88-2',
4359        'NCI_AIDS_Antiviral_Screen_EC50': '2.00E-04\tM\t>\t2.00E-04\t2',
4360        'NCI_AIDS_Antiviral_Screen_Conclusion': 'CI'
4361      },
4362      {
4363        '_MolFileInfo': 'BBtclserve11129916382D 0   0.00000     0.00000   256',
4364        'NSC': 256,
4365        'NCI_AIDS_Antiviral_Screen_IC50': '2.00E-04\tM\t>\t2.00E-04\t4',
4366        '_Name': 256,
4367        'CAS_RN': '5326-06-7',
4368        '_MolFileComments': '5326-06-7',
4369        'NCI_AIDS_Antiviral_Screen_EC50': '2.00E-04\tM\t>\t2.00E-04\t4',
4370        'NCI_AIDS_Antiviral_Screen_Conclusion': 'CI'
4371      },
4372    ]
4373    sdSup = Chem.SDMolSupplier(fileN)
4374    for i, mol in enumerate(sdSup):
4375      self.assertEqual(mol.GetPropsAsDict(includePrivate=True), sddata[i])
4376
4377  def testGetSetProps(self):
4378    m = Chem.MolFromSmiles("CC")
4379    errors = {
4380      "int": "key `foo` exists but does not result in an integer value",
4381      "double": "key `foo` exists but does not result in a double value",
4382      "bool": "key `foo` exists but does not result in a True or False value"
4383    }
4384
4385    for ob in [m, list(m.GetAtoms())[0], list(m.GetBonds())[0]]:
4386      ob.SetDoubleProp("foo", 2.0)
4387      with self.assertRaises(ValueError) as e:
4388        ob.GetBoolProp("foo")
4389      self.assertEqual(str(e.exception), errors["bool"])
4390
4391      with self.assertRaises(ValueError) as e:
4392        ob.GetIntProp("foo")
4393      self.assertEqual(str(e.exception), errors["int"])
4394
4395      ob.SetBoolProp("foo", True)
4396      with self.assertRaises(ValueError) as e:
4397        ob.GetDoubleProp("foo")
4398      self.assertEqual(str(e.exception), errors["double"])
4399
4400      with self.assertRaises(ValueError) as e:
4401        ob.GetIntProp("foo")
4402      self.assertEqual(str(e.exception), errors["int"])
4403
4404  def testInvariantException(self):
4405    m = Chem.MolFromSmiles("C")
4406    try:
4407      m.GetAtomWithIdx(3)
4408    except RuntimeError as e:
4409      import platform
4410      details = str(e)
4411      if platform.system() == 'Windows':
4412        details = details.replace('\\', '/')
4413      self.assertTrue("Code/GraphMol/ROMol.cpp".lower() in details.lower())
4414      self.assertTrue("Failed Expression: 3 < 1" in details)
4415      self.assertTrue("RDKIT:" in details)
4416      self.assertTrue(__version__ in details)
4417
4418  # this test should probably always be last since it wraps
4419  #  the logging stream
4420  def testLogging(self):
4421    from io import StringIO
4422    err = sys.stderr
4423    try:
4424      loggers = [("RDKit ERROR", "1", Chem.LogErrorMsg), ("RDKit WARNING", "2", Chem.LogWarningMsg)]
4425      for msg, v, log in loggers:
4426        sys.stderr = StringIO()
4427        log(v)
4428        self.assertEqual(sys.stderr.getvalue(), "")
4429
4430      Chem.WrapLogs()
4431      for msg, v, log in loggers:
4432        sys.stderr = StringIO()
4433        log(v)
4434        s = sys.stderr.getvalue()
4435        self.assertTrue(msg in s)
4436    finally:
4437      sys.stderr = err
4438
4439  def testGetSDText(self):
4440    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
4441                         'NCI_aids_few.sdf')
4442    #fileN = "../FileParsers/test_data/NCI_aids_few.sdf"
4443    sdSup = Chem.SDMolSupplier(fileN)
4444    for m in sdSup:
4445      sdt = Chem.SDWriter.GetText(m)
4446      ts = Chem.SDMolSupplier()
4447      ts.SetData(sdt)
4448      nm = next(ts)
4449      self.assertEqual(Chem.MolToSmiles(m, True), Chem.MolToSmiles(nm, True))
4450      for pn in m.GetPropNames():
4451        self.assertTrue(nm.HasProp(pn))
4452        self.assertEqual(m.GetProp(pn), nm.GetProp(pn))
4453
4454  def testUnfoldedRDKFingerprint(self):
4455    from rdkit.Chem import AllChem
4456
4457    m = Chem.MolFromSmiles('c1ccccc1N')
4458    fp = AllChem.UnfoldedRDKFingerprintCountBased(m)
4459    fpDict = fp.GetNonzeroElements()
4460    self.assertEqual(len(fpDict.items()), 19)
4461    self.assertTrue(374073638 in fpDict)
4462    self.assertEqual(fpDict[374073638], 6)
4463    self.assertTrue(464351883 in fpDict)
4464    self.assertEqual(fpDict[464351883], 2)
4465    self.assertTrue(1949583554 in fpDict)
4466    self.assertEqual(fpDict[1949583554], 6)
4467    self.assertTrue(4105342207 in fpDict)
4468    self.assertEqual(fpDict[4105342207], 1)
4469    self.assertTrue(794080973 in fpDict)
4470    self.assertEqual(fpDict[794080973], 1)
4471    self.assertTrue(3826517238 in fpDict)
4472    self.assertEqual(fpDict[3826517238], 2)
4473
4474    m = Chem.MolFromSmiles('Cl')
4475    fp = AllChem.UnfoldedRDKFingerprintCountBased(m)
4476    fpDict = fp.GetNonzeroElements()
4477    self.assertEqual(len(fpDict.items()), 0)
4478
4479    m = Chem.MolFromSmiles('CCCO')
4480    aBits = {}
4481    fp = AllChem.UnfoldedRDKFingerprintCountBased(m, bitInfo=aBits)
4482    fpDict = fp.GetNonzeroElements()
4483    self.assertEqual(len(fpDict.items()), 5)
4484    self.assertTrue(1524090560 in fpDict)
4485    self.assertEqual(fpDict[1524090560], 1)
4486    self.assertTrue(1940446997 in fpDict)
4487    self.assertEqual(fpDict[1940446997], 1)
4488    self.assertTrue(3977409745 in fpDict)
4489    self.assertEqual(fpDict[3977409745], 1)
4490    self.assertTrue(4274652475 in fpDict)
4491    self.assertEqual(fpDict[4274652475], 1)
4492    self.assertTrue(4275705116 in fpDict)
4493    self.assertEqual(fpDict[4275705116], 2)
4494
4495    self.assertTrue(1524090560 in aBits)
4496    self.assertEqual(aBits[1524090560], [[1, 2]])
4497    self.assertTrue(1940446997 in aBits)
4498    self.assertEqual(aBits[1940446997], [[0, 1]])
4499    self.assertTrue(3977409745 in aBits)
4500    self.assertEqual(aBits[3977409745], [[0, 1, 2]])
4501    self.assertTrue(4274652475 in aBits)
4502    self.assertEqual(aBits[4274652475], [[2]])
4503    self.assertTrue(4275705116 in aBits)
4504    self.assertEqual(aBits[4275705116], [[0], [1]])
4505
4506  def testRDKFingerprintBitInfo(self):
4507
4508    m = Chem.MolFromSmiles('CCCO')
4509    aBits = {}
4510    fp1 = Chem.RDKFingerprint(m, bitInfo=aBits)
4511    self.assertTrue(1183 in aBits)
4512    self.assertEqual(aBits[1183], [[1, 2]])
4513    self.assertTrue(709 in aBits)
4514    self.assertEqual(aBits[709], [[0, 1]])
4515    self.assertTrue(1118 in aBits)
4516    self.assertEqual(aBits[1118], [[0, 1, 2]])
4517    self.assertTrue(562 in aBits)
4518    self.assertEqual(aBits[562], [[2]])
4519    self.assertTrue(1772 in aBits)
4520    self.assertEqual(aBits[1772], [[0], [1]])
4521
4522  def testSimpleAromaticity(self):
4523    m = Chem.MolFromSmiles('c1ccccc1')
4524    self.assertTrue(m.GetBondWithIdx(0).GetIsAromatic())
4525    self.assertTrue(m.GetAtomWithIdx(0).GetIsAromatic())
4526    Chem.Kekulize(m, True)
4527    self.assertFalse(m.GetBondWithIdx(0).GetIsAromatic())
4528    self.assertFalse(m.GetAtomWithIdx(0).GetIsAromatic())
4529    Chem.SetAromaticity(m, Chem.AROMATICITY_SIMPLE)
4530    self.assertTrue(m.GetBondWithIdx(0).GetIsAromatic())
4531    self.assertTrue(m.GetAtomWithIdx(0).GetIsAromatic())
4532
4533    m = Chem.MolFromSmiles('c1c[nH]cc1')
4534    self.assertTrue(m.GetBondWithIdx(0).GetIsAromatic())
4535    self.assertTrue(m.GetAtomWithIdx(0).GetIsAromatic())
4536    Chem.Kekulize(m, True)
4537    self.assertFalse(m.GetBondWithIdx(0).GetIsAromatic())
4538    self.assertFalse(m.GetAtomWithIdx(0).GetIsAromatic())
4539    Chem.SetAromaticity(m, Chem.AROMATICITY_SIMPLE)
4540    self.assertTrue(m.GetBondWithIdx(0).GetIsAromatic())
4541    self.assertTrue(m.GetAtomWithIdx(0).GetIsAromatic())
4542
4543    m = Chem.MolFromSmiles('c1cccoocc1')
4544    self.assertTrue(m.GetBondWithIdx(0).GetIsAromatic())
4545    self.assertTrue(m.GetAtomWithIdx(0).GetIsAromatic())
4546    Chem.Kekulize(m, True)
4547    self.assertFalse(m.GetBondWithIdx(0).GetIsAromatic())
4548    self.assertFalse(m.GetAtomWithIdx(0).GetIsAromatic())
4549    Chem.SetAromaticity(m, Chem.AROMATICITY_SIMPLE)
4550    self.assertFalse(m.GetBondWithIdx(0).GetIsAromatic())
4551    self.assertFalse(m.GetAtomWithIdx(0).GetIsAromatic())
4552
4553    m = Chem.MolFromSmiles('c1ooc1')
4554    self.assertTrue(m.GetBondWithIdx(0).GetIsAromatic())
4555    self.assertTrue(m.GetAtomWithIdx(0).GetIsAromatic())
4556    Chem.Kekulize(m, True)
4557    self.assertFalse(m.GetBondWithIdx(0).GetIsAromatic())
4558    self.assertFalse(m.GetAtomWithIdx(0).GetIsAromatic())
4559    Chem.SetAromaticity(m, Chem.AROMATICITY_SIMPLE)
4560    self.assertFalse(m.GetBondWithIdx(0).GetIsAromatic())
4561    self.assertFalse(m.GetAtomWithIdx(0).GetIsAromatic())
4562
4563    m = Chem.MolFromSmiles('C1=CC2=CC=CC=CC2=C1')
4564    self.assertTrue(m.GetBondWithIdx(0).GetIsAromatic())
4565    self.assertTrue(m.GetAtomWithIdx(0).GetIsAromatic())
4566    Chem.Kekulize(m, True)
4567    self.assertFalse(m.GetBondWithIdx(0).GetIsAromatic())
4568    self.assertFalse(m.GetAtomWithIdx(0).GetIsAromatic())
4569    Chem.SetAromaticity(m, Chem.AROMATICITY_SIMPLE)
4570    self.assertFalse(m.GetBondWithIdx(0).GetIsAromatic())
4571    self.assertFalse(m.GetAtomWithIdx(0).GetIsAromatic())
4572
4573  def testGithub955(self):
4574    m = Chem.MolFromSmiles("CCC")
4575    m.GetAtomWithIdx(0).SetProp("foo", "1")
4576    self.assertEqual(list(m.GetAtomWithIdx(0).GetPropNames()), ["foo"])
4577    m.GetBondWithIdx(0).SetProp("foo", "1")
4578    self.assertEqual(list(m.GetBondWithIdx(0).GetPropNames()), ["foo"])
4579
4580  def testMDLProps(self):
4581    m = Chem.MolFromSmiles("CCC")
4582    m.GetAtomWithIdx(0).SetAtomMapNum(1)
4583    Chem.SetAtomAlias(m.GetAtomWithIdx(1), "foo")
4584    Chem.SetAtomValue(m.GetAtomWithIdx(1), "bar")
4585
4586    m = Chem.MolFromMolBlock(Chem.MolToMolBlock(m))
4587    self.assertEqual(m.GetAtomWithIdx(0).GetAtomMapNum(), 1)
4588    self.assertEqual(Chem.GetAtomAlias(m.GetAtomWithIdx(1)), "foo")
4589    self.assertEqual(Chem.GetAtomValue(m.GetAtomWithIdx(1)), "bar")
4590
4591  def testSmilesProps(self):
4592    m = Chem.MolFromSmiles("C")
4593    Chem.SetSupplementalSmilesLabel(m.GetAtomWithIdx(0), 'xxx')
4594    self.assertEqual(Chem.MolToSmiles(m), "Cxxx")
4595
4596  def testGithub1051(self):
4597    # just need to test that this exists:
4598    self.assertTrue(Chem.BondDir.EITHERDOUBLE)
4599
4600  def testGithub1041(self):
4601    a = Chem.Atom(6)
4602    self.assertRaises(RuntimeError, lambda: a.GetOwningMol())
4603    self.assertRaises(RuntimeError, lambda: a.GetNeighbors())
4604    self.assertRaises(RuntimeError, lambda: a.GetBonds())
4605    self.assertRaises(RuntimeError, lambda: a.IsInRing())
4606    self.assertRaises(RuntimeError, lambda: a.IsInRingSize(4))
4607
4608  def testSmilesParseParams(self):
4609    smi = "CCC |$foo;;bar$| ourname"
4610    m = Chem.MolFromSmiles(smi)
4611    self.assertTrue(m is not None)
4612    ps = Chem.SmilesParserParams()
4613    ps.allowCXSMILES = False
4614    m = Chem.MolFromSmiles(smi, ps)
4615    self.assertTrue(m is None)
4616    ps.allowCXSMILES = True
4617    ps.parseName = True
4618    m = Chem.MolFromSmiles(smi, ps)
4619    self.assertTrue(m is not None)
4620    self.assertTrue(m.GetAtomWithIdx(0).HasProp('atomLabel'))
4621    self.assertEqual(m.GetAtomWithIdx(0).GetProp('atomLabel'), "foo")
4622    self.assertTrue(m.HasProp('_Name'))
4623    self.assertEqual(m.GetProp('_Name'), "ourname")
4624    self.assertEqual(m.GetProp("_CXSMILES_Data"), "|$foo;;bar$|")
4625
4626  def testWriteCXSmiles(self):
4627    smi = "CCC |$foo;;bar$|"
4628    ps = Chem.SmilesParserParams()
4629    ps.allowCXSMILES = True
4630    m = Chem.MolFromSmiles(smi, ps)
4631    self.assertTrue(m is not None)
4632    self.assertTrue(m.GetAtomWithIdx(0).HasProp('atomLabel'))
4633    self.assertEqual(m.GetAtomWithIdx(0).GetProp('atomLabel'), "foo")
4634    self.assertEqual(Chem.MolToCXSmiles(m), 'CCC |$foo;;bar$|')
4635
4636    smi = "Cl.CCC |$;foo;;bar$|"
4637    m = Chem.MolFromSmiles(smi, ps)
4638    self.assertTrue(m is not None)
4639    self.assertTrue(m.GetAtomWithIdx(1).HasProp('atomLabel'))
4640    self.assertEqual(m.GetAtomWithIdx(1).GetProp('atomLabel'), "foo")
4641    self.assertEqual(Chem.MolFragmentToCXSmiles(m, atomsToUse=(1, 2, 3)), 'CCC |$foo;;bar$|')
4642
4643  def testPickleProps(self):
4644    import pickle
4645    m = Chem.MolFromSmiles('C1=CN=CC=C1')
4646    m.SetProp("_Name", "Name")
4647    for atom in m.GetAtoms():
4648      atom.SetProp("_foo", "bar" + str(atom.GetIdx()))
4649      atom.SetProp("foo", "baz" + str(atom.GetIdx()))
4650
4651    Chem.SetDefaultPickleProperties(Chem.PropertyPickleOptions.AllProps)
4652    pkl = pickle.dumps(m)
4653    m2 = pickle.loads(pkl)
4654    smi1 = Chem.MolToSmiles(m)
4655    smi2 = Chem.MolToSmiles(m2)
4656    self.assertTrue(smi1 == smi2)
4657    self.assertEqual(m2.GetProp("_Name"), "Name")
4658    for atom in m2.GetAtoms():
4659      self.assertEqual(atom.GetProp("_foo"), "bar" + str(atom.GetIdx()))
4660      self.assertEqual(atom.GetProp("foo"), "baz" + str(atom.GetIdx()))
4661
4662    Chem.SetDefaultPickleProperties(Chem.PropertyPickleOptions.AtomProps)
4663    pkl = pickle.dumps(m)
4664    m2 = pickle.loads(pkl)
4665    smi1 = Chem.MolToSmiles(m)
4666    smi2 = Chem.MolToSmiles(m2)
4667    self.assertTrue(smi1 == smi2)
4668    self.assertFalse(m2.HasProp("_Name"))
4669    for atom in m2.GetAtoms():
4670      self.assertFalse(atom.HasProp("_foo"))
4671      self.assertEqual(atom.GetProp("foo"), "baz" + str(atom.GetIdx()))
4672
4673    Chem.SetDefaultPickleProperties(Chem.PropertyPickleOptions.NoProps)
4674    pkl = pickle.dumps(m)
4675    m2 = pickle.loads(pkl)
4676    smi1 = Chem.MolToSmiles(m)
4677    smi2 = Chem.MolToSmiles(m2)
4678    self.assertTrue(smi1 == smi2)
4679    self.assertFalse(m2.HasProp("_Name"))
4680    for atom in m2.GetAtoms():
4681      self.assertFalse(atom.HasProp("_foo"))
4682      self.assertFalse(atom.HasProp("foo"))
4683
4684    Chem.SetDefaultPickleProperties(Chem.PropertyPickleOptions.MolProps
4685                                    | Chem.PropertyPickleOptions.PrivateProps)
4686    pkl = pickle.dumps(m)
4687    m2 = pickle.loads(pkl)
4688    smi1 = Chem.MolToSmiles(m)
4689    smi2 = Chem.MolToSmiles(m2)
4690    self.assertTrue(smi1 == smi2)
4691    self.assertEqual(m2.GetProp("_Name"), "Name")
4692    for atom in m2.GetAtoms():
4693      self.assertFalse(atom.HasProp("_foo"))
4694      self.assertFalse(atom.HasProp("foo"))
4695
4696  def testGithub1352(self):
4697    self.assertTrue('SP' in Chem.HybridizationType.names)
4698    self.assertTrue('S' in Chem.HybridizationType.names)
4699    m = Chem.MolFromSmiles('CC(=O)O.[Na]')
4700    self.assertEqual(m.GetAtomWithIdx(0).GetHybridization().name, 'SP3')
4701    self.assertEqual(m.GetAtomWithIdx(4).GetHybridization().name, 'S')
4702
4703  def testGithub1366(self):
4704    mol = Chem.MolFromSmiles('*C*')
4705    mol = Chem.RWMol(mol)
4706    ats = iter(mol.GetAtoms())
4707    atom = next(ats)
4708    mol.RemoveAtom(atom.GetIdx())
4709    self.assertRaises(RuntimeError, next, ats)
4710
4711    mol = Chem.MolFromSmiles('*C*')
4712    mol = Chem.RWMol(mol)
4713    bonds = iter(mol.GetBonds())
4714    bond = next(bonds)
4715    mol.RemoveBond(bond.GetBeginAtomIdx(), bond.GetEndAtomIdx())
4716    self.assertRaises(RuntimeError, next, bonds)
4717
4718  def testGithub1478(self):
4719    data = """
4720  MJ150720
4721
4722  8  8  0  0  0  0  0  0  0  0999 V2000
4723   -0.4242   -1.4883    0.0000 O   0  0  0  0  0  0  0  0  0  0  0  0
4724    0.2901   -1.0758    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
4725    1.0046    0.9865    0.0000 A   0  0  0  0  0  0  0  0  0  0  0  0
4726    1.0046    0.1614    0.0000 A   0  0  0  0  0  0  0  0  0  0  0  0
4727    0.2901   -0.2508    0.0000 A   0  0  0  0  0  0  0  0  0  0  0  0
4728   -0.4243    0.1614    0.0000 A   0  0  0  0  0  0  0  0  0  0  0  0
4729   -0.4243    0.9865    0.0000 A   0  0  0  0  0  0  0  0  0  0  0  0
4730    0.2901    1.3990    0.0000 A   0  0  0  0  0  0  0  0  0  0  0  0
4731  7  6  4  0  0  0  0
4732  8  7  4  0  0  0  0
4733  6  5  4  0  0  0  0
4734  5  4  4  0  0  0  0
4735  5  2  1  0  0  0  0
4736  4  3  4  0  0  0  0
4737  8  3  4  0  0  0  0
4738  2  1  2  0  0  0  0
4739M  END
4740"""
4741    pattern = Chem.MolFromMolBlock(data)
4742    m = Chem.MolFromSmiles("c1ccccc1C=O")
4743    self.assertTrue(m.HasSubstructMatch(pattern))
4744
4745  def testGithub1320(self):
4746    import pickle
4747    mol = Chem.MolFromSmiles('N[C@@H](C)O')
4748    mol2 = pickle.loads(pickle.dumps(mol))
4749    self.assertEqual(Chem.MolToSmiles(mol, isomericSmiles=True),
4750                     Chem.MolToSmiles(mol2, isomericSmiles=True))
4751    Chem.SetDefaultPickleProperties(Chem.PropertyPickleOptions.AtomProps
4752                                    | Chem.PropertyPickleOptions.BondProps
4753                                    | Chem.PropertyPickleOptions.MolProps
4754                                    | Chem.PropertyPickleOptions.PrivateProps
4755                                    | Chem.PropertyPickleOptions.ComputedProps)
4756    mol3 = pickle.loads(pickle.dumps(mol))
4757
4758    for a1, a2 in zip(mol.GetAtoms(), mol3.GetAtoms()):
4759      d1 = a1.GetPropsAsDict()
4760      d2 = a2.GetPropsAsDict()
4761      if "__computedProps" in d1:
4762        c1 = list(d1["__computedProps"])
4763        c2 = list(d2["__computedProps"])
4764        del d1["__computedProps"]
4765        del d2["__computedProps"]
4766        self.assertEqual(c1, c2)
4767
4768      assert d1 == d2
4769
4770    for a1, a2 in zip(mol.GetBonds(), mol3.GetBonds()):
4771      d1 = a1.GetPropsAsDict()
4772      d2 = a2.GetPropsAsDict()
4773      if "__computedProps" in d1:
4774        c1 = list(d1["__computedProps"])
4775        c2 = list(d2["__computedProps"])
4776        del d1["__computedProps"]
4777        del d2["__computedProps"]
4778        self.assertEqual(c1, c2)
4779
4780      assert d1 == d2
4781
4782    self.assertEqual(Chem.MolToSmiles(mol, isomericSmiles=True),
4783                     Chem.MolToSmiles(mol3, isomericSmiles=True))
4784
4785  def testOldPropPickles(self):
4786    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.'
4787    import pickle
4788    # bonds were broken in v1
4789    m2 = pickle.loads(data.encode("utf-8"), encoding='bytes')
4790
4791    self.assertEqual(m2.GetProp("foo"), "bar")
4792    for atom in m2.GetAtoms():
4793      self.assertEqual(atom.GetProp("myidx"), str(atom.GetIdx()))
4794
4795    self.assertEqual(
4796      Chem.MolToSmiles(m2, True),
4797      Chem.MolToSmiles(
4798        Chem.MolFromSmiles(
4799          "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"
4800        ), True))
4801
4802  def testGithub1461(self):
4803    # this is simple, it should throw a precondition and not seg fault
4804    m = Chem.RWMol()
4805    try:
4806      m.AddBond(0, 1, Chem.BondType.SINGLE)
4807      self.assertFalse(True)  # shouldn't get here
4808    except RuntimeError:
4809      pass
4810
4811  def testMolBundles1(self):
4812    b = Chem.MolBundle()
4813    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)')
4814    for smi in smis:
4815      b.AddMol(Chem.MolFromSmiles(smi))
4816    self.assertEqual(len(b), 3)
4817    self.assertEqual(b.Size(), 3)
4818    self.assertRaises(IndexError, lambda: b[4])
4819    self.assertEqual(Chem.MolToSmiles(b[1], isomericSmiles=True),
4820                     Chem.MolToSmiles(Chem.MolFromSmiles(smis[1]), isomericSmiles=True))
4821    self.assertTrue(b.HasSubstructMatch(Chem.MolFromSmiles('CC(Cl)(F)CC(F)(Br)'),
4822                                        useChirality=True))
4823    self.assertTrue(
4824      b.HasSubstructMatch(Chem.MolFromSmiles('C[C@](Cl)(F)C[C@@H](F)(Br)'), useChirality=True))
4825    self.assertTrue(
4826      b.HasSubstructMatch(Chem.MolFromSmiles('C[C@@](Cl)(F)C[C@@H](F)(Br)'), useChirality=False))
4827    self.assertFalse(
4828      b.HasSubstructMatch(Chem.MolFromSmiles('C[C@@](Cl)(F)C[C@@H](F)(Br)'), useChirality=True))
4829
4830    self.assertEqual(
4831      len(b.GetSubstructMatch(Chem.MolFromSmiles('CC(Cl)(F)CC(F)(Br)'), useChirality=True)), 8)
4832    self.assertEqual(
4833      len(b.GetSubstructMatch(Chem.MolFromSmiles('C[C@](Cl)(F)C[C@@H](F)(Br)'), useChirality=True)),
4834      8)
4835    self.assertEqual(
4836      len(b.GetSubstructMatch(Chem.MolFromSmiles('C[C@@](Cl)(F)C[C@@H](F)(Br)'),
4837                              useChirality=False)), 8)
4838    self.assertEqual(
4839      len(b.GetSubstructMatch(Chem.MolFromSmiles('C[C@@](Cl)(F)C[C@@H](F)(Br)'),
4840                              useChirality=True)), 0)
4841
4842    self.assertEqual(
4843      len(b.GetSubstructMatches(Chem.MolFromSmiles('CC(Cl)(F)CC(F)(Br)'), useChirality=True)), 1)
4844    self.assertEqual(
4845      len(b.GetSubstructMatches(Chem.MolFromSmiles('C[C@](Cl)(F)C[C@@H](F)(Br)'),
4846                                useChirality=True)), 1)
4847    self.assertEqual(
4848      len(
4849        b.GetSubstructMatches(Chem.MolFromSmiles('C[C@@](Cl)(F)C[C@@H](F)(Br)'),
4850                              useChirality=False)), 1)
4851    self.assertEqual(
4852      len(
4853        b.GetSubstructMatches(Chem.MolFromSmiles('C[C@@](Cl)(F)C[C@@H](F)(Br)'),
4854                              useChirality=True)), 0)
4855    self.assertEqual(
4856      len(b.GetSubstructMatches(Chem.MolFromSmiles('CC(Cl)(F)CC(F)(Br)'), useChirality=True)[0]), 8)
4857    self.assertEqual(
4858      len(
4859        b.GetSubstructMatches(Chem.MolFromSmiles('C[C@](Cl)(F)C[C@@H](F)(Br)'),
4860                              useChirality=True)[0]), 8)
4861    self.assertEqual(
4862      len(
4863        b.GetSubstructMatches(Chem.MolFromSmiles('C[C@@](Cl)(F)C[C@@H](F)(Br)'),
4864                              useChirality=False)[0]), 8)
4865
4866  def testMolBundles2(self):
4867    b = Chem.MolBundle()
4868    smis = ('Fc1c(Cl)cccc1', 'Fc1cc(Cl)ccc1', 'Fc1ccc(Cl)cc1')
4869    for smi in smis:
4870      b.AddMol(Chem.MolFromSmiles(smi))
4871    self.assertEqual(len(b), 3)
4872    self.assertEqual(b.Size(), 3)
4873    self.assertTrue(Chem.MolFromSmiles('Fc1c(Cl)cccc1').HasSubstructMatch(b))
4874    self.assertTrue(Chem.MolFromSmiles('Fc1cc(Cl)ccc1').HasSubstructMatch(b))
4875    self.assertTrue(Chem.MolFromSmiles('Fc1c(Cl)cccc1C').HasSubstructMatch(b))
4876    self.assertTrue(Chem.MolFromSmiles('Fc1cc(Cl)ccc1C').HasSubstructMatch(b))
4877    self.assertFalse(Chem.MolFromSmiles('Fc1c(Br)cccc1').HasSubstructMatch(b))
4878
4879    self.assertEqual(len(Chem.MolFromSmiles('Fc1c(Cl)cccc1').GetSubstructMatch(b)), 8)
4880    self.assertEqual(len(Chem.MolFromSmiles('Fc1c(Cl)cccc1').GetSubstructMatches(b)), 1)
4881    self.assertEqual(len(Chem.MolFromSmiles('Fc1c(Cl)cccc1').GetSubstructMatches(b)[0]), 8)
4882    self.assertEqual(len(Chem.MolFromSmiles('Fc1ccc(Cl)cc1').GetSubstructMatches(b)), 1)
4883    self.assertEqual(
4884      len(Chem.MolFromSmiles('Fc1ccc(Cl)cc1').GetSubstructMatches(b, uniquify=False)), 2)
4885
4886    self.assertEqual(len(Chem.MolFromSmiles('Fc1c(C)cccc1').GetSubstructMatch(b)), 0)
4887    self.assertEqual(len(Chem.MolFromSmiles('Fc1c(C)cccc1').GetSubstructMatches(b)), 0)
4888
4889  def testMolBundles3(self):
4890    smis = ('CCC', 'CCO', 'CCN')
4891    b = Chem.FixedMolSizeMolBundle()
4892    for smi in smis:
4893      b.AddMol(Chem.MolFromSmiles(smi))
4894    self.assertEqual(len(b), 3)
4895    self.assertEqual(b.Size(), 3)
4896    with self.assertRaises(ValueError):
4897      b.AddMol(Chem.MolFromSmiles('CCCC'))
4898
4899    b = Chem.MolBundle()
4900    for smi in smis:
4901      b.AddMol(Chem.MolFromSmiles(smi))
4902    self.assertEqual(len(b), 3)
4903    self.assertEqual(b.Size(), 3)
4904    b.AddMol(Chem.MolFromSmiles('CCCC'))
4905    self.assertEqual(b.Size(), 4)
4906
4907  def testGithub1622(self):
4908    nonaromatics = (
4909      "C1=C[N]C=C1",  # radicals are not two electron donors
4910      "O=C1C=CNC=C1",  # exocyclic double bonds don't steal electrons
4911      "C1=CS(=O)C=C1",  # not sure how to classify this example from the
4912      # OEChem docs
4913      "C1#CC=CC=C1"  # benzyne
4914      # 5-membered heterocycles
4915      "C1=COC=C1",  # furan
4916      "C1=CSC=C1",  # thiophene
4917      "C1=CNC=C1",  #pyrrole
4918      "C1=COC=N1",  # oxazole
4919      "C1=CSC=N1",  # thiazole
4920      "C1=CNC=N1",  # imidazole
4921      "C1=CNN=C1",  # pyrazole
4922      "C1=CON=C1",  # isoxazole
4923      "C1=CSN=C1",  # isothiazole
4924      "C1=CON=N1",  # 1,2,3-oxadiazole
4925      "C1=CNN=N1",  # 1,2,3-triazole
4926      "N1=CSC=N1",  # 1,3,4-thiadiazole
4927      # not outside the second rows
4928      "C1=CC=C[Si]=C1",
4929      "C1=CC=CC=P1",
4930      # 5-membered heterocycles outside the second row
4931      "C1=C[Se]C=C1",
4932      'C1=C[Te]C=C1')
4933    for smi in nonaromatics:
4934      m = Chem.MolFromSmiles(smi, sanitize=False)
4935      Chem.SanitizeMol(m, Chem.SANITIZE_ALL ^ Chem.SANITIZE_SETAROMATICITY)
4936      Chem.SetAromaticity(m, Chem.AROMATICITY_MDL)
4937      self.assertFalse(m.GetAtomWithIdx(0).GetIsAromatic())
4938    aromatics = (
4939      "C1=CC=CC=C1",  # benzene, of course
4940      # hetrocyclics
4941      "N1=CC=CC=C1",  # pyridine
4942      "N1=CC=CC=N1",  # pyridazine
4943      "N1=CC=CN=C1",  # pyrimidine
4944      "N1=CC=NC=C1",  # pyrazine
4945      "N1=CN=CN=C1",  # 1,3,5-triazine
4946      # polycyclic aromatics
4947      "C1=CC2=CC=CC=CC2=C1",  # azulene
4948      "C1=CC=CC2=CC=CC=C12",
4949      "C1=CC2=CC=CC=CC=C12",
4950      "C1=CC=C2C(=C1)N=CC=N2",
4951      "C1=CN=CC2C=CC=CC1=2",
4952      "C1=CC=C2C(=C1)N=C3C=CC=CC3=N2",
4953      "C1=CN=NC2C=CC=CC1=2",
4954      # macrocycle aromatics
4955      "C1=CC=CC=CC=CC=C1",
4956      "C1=CC=CC=CC=CC=CC=CC=CC=CC=C1",
4957      "N1=CN=NC=CC=CC=CC=CC=CC=CC=CC=CC=CC=CC=CC=CC=C1")
4958    for smi in aromatics:
4959      m = Chem.MolFromSmiles(smi, sanitize=False)
4960      Chem.SanitizeMol(m, Chem.SANITIZE_ALL ^ Chem.SANITIZE_SETAROMATICITY)
4961      Chem.SetAromaticity(m, Chem.AROMATICITY_MDL)
4962      self.assertTrue(m.GetAtomWithIdx(0).GetIsAromatic())
4963
4964  def testMolBlockChirality(self):
4965    m = Chem.MolFromSmiles('C[C@H](Cl)Br')
4966    mb = Chem.MolToMolBlock(m)
4967    m2 = Chem.MolFromMolBlock(mb)
4968    csmi1 = Chem.MolToSmiles(m, isomericSmiles=True)
4969    csmi2 = Chem.MolToSmiles(m2, isomericSmiles=True)
4970    self.assertEqual(csmi1, csmi2)
4971
4972  def testIssue1735(self):
4973    # this shouldn't seg fault...
4974    m = Chem.RWMol()
4975    ranks = Chem.CanonicalRankAtoms(m, breakTies=False)
4976    ranks = Chem.CanonicalRankAtoms(m, breakTies=True)
4977
4978  def testGithub1615(self):
4979    mb = """Issue399a.mol
4980  ChemDraw04050615582D
4981
4982  4  4  0  0  0  0  0  0  0  0999 V2000
4983   -0.7697    0.0000    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
4984    0.0553    0.0000    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
4985    0.7697    0.4125    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
4986    0.7697   -0.4125    0.0000 O   0  0  0  0  0  0  0  0  0  0  0  0
4987  2  1  1  0
4988  2  3  1  0
4989  3  4  1  0
4990  2  4  1  0
4991M  END"""
4992    m = Chem.MolFromMolBlock(mb)
4993    self.assertFalse(m.GetAtomWithIdx(1).HasProp("_CIPCode"))
4994    self.assertEqual(m.GetBondWithIdx(0).GetBondDir(), Chem.BondDir.NONE)
4995    self.assertEqual(m.GetAtomWithIdx(1).GetChiralTag(), Chem.ChiralType.CHI_UNSPECIFIED)
4996    m.GetAtomWithIdx(1).SetChiralTag(Chem.ChiralType.CHI_TETRAHEDRAL_CW)
4997    Chem.AssignStereochemistry(m, force=True)
4998    self.assertTrue(m.GetAtomWithIdx(1).HasProp("_CIPCode"))
4999    self.assertEqual(m.GetAtomWithIdx(1).GetProp("_CIPCode"), "S")
5000    self.assertEqual(m.GetBondWithIdx(0).GetBondDir(), Chem.BondDir.NONE)
5001    Chem.WedgeBond(m.GetBondWithIdx(0), 1, m.GetConformer())
5002    self.assertEqual(m.GetBondWithIdx(0).GetBondDir(), Chem.BondDir.BEGINWEDGE)
5003
5004  def testSmilesToAtom(self):
5005    a = Chem.AtomFromSmiles("C")
5006    self.assertEqual(a.GetAtomicNum(), 6)
5007    b = Chem.BondFromSmiles("=")
5008    self.assertEqual(b.GetBondType(), Chem.BondType.DOUBLE)
5009    a = Chem.AtomFromSmiles("error")
5010    self.assertIs(a, None)
5011    b = Chem.BondFromSmiles("d")
5012    self.assertIs(b, None)
5013
5014    a = Chem.AtomFromSmarts("C")
5015    self.assertEqual(a.GetAtomicNum(), 6)
5016    b = Chem.BondFromSmarts("=")
5017    self.assertEqual(b.GetBondType(), Chem.BondType.DOUBLE)
5018    a = Chem.AtomFromSmarts("error")
5019    self.assertIs(a, None)
5020    b = Chem.BondFromSmarts("d")
5021    self.assertIs(b, None)
5022
5023  def testSVGParsing(self):
5024    svg = """<?xml version='1.0' encoding='iso-8859-1'?>
5025<svg version='1.1' baseProfile='full'
5026              xmlns='http://www.w3.org/2000/svg'
5027                      xmlns:rdkit='http://www.rdkit.org/xml'
5028                      xmlns:xlink='http://www.w3.org/1999/xlink'
5029                  xml:space='preserve'
5030width='200px' height='200px' >
5031<rect style='opacity:1.0;fill:#FFFFFF;stroke:none' width='200' height='200' x='0' y='0'> </rect>
5032<path d='M 9.09091,89.4974 24.2916,84.7462' style='fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1' />
5033<path d='M 24.2916,84.7462 39.4923,79.9949' style='fill:none;fill-rule:evenodd;stroke:#0000FF;stroke-width:2px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1' />
5034<path d='M 86.2908,106.814 75.1709,93.4683 72.0765,96.8285 86.2908,106.814' style='fill:#000000;fill-rule:evenodd;stroke:#000000;stroke-width:2px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1' />
5035<path d='M 75.1709,93.4683 57.8622,86.8431 64.051,80.1229 75.1709,93.4683' style='fill:#0000FF;fill-rule:evenodd;stroke:#0000FF;stroke-width:2px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1' />
5036<path d='M 75.1709,93.4683 72.0765,96.8285 57.8622,86.8431 75.1709,93.4683' style='fill:#0000FF;fill-rule:evenodd;stroke:#0000FF;stroke-width:2px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1' />
5037<path d='M 86.2908,106.814 82.1459,125.293' style='fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1' />
5038<path d='M 82.1459,125.293 78.0009,143.772' style='fill:none;fill-rule:evenodd;stroke:#00CC00;stroke-width:2px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1' />
5039<path d='M 86.2908,106.814 129.89,93.1862' style='fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1' />
5040<path d='M 134.347,94.186 138.492,75.7069' style='fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1' />
5041<path d='M 138.492,75.7069 142.637,57.2277' style='fill:none;fill-rule:evenodd;stroke:#FF0000;stroke-width:2px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1' />
5042<path d='M 125.432,92.1865 129.577,73.7074' style='fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1' />
5043<path d='M 129.577,73.7074 133.722,55.2282' style='fill:none;fill-rule:evenodd;stroke:#FF0000;stroke-width:2px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1' />
5044<path d='M 129.89,93.1862 142.557,104.852' style='fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1' />
5045<path d='M 142.557,104.852 155.224,116.517' style='fill:none;fill-rule:evenodd;stroke:#FF0000;stroke-width:2px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1' />
5046<text x='39.4923' y='83.483' style='font-size:15px;font-style:normal;font-weight:normal;fill-opacity:1;stroke:none;font-family:sans-serif;text-anchor:start;fill:#0000FF' ><tspan>NH</tspan></text>
5047<text x='67.6656' y='158.998' style='font-size:15px;font-style:normal;font-weight:normal;fill-opacity:1;stroke:none;font-family:sans-serif;text-anchor:start;fill:#00CC00' ><tspan>Cl</tspan></text>
5048<text x='132.777' y='56.228' style='font-size:15px;font-style:normal;font-weight:normal;fill-opacity:1;stroke:none;font-family:sans-serif;text-anchor:start;fill:#FF0000' ><tspan>O</tspan></text>
5049<text x='149.782' y='131.743' style='font-size:15px;font-style:normal;font-weight:normal;fill-opacity:1;stroke:none;font-family:sans-serif;text-anchor:start;fill:#FF0000' ><tspan>OH</tspan></text>
5050<text x='89.9952' y='194' style='font-size:12px;font-style:normal;font-weight:normal;fill-opacity:1;stroke:none;font-family:sans-serif;text-anchor:start;fill:#000000' ><tspan>m1</tspan></text>
5051<metadata>
5052<rdkit:mol xmlns:rdkit = "http://www.rdkit.org/xml" version="0.9">
5053<rdkit:atom idx="1" atom-smiles="[CH3]" drawing-x="9.09091" drawing-y="89.4974" x="-2.78651" y="0.295614" z="0" />
5054<rdkit:atom idx="2" atom-smiles="[NH]" drawing-x="52.6897" drawing-y="75.8699" x="-1.35482" y="0.743114" z="0" />
5055<rdkit:atom idx="3" atom-smiles="[C@H]" drawing-x="86.2908" drawing-y="106.814" x="-0.251428" y="-0.273019" z="0" />
5056<rdkit:atom idx="4" atom-smiles="[Cl]" drawing-x="76.2932" drawing-y="151.385" x="-0.579728" y="-1.73665" z="0" />
5057<rdkit:atom idx="5" atom-smiles="[C]" drawing-x="129.89" drawing-y="93.1862" x="1.18027" y="0.174481" z="0" />
5058<rdkit:atom idx="6" atom-smiles="[O]" drawing-x="139.887" drawing-y="48.6148" x="1.50857" y="1.63811" z="0" />
5059<rdkit:atom idx="7" atom-smiles="[OH]" drawing-x="163.491" drawing-y="124.13" x="2.28366" y="-0.841652" z="0" />
5060<rdkit:bond idx="1" begin-atom-idx="1" end-atom-idx="2" bond-smiles="-" />
5061<rdkit:bond idx="2" begin-atom-idx="2" end-atom-idx="3" bond-smiles="-" />
5062<rdkit:bond idx="3" begin-atom-idx="3" end-atom-idx="4" bond-smiles="-" />
5063<rdkit:bond idx="4" begin-atom-idx="3" end-atom-idx="5" bond-smiles="-" />
5064<rdkit:bond idx="5" begin-atom-idx="5" end-atom-idx="6" bond-smiles="=" />
5065<rdkit:bond idx="6" begin-atom-idx="5" end-atom-idx="7" bond-smiles="-" />
5066</rdkit:mol></metadata>
5067</svg>"""
5068    mol = Chem.MolFromRDKitSVG(svg)
5069    self.assertEqual(mol.GetNumAtoms(), 7)
5070    self.assertEqual(Chem.MolToSmiles(mol), 'CN[C@H](Cl)C(=O)O')
5071
5072    svg2 = """<?xml version='1.0' encoding='iso-8859-1'?>
5073<svg version='1.1' baseProfile='full'
5074          xmlns='http://www.w3.org/2000/svg'
5075                  xmlns:rdkit='http://www.rdkit.org/xml'
5076                  xmlns:xlink='http://www.w3.org/1999/xlink'
5077              xml:space='preserve'
5078width='200px' height='200px' >
5079<rect style='opacity:1.0;fill:#FFFFFF;stroke:none' width='200' height='200' x='0' y='0'> </rect>
5080<path d='M 9.09091,89.4974 24.2916,84.7462' style='fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1' />
5081<path d='M 24.2916,84.7462 39.4923,79.9949' style='fill:none;fill-rule:evenodd;stroke:#0000FF;stroke-width:2px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1' />
5082<path d='M 86.2908,106.814 75.1709,93.4683 72.0765,96.8285 86.2908,106.814' style='fill:#000000;fill-rule:evenodd;stroke:#000000;stroke-width:2px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1' />
5083<path d='M 75.1709,93.4683 57.8622,86.8431 64.051,80.1229 75.1709,93.4683' style='fill:#0000FF;fill-rule:evenodd;stroke:#0000FF;stroke-width:2px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1' />
5084<path d='M 75.1709,93.4683 72.0765,96.8285 57.8622,86.8431 75.1709,93.4683' style='fill:#0000FF;fill-rule:evenodd;stroke:#0000FF;stroke-width:2px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1' />
5085<path d='M 86.2908,106.814 82.1459,125.293' style='fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1' />
5086<path d='M 82.1459,125.293 78.0009,143.772' style='fill:none;fill-rule:evenodd;stroke:#00CC00;stroke-width:2px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1' />
5087<path d='M 86.2908,106.814 129.89,93.1862' style='fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1' />
5088<path d='M 134.347,94.186 138.492,75.7069' style='fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1' />
5089<path d='M 138.492,75.7069 142.637,57.2277' style='fill:none;fill-rule:evenodd;stroke:#FF0000;stroke-width:2px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1' />
5090<path d='M 125.432,92.1865 129.577,73.7074' style='fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1' />
5091<path d='M 129.577,73.7074 133.722,55.2282' style='fill:none;fill-rule:evenodd;stroke:#FF0000;stroke-width:2px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1' />
5092<path d='M 129.89,93.1862 142.557,104.852' style='fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1' />
5093<path d='M 142.557,104.852 155.224,116.517' style='fill:none;fill-rule:evenodd;stroke:#FF0000;stroke-width:2px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1' />
5094<text x='39.4923' y='83.483' style='font-size:15px;font-style:normal;font-weight:normal;fill-opacity:1;stroke:none;font-family:sans-serif;text-anchor:start;fill:#0000FF' ><tspan>NH</tspan></text>
5095<text x='67.6656' y='158.998' style='font-size:15px;font-style:normal;font-weight:normal;fill-opacity:1;stroke:none;font-family:sans-serif;text-anchor:start;fill:#00CC00' ><tspan>Cl</tspan></text>
5096<text x='132.777' y='56.228' style='font-size:15px;font-style:normal;font-weight:normal;fill-opacity:1;stroke:none;font-family:sans-serif;text-anchor:start;fill:#FF0000' ><tspan>O</tspan></text>
5097<text x='149.782' y='131.743' style='font-size:15px;font-style:normal;font-weight:normal;fill-opacity:1;stroke:none;font-family:sans-serif;text-anchor:start;fill:#FF0000' ><tspan>OH</tspan></text>
5098<text x='89.9952' y='194' style='font-size:12px;font-style:normal;font-weight:normal;fill-opacity:1;stroke:none;font-family:sans-serif;text-anchor:start;fill:#000000' ><tspan>m1</tspan></text>
5099</svg>"""
5100    mol = Chem.MolFromRDKitSVG(svg2)
5101    self.assertTrue(mol is None)
5102
5103    with self.assertRaises(RuntimeError):
5104      mol = Chem.MolFromRDKitSVG("bad svg")
5105
5106  def testAssignChiralTypesFromBondDirs(self):
5107    """
5108    Just check to see that AssignChiralTypesFromBondDirs is wrapped.
5109    Critical tests of the underlying C++ function already exist
5110    in SD file reader tests.
5111    """
5112    mol = Chem.MolFromSmiles('C(F)(Cl)Br')
5113    rdkit.Chem.rdDepictor.Compute2DCoords(mol)
5114    atom0 = mol.GetAtomWithIdx(0)
5115    self.assertEqual(atom0.GetChiralTag(), Chem.rdchem.ChiralType.CHI_UNSPECIFIED)
5116    bond = mol.GetBondBetweenAtoms(0, 1)
5117    bond.SetBondDir(Chem.rdchem.BondDir.BEGINWEDGE)
5118    Chem.AssignChiralTypesFromBondDirs(mol)
5119    self.assertEqual(atom0.GetChiralTag(), Chem.rdchem.ChiralType.CHI_TETRAHEDRAL_CCW)
5120
5121  def testAssignStereochemistryFrom3D(self):
5122
5123    def _stereoTester(mol, expectedCIP, expectedStereo):
5124      mol.UpdatePropertyCache()
5125      self.assertEqual(mol.GetNumAtoms(), 9)
5126      self.assertFalse(mol.GetAtomWithIdx(1).HasProp("_CIPCode"))
5127      self.assertEqual(mol.GetBondWithIdx(3).GetStereo(), Chem.BondStereo.STEREONONE)
5128      for bond in mol.GetBonds():
5129        bond.SetBondDir(Chem.BondDir.NONE)
5130      Chem.AssignStereochemistryFrom3D(mol)
5131      self.assertTrue(mol.GetAtomWithIdx(1).HasProp("_CIPCode"))
5132      self.assertEqual(mol.GetAtomWithIdx(1).GetProp("_CIPCode"), expectedCIP)
5133      self.assertEqual(mol.GetBondWithIdx(3).GetStereo(), expectedStereo)
5134
5135    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'test_data', 'stereochem.sdf')
5136    suppl = Chem.SDMolSupplier(fileN, sanitize=False)
5137    expected = (
5138      ("R", Chem.BondStereo.STEREOZ),
5139      ("R", Chem.BondStereo.STEREOE),
5140      ("S", Chem.BondStereo.STEREOZ),
5141      ("S", Chem.BondStereo.STEREOE),
5142    )
5143    for i, mol in enumerate(suppl):
5144      cip, stereo = expected[i]
5145      _stereoTester(mol, cip, stereo)
5146
5147  def testGitHub2082(self):
5148    ctab = """
5149  MJ150720
5150
5151  9  9  0  0  0  0  0  0  0  0999 V2000
5152    2.5687   -0.7144    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
5153    2.1562    0.0000    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
5154    2.5687    0.7144    0.0000 O   0  0  0  0  0  0  0  0  0  0  0  0
5155    1.3312    0.0000    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
5156    0.9187   -0.7144    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
5157    0.0937   -0.7144    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
5158   -0.3187    0.0000    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
5159    0.0937    0.7144    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
5160    0.9187    0.7144    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
5161  2  1  1  6
5162  2  3  1  0
5163  2  4  1  0
5164  4  5  2  0
5165  5  6  1  0
5166  6  7  2  0
5167  7  8  1  0
5168  8  9  2  0
5169  9  4  1  0
5170M  END
5171"""
5172    mol = Chem.MolFromMolBlock(ctab)
5173    self.assertFalse(mol.GetConformer().Is3D())
5174    self.assertTrue("@" in Chem.MolToSmiles(mol, True))
5175
5176  def testGitHub2082_2(self):
5177    # test a mol block that lies is 3D but labelled 2D
5178    ofile = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'Wrap', 'test_data',
5179                         'issue2082.mol')
5180    with open(ofile) as inf:
5181      ctab = inf.read()
5182    m = Chem.MolFromMolBlock(ctab)
5183    self.assertTrue(m.GetConformer().Is3D())
5184
5185  def testSetQuery(self):
5186    from rdkit.Chem import rdqueries
5187    pat = Chem.MolFromSmarts("[C]")
5188    self.assertFalse(Chem.MolFromSmiles("c1ccccc1").HasSubstructMatch(pat))
5189
5190    q = rdqueries.AtomNumEqualsQueryAtom(6)
5191    for atom in pat.GetAtoms():
5192      atom.SetQuery(q)
5193
5194    self.assertTrue(Chem.MolFromSmiles("c1ccccc1").HasSubstructMatch(pat))
5195
5196  def testBondSetQuery(self):
5197    pat = Chem.MolFromSmarts('[#6]=[#6]')
5198    mol = Chem.MolFromSmiles("c1ccccc1")
5199    self.assertFalse(mol.HasSubstructMatch(pat))
5200    pat2 = Chem.MolFromSmarts('C:C')
5201    for bond in pat.GetBonds():
5202      bond.SetQuery(pat2.GetBondWithIdx(0))
5203    self.assertTrue(mol.HasSubstructMatch(pat))
5204
5205  def testBondExpandQuery(self):
5206    pat = Chem.MolFromSmarts('C-C')
5207    mol = Chem.MolFromSmiles("C=C-C")
5208    self.assertEqual(len(mol.GetSubstructMatches(pat)), 1)
5209    pat2 = Chem.MolFromSmarts('C=C')
5210    for bond in pat.GetBonds():
5211      bond.ExpandQuery(pat2.GetBondWithIdx(0), Chem.CompositeQueryType.COMPOSITE_OR)
5212    self.assertEqual(len(mol.GetSubstructMatches(pat)), 2)
5213
5214  def testGitHub1985(self):
5215    # simple check, this used to throw an exception
5216    try:
5217      Chem.MolToSmarts(Chem.MolFromSmarts("[C@]"))
5218    except:
5219      self.fail("[C@] caused an exception when roundtripping smarts")
5220
5221  def testGetEnhancedStereo(self):
5222
5223    rdbase = os.environ['RDBASE']
5224    filename = os.path.join(rdbase, 'Code/GraphMol/FileParsers/test_data/two_centers_or.mol')
5225    m = Chem.MolFromMolFile(filename)
5226
5227    sg = m.GetStereoGroups()
5228    self.assertEqual(len(sg), 2)
5229    group1 = sg[1]
5230    self.assertEqual(group1.GetGroupType(), Chem.StereoGroupType.STEREO_OR)
5231    stereo_atoms = group1.GetAtoms()
5232    self.assertEqual(len(stereo_atoms), 2)
5233    # file is 1 indexed and says 5
5234    self.assertEqual(stereo_atoms[1].GetIdx(), 4)
5235
5236    # make sure the atoms are connected to the parent molecule
5237    stereo_atoms[1].SetProp("foo", "bar")
5238    self.assertTrue(m.GetAtomWithIdx(4).HasProp("foo"))
5239
5240    # make sure that we can iterate over the atoms:
5241    for at in stereo_atoms:
5242      at.SetProp("foo2", "bar2")
5243      self.assertTrue(m.GetAtomWithIdx(at.GetIdx()).HasProp("foo2"))
5244
5245  def testEnhancedStereoPreservesMol(self):
5246    """
5247    Check that the stereo group (and the atoms therein) preserve the lifetime
5248    of the associated mol.
5249    """
5250    rdbase = os.environ['RDBASE']
5251    filename = os.path.join(rdbase, 'Code/GraphMol/FileParsers/test_data/two_centers_or.mol')
5252    m = Chem.MolFromMolFile(filename)
5253
5254    sg = m.GetStereoGroups()
5255    m = None
5256    gc.collect()
5257    self.assertEqual(len(sg), 2)
5258    group1 = sg[1]
5259    stereo_atoms = group1.GetAtoms()
5260    sg = None
5261    gc.collect()
5262    self.assertEqual(stereo_atoms[1].GetIdx(), 4)
5263    self.assertEqual(stereo_atoms[1].GetOwningMol().GetNumAtoms(), 8)
5264
5265  def testSetEnhancedStereoGroup(self):
5266    m = Chem.MolFromSmiles('F[C@@H](Br)[C@H](F)Cl |o1:1|')
5267    m2 = Chem.RWMol(m)
5268
5269    groups = m2.GetStereoGroups()
5270    self.assertEqual(len(groups), 1)
5271    # Can clear the StereoGroups by setting to an empty list
5272    m2.SetStereoGroups([])
5273    self.assertEqual(len(m2.GetStereoGroups()), 0)
5274
5275    # Can add new StereoGroups
5276    group1 = Chem.rdchem.CreateStereoGroup(Chem.rdchem.StereoGroupType.STEREO_OR, m2, [1])
5277    m2.SetStereoGroups([group1])
5278    self.assertEqual(len(m2.GetStereoGroups()), 1)
5279
5280  def testSetEnhancedStereoGroupOwnershipCheck(self):
5281    # make sure that the object returned by CreateStereoGroup()
5282    # preserves the owning molecule:
5283    m = Chem.RWMol(Chem.MolFromSmiles('F[C@@H](Br)[C@H](F)Cl'))
5284    group1 = Chem.rdchem.CreateStereoGroup(Chem.rdchem.StereoGroupType.STEREO_OR, m, [1])
5285    m.SetStereoGroups([group1])
5286    self.assertEqual(len(m.GetStereoGroups()), 1)
5287
5288    m = None
5289    gc.collect()
5290    stereo_atoms = group1.GetAtoms()
5291    self.assertEqual(stereo_atoms[0].GetIdx(), 1)
5292    return
5293    self.assertEqual(stereo_atoms[0].GetOwningMol().GetNumAtoms(), 6)
5294
5295    # make sure we can't add StereoGroups constructed from one molecule
5296    # to a different one:
5297    m2 = Chem.RWMol(Chem.MolFromSmiles('F[C@@H](Br)[C@H](F)Cl'))
5298    with self.assertRaises(ValueError):
5299      m2.SetStereoGroups([group1])
5300
5301  def testSetEnhancedStereoTypeChecking(self):
5302    m = Chem.RWMol(Chem.MolFromSmiles('F[C@@H](Br)[C@H](F)Cl'))
5303
5304    # List or tuple should be allowed:
5305    group = Chem.rdchem.CreateStereoGroup(Chem.rdchem.StereoGroupType.STEREO_OR, m, [1, 3])
5306    group = Chem.rdchem.CreateStereoGroup(Chem.rdchem.StereoGroupType.STEREO_OR, m, (1, 3))
5307
5308    # Python ValueError (range error) with index past the end
5309    with self.assertRaises(ValueError):
5310      group = Chem.rdchem.CreateStereoGroup(Chem.rdchem.StereoGroupType.STEREO_OR, m, [100])
5311
5312    # Mol is None
5313    with self.assertRaises(TypeError):
5314      group = Chem.rdchem.CreateStereoGroup(Chem.rdchem.StereoGroupType.STEREO_OR, None, [1])
5315
5316    # Atom indices must be numbers
5317    with self.assertRaises(TypeError):
5318      group = Chem.rdchem.CreateStereoGroup(Chem.rdchem.StereoGroupType.STEREO_OR, m, [1, 'text'])
5319
5320  def testSubstructParameters(self):
5321    m = Chem.MolFromSmiles('C[C@](F)(Cl)OCC')
5322    p1 = Chem.MolFromSmiles('C[C@](F)(Cl)O')
5323    p2 = Chem.MolFromSmiles('C[C@@](F)(Cl)O')
5324    p3 = Chem.MolFromSmiles('CC(F)(Cl)O')
5325
5326    ps = Chem.SubstructMatchParameters()
5327    self.assertTrue(m.HasSubstructMatch(p1, ps))
5328    self.assertTrue(m.HasSubstructMatch(p2, ps))
5329    self.assertTrue(m.HasSubstructMatch(p3, ps))
5330    self.assertEqual(m.GetSubstructMatch(p1, ps), (0, 1, 2, 3, 4))
5331    self.assertEqual(m.GetSubstructMatch(p2, ps), (0, 1, 2, 3, 4))
5332    self.assertEqual(m.GetSubstructMatch(p3, ps), (0, 1, 2, 3, 4))
5333    self.assertEqual(m.GetSubstructMatches(p1, ps), ((0, 1, 2, 3, 4), ))
5334    self.assertEqual(m.GetSubstructMatches(p2, ps), ((0, 1, 2, 3, 4), ))
5335    self.assertEqual(m.GetSubstructMatches(p3, ps), ((0, 1, 2, 3, 4), ))
5336    ps.useChirality = True
5337    self.assertTrue(m.HasSubstructMatch(p1, ps))
5338    self.assertFalse(m.HasSubstructMatch(p2, ps))
5339    self.assertTrue(m.HasSubstructMatch(p3, ps))
5340    self.assertEqual(m.GetSubstructMatch(p1, ps), (0, 1, 2, 3, 4))
5341    self.assertEqual(m.GetSubstructMatch(p2, ps), ())
5342    self.assertEqual(m.GetSubstructMatch(p3, ps), (0, 1, 2, 3, 4))
5343    self.assertEqual(m.GetSubstructMatches(p1, ps), ((0, 1, 2, 3, 4), ))
5344    self.assertEqual(m.GetSubstructMatches(p2, ps), ())
5345    self.assertEqual(m.GetSubstructMatches(p3, ps), ((0, 1, 2, 3, 4), ))
5346
5347  def testSubstructParametersBundles(self):
5348    b = Chem.MolBundle()
5349    smis = ('C[C@](F)(Cl)O', 'C[C@](Br)(Cl)O', 'C[C@](I)(Cl)O')
5350    for smi in smis:
5351      b.AddMol(Chem.MolFromSmiles(smi))
5352    self.assertEqual(len(b), 3)
5353    self.assertEqual(b.Size(), 3)
5354    ps = Chem.SubstructMatchParameters()
5355    ps.useChirality = True
5356    self.assertTrue(Chem.MolFromSmiles('C[C@](F)(Cl)OCC').HasSubstructMatch(b, ps))
5357    self.assertFalse(Chem.MolFromSmiles('C[C@@](F)(Cl)OCC').HasSubstructMatch(b, ps))
5358    self.assertTrue(Chem.MolFromSmiles('C[C@](I)(Cl)OCC').HasSubstructMatch(b, ps))
5359    self.assertFalse(Chem.MolFromSmiles('C[C@@](I)(Cl)OCC').HasSubstructMatch(b, ps))
5360
5361    self.assertEqual(
5362      Chem.MolFromSmiles('C[C@](F)(Cl)OCC').GetSubstructMatch(b, ps), (0, 1, 2, 3, 4))
5363    self.assertEqual(Chem.MolFromSmiles('C[C@@](F)(Cl)OCC').GetSubstructMatch(b, ps), ())
5364    self.assertEqual(
5365      Chem.MolFromSmiles('C[C@](I)(Cl)OCC').GetSubstructMatch(b, ps), (0, 1, 2, 3, 4))
5366    self.assertEqual(Chem.MolFromSmiles('C[C@@](I)(Cl)OCC').GetSubstructMatch(b, ps), ())
5367
5368    self.assertEqual(
5369      Chem.MolFromSmiles('C[C@](F)(Cl)OCC').GetSubstructMatches(b, ps), ((0, 1, 2, 3, 4), ))
5370    self.assertEqual(Chem.MolFromSmiles('C[C@@](F)(Cl)OCC').GetSubstructMatches(b, ps), ())
5371    self.assertEqual(
5372      Chem.MolFromSmiles('C[C@](I)(Cl)OCC').GetSubstructMatches(b, ps), ((0, 1, 2, 3, 4), ))
5373    self.assertEqual(Chem.MolFromSmiles('C[C@@](I)(Cl)OCC').GetSubstructMatches(b, ps), ())
5374
5375  def testSubstructParametersBundles2(self):
5376    b = Chem.MolBundle()
5377    smis = ('C[C@](F)(Cl)O', 'C[C@](Br)(Cl)O', 'C[C@](I)(Cl)O')
5378    for smi in smis:
5379      b.AddMol(Chem.MolFromSmiles(smi))
5380    self.assertEqual(len(b), 3)
5381    b2 = Chem.MolBundle()
5382    smis = ('C[C@@](F)(Cl)O', 'C[C@@](Br)(Cl)O', 'C[C@@](I)(Cl)O')
5383    for smi in smis:
5384      b2.AddMol(Chem.MolFromSmiles(smi))
5385    self.assertEqual(len(b2), 3)
5386    ps = Chem.SubstructMatchParameters()
5387    ps.useChirality = True
5388    self.assertTrue(b.HasSubstructMatch(b, ps))
5389    self.assertFalse(b.HasSubstructMatch(b2, ps))
5390    self.assertFalse(b2.HasSubstructMatch(b, ps))
5391
5392    self.assertEqual(b.GetSubstructMatch(b, ps), (0, 1, 2, 3, 4))
5393    self.assertEqual(b.GetSubstructMatch(b2, ps), ())
5394    self.assertEqual(b2.GetSubstructMatch(b, ps), ())
5395
5396    self.assertEqual(b.GetSubstructMatches(b, ps), ((0, 1, 2, 3, 4), ))
5397    self.assertEqual(b.GetSubstructMatches(b2, ps), ())
5398    self.assertEqual(b2.GetSubstructMatches(b, ps), ())
5399
5400  def testGithub2285(self):
5401    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
5402                         'github2285.sdf')
5403
5404    supp = Chem.ForwardSDMolSupplier(fileN, removeHs=False)
5405    if hasattr(supp, "__next__"):
5406      self.assertTrue(supp.__next__() is not None)
5407    else:
5408      self.assertTrue(supp.next() is not None)
5409
5410  def testBitVectProp(self):
5411    bv = DataStructs.ExplicitBitVect(100)
5412    m = Chem.MolFromSmiles("CC")
5413    for atom in m.GetAtoms():
5414      bv.SetBit(atom.GetIdx())
5415      atom.SetExplicitBitVectProp("prop", bv)
5416
5417    for atom in m.GetAtoms():
5418      bv = atom.GetExplicitBitVectProp("prop")
5419      self.assertTrue(bv.GetBit(atom.GetIdx()))
5420
5421  def testBitVectQuery(self):
5422    bv = DataStructs.ExplicitBitVect(4)
5423    bv.SetBit(0)
5424    bv.SetBit(2)
5425
5426    # wow, what a mouthfull..
5427    qa = rdqueries.HasBitVectPropWithValueQueryAtom("prop", bv, tolerance=0.0)
5428
5429    m = Chem.MolFromSmiles("CC")
5430    for atom in m.GetAtoms():
5431      if atom.GetIdx() == 0:
5432        atom.SetExplicitBitVectProp("prop", bv)
5433
5434    l = tuple([x.GetIdx() for x in m.GetAtomsMatchingQuery(qa)])
5435    self.assertEqual(l, (0, ))
5436
5437    m = Chem.MolFromSmiles("CC")
5438    for atom in m.GetAtoms():
5439      bv = DataStructs.ExplicitBitVect(4)
5440      bv.SetBit(atom.GetIdx())
5441      atom.SetExplicitBitVectProp("prop", bv)
5442
5443    sma = Chem.MolFromSmarts("C")
5444    for atom in sma.GetAtoms():
5445      bv = DataStructs.ExplicitBitVect(4)
5446      bv.SetBit(1)
5447      qa = rdqueries.HasBitVectPropWithValueQueryAtom("prop", bv, tolerance=0.0)
5448      atom.ExpandQuery(qa)
5449
5450    res = m.GetSubstructMatches(sma)
5451    self.assertEqual(res, ((1, ), ))
5452
5453    sma = Chem.MolFromSmarts("C")
5454    for atom in sma.GetAtoms():
5455      bv = DataStructs.ExplicitBitVect(4)
5456      bv.SetBit(0)
5457      qa = rdqueries.HasBitVectPropWithValueQueryAtom("prop", bv, tolerance=0.0)
5458      atom.ExpandQuery(qa)
5459
5460    res = m.GetSubstructMatches(sma)
5461    self.assertEqual(res, ((0, ), ))
5462
5463    sma = Chem.MolFromSmarts("C")
5464    for atom in sma.GetAtoms():
5465      bv = DataStructs.ExplicitBitVect(4)
5466      bv.SetBit(0)
5467      qa = rdqueries.HasBitVectPropWithValueQueryAtom("prop", bv, tolerance=1.0)
5468      atom.ExpandQuery(qa)
5469
5470    res = m.GetSubstructMatches(sma)
5471    self.assertEqual(res, ((0, ), (1, )))
5472
5473  def testGithub2441(self):
5474    m = Chem.MolFromSmiles("CC")
5475    conf = Chem.Conformer(2)
5476    m.AddConformer(conf, assignId=False)
5477    m.GetConformer().SetIntProp("foo", 1)
5478    m.GetConformer().SetProp("bar", "foo")
5479    self.assertTrue(m.GetConformer().HasProp("foo"))
5480    self.assertFalse(m.GetConformer().HasProp("food"))
5481    d = m.GetConformer().GetPropsAsDict()
5482    self.assertTrue('foo' in d)
5483    self.assertTrue('bar' in d)
5484    self.assertEqual(d['bar'], 'foo')
5485    self.assertEqual(m.GetConformer().GetProp("bar"), "foo")
5486    self.assertEqual(m.GetConformer().GetIntProp("foo"), 1)
5487
5488  def testGithub2479(self):
5489    # Chemistry failure in last entry
5490    smi2 = '''c1ccccc  duff
5491c1ccccc1 ok
5492c1ccncc1 pyridine
5493C(C garbage
5494C1CC1 ok2
5495C1C(Cl)C1 ok3
5496CC(C)(C)(C)C duff2
5497'''
5498    suppl2 = Chem.SmilesMolSupplier()
5499    suppl2.SetData(smi2, titleLine=False, nameColumn=1)
5500    l = [x for x in suppl2]
5501    self.assertEqual(len(l), 7)
5502    self.assertTrue(l[6] is None)
5503
5504    # SMILES failure in last entry
5505    smi2 = '''c1ccccc  duff
5506c1ccccc1 ok
5507c1ccncc1 pyridine
5508C(C garbage
5509C1CC1 ok2
5510C1C(Cl)C1 ok3
5511C1C(Cl)CCCC duff2
5512'''
5513    suppl2 = Chem.SmilesMolSupplier()
5514    suppl2.SetData(smi2, titleLine=False, nameColumn=1)
5515    l = [x for x in suppl2]
5516    self.assertEqual(len(l), 7)
5517    self.assertTrue(l[6] is None)
5518
5519    sdf = b"""
5520  Mrv1810 06051911332D
5521
5522  3  2  0  0  0  0            999 V2000
5523  -13.3985    4.9850    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
5524  -12.7066    5.4343    0.0000 O   0  0  0  0  0  0  0  0  0  0  0  0
5525  -12.0654    4.9151    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
5526  1  2  1  0  0  0  0
5527  2  3  1  0  0  0  0
5528M  END
5529$$$$
5530
5531  Mrv1810 06051911332D
5532
5533  3  2  0  0  0  0            999 V2000
5534  -10.3083    4.8496    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
5535   -9.6408    5.3345    0.0000 F   0  0  0  0  0  0  0  0  0  0  0  0
5536   -9.0277    4.7825    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
5537  1  2  1  0  0  0  0
5538  2  3  1  0  0  0  0
5539M  END
5540$$$$
5541
5542  Mrv1810 06051911332D
5543
5544  3  2  0  0  0  0            999 V2000
5545  -10.3083    4.8496    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
5546   -9.6"""
5547    suppl3 = Chem.SDMolSupplier()
5548    suppl3.SetData(sdf)
5549    l = [x for x in suppl3]
5550    self.assertEqual(len(l), 3)
5551    self.assertTrue(l[1] is None)
5552    self.assertTrue(l[2] is None)
5553
5554    from io import BytesIO
5555    sio = BytesIO(sdf)
5556    suppl3 = Chem.ForwardSDMolSupplier(sio)
5557    l = [x for x in suppl3]
5558    self.assertEqual(len(l), 3)
5559    self.assertTrue(l[1] is None)
5560    self.assertTrue(l[2] is None)
5561
5562    sdf = b"""
5563  Mrv1810 06051911332D
5564
5565  3  2  0  0  0  0            999 V2000
5566  -13.3985    4.9850    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
5567  -12.7066    5.4343    0.0000 O   0  0  0  0  0  0  0  0  0  0  0  0
5568  -12.0654    4.9151    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
5569  1  2  1  0  0  0  0
5570  2  3  1  0  0  0  0
5571M  END
5572>  <pval>  (1)
5573[1,2,]
5574
5575$$$$
5576
5577  Mrv1810 06051911332D
5578
5579  3  2  0  0  0  0            999 V2000
5580  -10.3083    4.8496    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
5581   -9.6408    5.3345    0.0000 O   0  0  0  0  0  0  0  0  0  0  0  0
5582   -9.0277    4.7825    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
5583  1  2  1  0  0  0  0
5584  2  3  1  0  0  0  0
5585M  END
5586>  <pval>  (1)
5587[1,2,]
5588"""
5589    suppl3 = Chem.SDMolSupplier()
5590    suppl3.SetData(sdf)
5591    l = [x for x in suppl3]
5592    self.assertEqual(len(l), 2)
5593    self.assertTrue(l[0] is not None)
5594    self.assertTrue(l[1] is not None)
5595
5596    from io import BytesIO
5597    sio = BytesIO(sdf)
5598    suppl3 = Chem.ForwardSDMolSupplier(sio)
5599    l = [x for x in suppl3]
5600    self.assertEqual(len(l), 2)
5601    self.assertTrue(l[0] is not None)
5602    self.assertTrue(l[1] is not None)
5603
5604  def testXYZ(self):
5605    conf = Chem.Conformer(5)
5606    conf.SetAtomPosition(0, [0.000, 0.000, 0.000])
5607    conf.SetAtomPosition(1, [-0.635, -0.635, 0.635])
5608    conf.SetAtomPosition(2, [-0.635, 0.635, -0.635])
5609    conf.SetAtomPosition(3, [0.635, -0.635, -0.635])
5610    conf.SetAtomPosition(4, [0.635, 0.635, 0.635])
5611
5612    emol = Chem.EditableMol(Chem.Mol())
5613    for z in [6, 1, 1, 1, 1]:
5614      emol.AddAtom(Chem.Atom(z))
5615    mol = emol.GetMol()
5616    mol.SetProp('_Name', 'methane\nthis part should not be output')
5617    mol.AddConformer(conf)
5618
5619    xyzblock_expected = """5
5620methane
5621C      0.000000    0.000000    0.000000
5622H     -0.635000   -0.635000    0.635000
5623H     -0.635000    0.635000   -0.635000
5624H      0.635000   -0.635000   -0.635000
5625H      0.635000    0.635000    0.635000
5626"""
5627
5628    self.assertEqual(Chem.MolToXYZBlock(mol), xyzblock_expected)
5629
5630  def testSanitizationExceptionBasics(self):
5631    try:
5632      Chem.SanitizeMol(Chem.MolFromSmiles('CFC', sanitize=False))
5633    except Chem.AtomValenceException as exc:
5634      self.assertEqual(exc.cause.GetAtomIdx(), 1)
5635    else:
5636      self.assertFalse(True)
5637
5638    try:
5639      Chem.SanitizeMol(Chem.MolFromSmiles('c1cc1', sanitize=False))
5640    except Chem.KekulizeException as exc:
5641      self.assertEqual(exc.cause.GetAtomIndices(), (0, 1, 2))
5642    else:
5643      self.assertFalse(True)
5644
5645  def testSanitizationExceptionHierarchy(self):
5646    with self.assertRaises(Chem.AtomValenceException):
5647      Chem.SanitizeMol(Chem.MolFromSmiles('CFC', sanitize=False))
5648    with self.assertRaises(Chem.AtomSanitizeException):
5649      Chem.SanitizeMol(Chem.MolFromSmiles('CFC', sanitize=False))
5650    with self.assertRaises(Chem.MolSanitizeException):
5651      Chem.SanitizeMol(Chem.MolFromSmiles('CFC', sanitize=False))
5652    with self.assertRaises(ValueError):
5653      Chem.SanitizeMol(Chem.MolFromSmiles('CFC', sanitize=False))
5654
5655    with self.assertRaises(Chem.KekulizeException):
5656      Chem.SanitizeMol(Chem.MolFromSmiles('c1cc1', sanitize=False))
5657    with self.assertRaises(Chem.MolSanitizeException):
5658      Chem.SanitizeMol(Chem.MolFromSmiles('c1cc1', sanitize=False))
5659    with self.assertRaises(ValueError):
5660      Chem.SanitizeMol(Chem.MolFromSmiles('c1cc1', sanitize=False))
5661
5662  def testNoExceptionSmilesParserParams(self):
5663    """
5664    MolFromSmiles should catch exceptions even when SmilesParserParams
5665    is provided.
5666    """
5667    smiles_params = Chem.SmilesParserParams()
5668    mol = Chem.MolFromSmiles("C1CC", smiles_params)
5669    self.assertIsNone(mol)
5670
5671  def testDetectChemistryProblems(self):
5672    m = Chem.MolFromSmiles('CFCc1cc1FC', sanitize=False)
5673    ps = Chem.DetectChemistryProblems(m)
5674    self.assertEqual(len(ps), 3)
5675    self.assertEqual([x.GetType() for x in ps],
5676                     ['AtomValenceException', 'AtomValenceException', 'KekulizeException'])
5677    self.assertEqual(ps[0].GetAtomIdx(), 1)
5678    self.assertEqual(ps[1].GetAtomIdx(), 6)
5679    self.assertEqual(ps[2].GetAtomIndices(), (3, 4, 5))
5680
5681  def testGithub2611(self):
5682    mol = Chem.MolFromSmiles('ONCS.ONCS')
5683    for atom in mol.GetAtoms():
5684      atom.SetIsotope(atom.GetIdx())
5685
5686    order1 = list(
5687      Chem.CanonicalRankAtomsInFragment(mol, atomsToUse=range(0, 4), breakTies=False,
5688                                        includeIsotopes=True))
5689    order2 = list(
5690      Chem.CanonicalRankAtomsInFragment(mol, atomsToUse=range(0, 8), breakTies=False,
5691                                        includeIsotopes=False))
5692    self.assertNotEqual(order1[:4], order2[4:])
5693    # ensure that the orders are ignored in the second batch
5694    self.assertEqual(order2[:4], order2[4:])
5695
5696    for smi in ['ONCS.ONCS', 'F[C@@H](Br)[C@H](F)Cl']:
5697      mol = Chem.MolFromSmiles(smi)
5698      for atom in mol.GetAtoms():
5699        atom.SetIsotope(atom.GetIdx())
5700
5701        for iso, chiral in [(True, True), (True, False), (False, True), (False, False)]:
5702          order1 = list(
5703            Chem.CanonicalRankAtomsInFragment(mol, atomsToUse=range(0, mol.GetNumAtoms()),
5704                                              bondsToUse=range(0,
5705                                                               mol.GetNumBonds()), breakTies=False,
5706                                              includeIsotopes=iso, includeChirality=chiral))
5707          order2 = list(
5708            Chem.CanonicalRankAtomsInFragment(mol, atomsToUse=range(0, mol.GetNumAtoms()),
5709                                              bondsToUse=range(0,
5710                                                               mol.GetNumBonds()), breakTies=True,
5711                                              includeIsotopes=iso, includeChirality=chiral))
5712          order3 = list(
5713            Chem.CanonicalRankAtoms(mol, breakTies=False, includeIsotopes=iso,
5714                                    includeChirality=chiral))
5715          order4 = list(
5716            Chem.CanonicalRankAtoms(mol, breakTies=True, includeIsotopes=iso,
5717                                    includeChirality=chiral))
5718          self.assertEqual(order1, order3)
5719          self.assertEqual(order2, order4)
5720
5721  def testSetBondStereoFromDirections(self):
5722    m1 = Chem.MolFromMolBlock(
5723      '''
5724  Mrv1810 10141909482D
5725
5726  4  3  0  0  0  0            999 V2000
5727    3.3412   -2.9968    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
5728    2.5162   -2.9968    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
5729    2.1037   -3.7112    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
5730    3.7537   -2.2823    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
5731  1  2  2  0  0  0  0
5732  2  3  1  0  0  0  0
5733  1  4  1  0  0  0  0
5734M  END
5735''', sanitize=False)
5736    self.assertEqual(m1.GetBondBetweenAtoms(0, 1).GetBondType(), Chem.BondType.DOUBLE)
5737    self.assertEqual(m1.GetBondBetweenAtoms(0, 1).GetStereo(), Chem.BondStereo.STEREONONE)
5738    Chem.SetBondStereoFromDirections(m1)
5739    self.assertEqual(m1.GetBondBetweenAtoms(0, 1).GetStereo(), Chem.BondStereo.STEREOTRANS)
5740
5741    m2 = Chem.MolFromMolBlock(
5742      '''
5743  Mrv1810 10141909542D
5744
5745  4  3  0  0  0  0            999 V2000
5746    3.4745   -5.2424    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
5747    2.6495   -5.2424    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
5748    2.2370   -5.9569    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
5749    3.8870   -5.9569    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
5750  1  2  2  0  0  0  0
5751  2  3  1  0  0  0  0
5752  1  4  1  0  0  0  0
5753M  END
5754''', sanitize=False)
5755    self.assertEqual(m2.GetBondBetweenAtoms(0, 1).GetBondType(), Chem.BondType.DOUBLE)
5756    self.assertEqual(m2.GetBondBetweenAtoms(0, 1).GetStereo(), Chem.BondStereo.STEREONONE)
5757    Chem.SetBondStereoFromDirections(m2)
5758    self.assertEqual(m2.GetBondBetweenAtoms(0, 1).GetStereo(), Chem.BondStereo.STEREOCIS)
5759
5760  def testSetBondDirFromStereo(self):
5761    m1 = Chem.MolFromSmiles('CC=CC')
5762    m1.GetBondWithIdx(1).SetStereoAtoms(0, 3)
5763    m1.GetBondWithIdx(1).SetStereo(Chem.BondStereo.STEREOCIS)
5764    Chem.SetDoubleBondNeighborDirections(m1)
5765    self.assertEqual(Chem.MolToSmiles(m1), r"C/C=C\C")
5766    self.assertEqual(m1.GetBondWithIdx(0).GetBondDir(), Chem.BondDir.ENDUPRIGHT)
5767    self.assertEqual(m1.GetBondWithIdx(2).GetBondDir(), Chem.BondDir.ENDDOWNRIGHT)
5768
5769  def testAssignChiralTypesFromMolParity(self):
5770
5771    class TestAssignChiralTypesFromMolParity:
5772
5773      class BondDef:
5774
5775        def __init__(self, bi, ei, t):
5776          self.beginIdx = bi
5777          self.endIdx = ei
5778          self.type = t
5779
5780      def __init__(self, mol, parent):
5781        self.parent = parent
5782        self.parityMap = {
5783          Chem.ChiralType.CHI_TETRAHEDRAL_CW: 1,
5784          Chem.ChiralType.CHI_TETRAHEDRAL_CCW: 2,
5785          Chem.ChiralType.CHI_UNSPECIFIED: 0,
5786          Chem.ChiralType.CHI_OTHER: 0
5787        }
5788        self.d_rwMol = Chem.RWMol(mol)
5789        self.assignMolParity()
5790        self.fillBondDefVect()
5791        Chem.AssignAtomChiralTagsFromMolParity(self.d_rwMol)
5792        self.d_refSmiles = Chem.MolToSmiles(self.d_rwMol)
5793        self.heapPermutation()
5794
5795      def assignMolParity(self):
5796        Chem.AssignAtomChiralTagsFromStructure(self.d_rwMol)
5797        for a in self.d_rwMol.GetAtoms():
5798          parity = self.parityMap[a.GetChiralTag()]
5799          a.SetIntProp("molParity", parity)
5800          a.SetChiralTag(Chem.ChiralType.CHI_UNSPECIFIED)
5801
5802      def fillBondDefVect(self):
5803        self.d_bondDefVect = [
5804          self.BondDef(b.GetBeginAtomIdx(), b.GetEndAtomIdx(), b.GetBondType())
5805          for b in self.d_rwMol.GetBonds()
5806        ]
5807
5808      def stripBonds(self):
5809        for i in reversed(range(self.d_rwMol.GetNumBonds())):
5810          b = self.d_rwMol.GetBondWithIdx(i)
5811          self.d_rwMol.RemoveBond(b.GetBeginAtomIdx(), b.GetEndAtomIdx())
5812
5813      def addBonds(self):
5814        [
5815          self.d_rwMol.AddBond(bondDef.beginIdx, bondDef.endIdx, bondDef.type)
5816          for bondDef in self.d_bondDefVect
5817        ]
5818
5819      def checkBondPermutation(self):
5820        self.stripBonds()
5821        self.addBonds()
5822        Chem.SanitizeMol(self.d_rwMol)
5823        Chem.AssignAtomChiralTagsFromMolParity(self.d_rwMol)
5824        self.parent.assertEqual(Chem.MolToSmiles(self.d_rwMol), self.d_refSmiles)
5825
5826      def heapPermutation(self, s=0):
5827        # if size becomes 1 the permutation is ready to use
5828        if (s == 0):
5829          s = len(self.d_bondDefVect)
5830        if (s == 1):
5831          self.checkBondPermutation()
5832          return
5833        for i in range(s):
5834          self.heapPermutation(s - 1)
5835          # if size is odd, swap first and last element
5836          j = 0 if (s % 2 == 1) else i
5837          self.d_bondDefVect[j], self.d_bondDefVect[s - 1] = \
5838            self.d_bondDefVect[s - 1], self.d_bondDefVect[j]
5839
5840    molb = """
5841     RDKit          3D
5842
5843  6  5  0  0  1  0  0  0  0  0999 V2000
5844   -2.9747    1.7234    0.0753 C   0  0  0  0  0  0  0  0  0  0  0  0
5845   -1.4586    1.4435    0.1253 C   0  0  0  0  0  0  0  0  0  0  0  0
5846   -3.5885    2.6215    1.4893 Cl  0  0  0  0  0  0  0  0  0  0  0  0
5847   -3.7306    0.3885   -0.0148 C   0  0  0  0  0  0  0  0  0  0  0  0
5848   -0.3395    3.0471    0.1580 Br  0  0  0  0  0  0  0  0  0  0  0  0
5849   -1.1574    0.7125    1.2684 F   0  0  0  0  0  0  0  0  0  0  0  0
5850  1  2  1  0
5851  1  3  1  0
5852  1  4  1  0
5853  2  5  1  0
5854  2  6  1  0
5855M  END
5856"""
5857    m = Chem.RWMol(Chem.MolFromMolBlock(molb, sanitize=True, removeHs=False))
5858    self.assertIsNotNone(m)
5859    TestAssignChiralTypesFromMolParity(m, self)
5860
5861  def testCXSMILESErrors(self):
5862    smi = "CCC |FAILURE|"
5863    ps = Chem.SmilesParserParams()
5864    ps.strictCXSMILES = False
5865    m = Chem.MolFromSmiles(smi, ps)
5866    self.assertTrue(m is not None)
5867    self.assertEqual(m.GetNumAtoms(), 3)
5868
5869  def testRemoveHsParams(self):
5870    smips = Chem.SmilesParserParams()
5871    smips.removeHs = False
5872
5873    m = Chem.MolFromSmiles('F.[H]', smips)
5874    ps = Chem.RemoveHsParameters()
5875    m = Chem.RemoveHs(m, ps)
5876    self.assertEqual(m.GetNumAtoms(), 2)
5877    ps.removeDegreeZero = True
5878    m = Chem.RemoveHs(m, ps)
5879    self.assertEqual(m.GetNumAtoms(), 1)
5880
5881    m = Chem.MolFromSmiles('F[H-]F', smips)
5882    ps = Chem.RemoveHsParameters()
5883    m = Chem.RemoveHs(m, ps)
5884    self.assertEqual(m.GetNumAtoms(), 3)
5885    m = Chem.MolFromSmiles('F[H-]F', smips)
5886    ps.removeHigherDegrees = True
5887    m = Chem.RemoveHs(m, ps)
5888    self.assertEqual(m.GetNumAtoms(), 2)
5889
5890    m = Chem.MolFromSmiles('[H][H]', smips)
5891    ps = Chem.RemoveHsParameters()
5892    m = Chem.RemoveHs(m, ps)
5893    self.assertEqual(m.GetNumAtoms(), 2)
5894    m = Chem.MolFromSmiles('[H][H]', smips)
5895    ps.removeOnlyHNeighbors = True
5896    m = Chem.RemoveHs(m, ps)
5897    self.assertEqual(m.GetNumAtoms(), 0)
5898
5899    m = Chem.MolFromSmiles('F[2H]', smips)
5900    ps = Chem.RemoveHsParameters()
5901    m = Chem.RemoveHs(m, ps)
5902    self.assertEqual(m.GetNumAtoms(), 2)
5903    m = Chem.MolFromSmiles('F[2H]', smips)
5904    ps.removeIsotopes = True
5905    m = Chem.RemoveHs(m, ps)
5906    self.assertEqual(m.GetNumAtoms(), 1)
5907
5908    m = Chem.MolFromSmiles('c1c(C([2H])([2H])F)cccc1', smips)
5909    ps = Chem.RemoveHsParameters()
5910    m_noh = Chem.RemoveHs(m, ps)
5911    self.assertEqual(m_noh.GetNumAtoms(), m.GetNumAtoms())
5912    ps.removeAndTrackIsotopes = True
5913    m_noh = Chem.RemoveHs(m, ps)
5914    self.assertEqual(m_noh.GetNumAtoms(), m.GetNumAtoms() - 2)
5915    self.assertTrue(m_noh.GetAtomWithIdx(2).HasProp("_isotopicHs"))
5916    self.assertEqual(tuple(m_noh.GetAtomWithIdx(2).GetPropsAsDict().get("_isotopicHs")), (2, 2))
5917    m_h = Chem.AddHs(m_noh)
5918    self.assertFalse(m_h.GetAtomWithIdx(2).HasProp("_isotopicHs"))
5919    self.assertEqual(
5920      sum([
5921        1 for nbr in m_h.GetAtomWithIdx(2).GetNeighbors()
5922        if (nbr.GetAtomicNum() == 1 and nbr.GetIsotope())
5923      ]), 2)
5924
5925    m = Chem.MolFromSmiles('*[H]', smips)
5926    ps = Chem.RemoveHsParameters()
5927    m = Chem.RemoveHs(m, ps)
5928    self.assertEqual(m.GetNumAtoms(), 2)
5929    m = Chem.MolFromSmiles('*[H]', smips)
5930    ps.removeDummyNeighbors = True
5931    m = Chem.RemoveHs(m, ps)
5932    self.assertEqual(m.GetNumAtoms(), 1)
5933
5934    m = Chem.MolFromSmiles('F/C=N/[H]', smips)
5935    ps = Chem.RemoveHsParameters()
5936    m = Chem.RemoveHs(m, ps)
5937    self.assertEqual(m.GetNumAtoms(), 4)
5938    m = Chem.MolFromSmiles('F/C=N/[H]', smips)
5939    ps.removeDefiningBondStereo = True
5940    m = Chem.RemoveHs(m, ps)
5941    self.assertEqual(m.GetNumAtoms(), 3)
5942
5943    m = Chem.MolFromSmiles('FC([H])(O)Cl', smips)
5944    m.GetBondBetweenAtoms(1, 2).SetBondDir(Chem.BondDir.BEGINWEDGE)
5945    ps = Chem.RemoveHsParameters()
5946    m = Chem.RemoveHs(m, ps)
5947    self.assertEqual(m.GetNumAtoms(), 4)
5948    m = Chem.MolFromSmiles('FC([H])(O)Cl', smips)
5949    m.GetBondBetweenAtoms(1, 2).SetBondDir(Chem.BondDir.BEGINWEDGE)
5950    ps.removeWithWedgedBond = False
5951    m = Chem.RemoveHs(m, ps)
5952    self.assertEqual(m.GetNumAtoms(), 5)
5953
5954    m = Chem.MolFromSmarts('F[#1]')
5955    ps = Chem.RemoveHsParameters()
5956    m = Chem.RemoveHs(m, ps)
5957    self.assertEqual(m.GetNumAtoms(), 2)
5958    m = Chem.MolFromSmarts('F[#1]')
5959    ps.removeWithQuery = True
5960    m = Chem.RemoveHs(m, ps)
5961    self.assertEqual(m.GetNumAtoms(), 1)
5962
5963    m = Chem.MolFromSmiles('[C@]12([H])CCC1CO2.[H+].F[H-]F.[H][H].[H]*.F/C=C/[H]')
5964    m = Chem.RemoveAllHs(m)
5965    for at in m.GetAtoms():
5966      self.assertNotEqual(at.GetAtomicNum(), 1)
5967
5968  def testPickleCoordsAsDouble(self):
5969    import pickle
5970    m = Chem.MolFromSmiles('C')
5971    test_num = 1e50
5972    conf = Chem.Conformer(1)
5973    conf.SetAtomPosition(0, (test_num, 0.0, 0.0))
5974    m.AddConformer(conf)
5975
5976    opts = Chem.GetDefaultPickleProperties()
5977    Chem.SetDefaultPickleProperties(Chem.PropertyPickleOptions.NoProps)
5978    self.assertNotEqual(pickle.loads(pickle.dumps(m)).GetConformer().GetAtomPosition(0).x, test_num)
5979
5980    try:
5981      Chem.SetDefaultPickleProperties(Chem.PropertyPickleOptions.CoordsAsDouble)
5982      self.assertEqual(pickle.loads(pickle.dumps(m)).GetConformer().GetAtomPosition(0).x, test_num)
5983    finally:
5984      Chem.SetDefaultPickleProperties(opts)
5985
5986  def testCustomSubstructMatchCheck(self):
5987
5988    def accept_none(mol, vect):
5989      return False
5990
5991    def accept_all(mol, vect):
5992      return True
5993
5994    def accept_large(mol, vect):
5995      return sum(vect) > 5
5996
5997    m = Chem.MolFromSmiles('CCOCC')
5998    p = Chem.MolFromSmiles('CCO')
5999    ps = Chem.SubstructMatchParameters()
6000    self.assertEqual(len(m.GetSubstructMatches(p, ps)), 2)
6001
6002    ps.setExtraFinalCheck(accept_none)
6003    self.assertEqual(len(m.GetSubstructMatches(p, ps)), 0)
6004    ps.setExtraFinalCheck(accept_all)
6005    self.assertEqual(len(m.GetSubstructMatches(p, ps)), 2)
6006    ps.setExtraFinalCheck(accept_large)
6007    self.assertEqual(len(m.GetSubstructMatches(p, ps)), 1)
6008
6009  def testMostSubstitutedCoreMatch(self):
6010    core = Chem.MolFromSmarts("[*:1]c1cc([*:2])ccc1[*:3]")
6011    orthoMeta = Chem.MolFromSmiles("c1ccc(-c2ccc(-c3ccccc3)c(-c3ccccc3)c2)cc1")
6012    ortho = Chem.MolFromSmiles("c1ccc(-c2ccccc2-c2ccccc2)cc1")
6013    meta = Chem.MolFromSmiles("c1ccc(-c2cccc(-c3ccccc3)c2)cc1")
6014    biphenyl = Chem.MolFromSmiles("c1ccccc1-c1ccccc1")
6015    phenyl = Chem.MolFromSmiles("c1ccccc1")
6016
6017    def numHsMatchingDummies(mol, core, match):
6018      return sum([
6019        1 for qi, ai in enumerate(match) if core.GetAtomWithIdx(qi).GetAtomicNum() == 0
6020        and mol.GetAtomWithIdx(ai).GetAtomicNum() == 1
6021      ])
6022
6023    for mol, res in ((orthoMeta, 0), (ortho, 1), (meta, 1), (biphenyl, 2), (phenyl, 3)):
6024      mol = Chem.AddHs(mol)
6025      matches = mol.GetSubstructMatches(core)
6026      bestMatch = Chem.GetMostSubstitutedCoreMatch(mol, core, matches)
6027      self.assertEqual(numHsMatchingDummies(mol, core, bestMatch), res)
6028      ctrlCounts = sorted([numHsMatchingDummies(mol, core, match) for match in matches])
6029      sortedCounts = [
6030        numHsMatchingDummies(mol, core, match)
6031        for match in Chem.SortMatchesByDegreeOfCoreSubstitution(mol, core, matches)
6032      ]
6033      self.assertEqual(len(ctrlCounts), len(sortedCounts))
6034      self.assertTrue(all(ctrl == sortedCounts[i] for i, ctrl in enumerate(ctrlCounts)))
6035    with self.assertRaises(ValueError):
6036      Chem.GetMostSubstitutedCoreMatch(orthoMeta, core, [])
6037    with self.assertRaises(ValueError):
6038      Chem.SortMatchesByDegreeOfCoreSubstitution(orthoMeta, core, [])
6039
6040  def testSetCoordsTerminalAtom(self):
6041    mol = Chem.MolFromMolBlock("""
6042     RDKit          2D
6043
6044  6  6  0  0  0  0  0  0  0  0999 V2000
6045    1.5000    0.0000    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
6046    0.7500   -1.2990    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
6047   -0.7500   -1.2990    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
6048   -1.5000    0.0000    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
6049   -0.7500    1.2990    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
6050    0.7500    1.2990    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
6051  1  2  2  0
6052  2  3  1  0
6053  3  4  2  0
6054  4  5  1  0
6055  5  6  2  0
6056  6  1  1  0
6057M  END
6058""")
6059    mol = Chem.RWMol(mol)
6060    atom = Chem.Atom(0)
6061    idx = mol.AddAtom(atom)
6062    mol.AddBond(idx, 0)
6063    Chem.SetTerminalAtomCoords(mol, idx, 0)
6064    coord = mol.GetConformer().GetAtomPosition(idx)
6065    self.assertAlmostEqual(coord.x, 2.5, 2)
6066    self.assertAlmostEqual(coord.y, 0, 2)
6067    self.assertAlmostEqual(coord.z, 0, 2)
6068
6069  def testSuppliersReadingDirectories(self):
6070    # this is an odd one, basically we need to check that we don't hang
6071    #  which is pretty much a bad test in my opinion, but YMMV
6072    d = tempfile.mkdtemp()
6073    self.assertTrue(os.path.exists(d))
6074
6075    for supplier in [
6076        Chem.SmilesMolSupplier,
6077        Chem.SDMolSupplier,
6078        Chem.TDTMolSupplier,
6079        #Chem.CompressedSDMolSupplier,
6080    ]:
6081      print("supplier:", supplier)
6082      with self.assertRaises(OSError):
6083        suppl = supplier(d)
6084    if hasattr(Chem, 'MaeMolSupplier'):
6085      with self.assertRaises(OSError):
6086        suppl = Chem.MaeMolSupplier(d)
6087    os.rmdir(d)
6088
6089  def testRandomSmilesVect(self):
6090    m = Chem.MolFromSmiles("C1OCC1N(CO)(Cc1ccccc1NCCl)")
6091    v = Chem.MolToRandomSmilesVect(m, 5, randomSeed=0xf00d)
6092    self.assertEqual(v, [
6093      "c1cc(CN(C2COC2)CO)c(cc1)NCCl", "N(CCl)c1c(CN(C2COC2)CO)cccc1", "N(CCl)c1ccccc1CN(C1COC1)CO",
6094      "OCN(Cc1ccccc1NCCl)C1COC1", "C(N(C1COC1)Cc1c(cccc1)NCCl)O"
6095    ])
6096
6097    v = Chem.MolToRandomSmilesVect(m, 5, randomSeed=0xf00d, allHsExplicit=True)
6098    self.assertEqual(v, [
6099      "[cH]1[cH][c]([CH2][N]([CH]2[CH2][O][CH2]2)[CH2][OH])[c]([cH][cH]1)[NH][CH2][Cl]",
6100      "[NH]([CH2][Cl])[c]1[c]([CH2][N]([CH]2[CH2][O][CH2]2)[CH2][OH])[cH][cH][cH][cH]1",
6101      "[NH]([CH2][Cl])[c]1[cH][cH][cH][cH][c]1[CH2][N]([CH]1[CH2][O][CH2]1)[CH2][OH]",
6102      "[OH][CH2][N]([CH2][c]1[cH][cH][cH][cH][c]1[NH][CH2][Cl])[CH]1[CH2][O][CH2]1",
6103      "[CH2]([N]([CH]1[CH2][O][CH2]1)[CH2][c]1[c]([cH][cH][cH][cH]1)[NH][CH2][Cl])[OH]"
6104    ])
6105
6106  def testGithub3198(self):
6107
6108    ps = Chem.SmilesParserParams()
6109    ps.removeHs = False
6110    m = Chem.MolFromSmiles('[H]OC(F).[Na]', ps)
6111
6112    qa = rdqueries.AAtomQueryAtom()
6113    self.assertEqual(tuple(x.GetIdx() for x in m.GetAtomsMatchingQuery(qa)), (1, 2, 3, 4))
6114
6115    qa = rdqueries.AHAtomQueryAtom()
6116    self.assertEqual(tuple(x.GetIdx() for x in m.GetAtomsMatchingQuery(qa)), (0, 1, 2, 3, 4))
6117
6118    qa = rdqueries.QAtomQueryAtom()
6119    self.assertEqual(tuple(x.GetIdx() for x in m.GetAtomsMatchingQuery(qa)), (1, 3, 4))
6120
6121    qa = rdqueries.QHAtomQueryAtom()
6122    self.assertEqual(tuple(x.GetIdx() for x in m.GetAtomsMatchingQuery(qa)), (0, 1, 3, 4))
6123
6124    qa = rdqueries.XAtomQueryAtom()
6125    self.assertEqual(tuple(x.GetIdx() for x in m.GetAtomsMatchingQuery(qa)), (3, ))
6126
6127    qa = rdqueries.XHAtomQueryAtom()
6128    self.assertEqual(tuple(x.GetIdx() for x in m.GetAtomsMatchingQuery(qa)), (0, 3))
6129
6130    qa = rdqueries.MAtomQueryAtom()
6131    self.assertEqual(tuple(x.GetIdx() for x in m.GetAtomsMatchingQuery(qa)), (4, ))
6132
6133    qa = rdqueries.MHAtomQueryAtom()
6134    self.assertEqual(tuple(x.GetIdx() for x in m.GetAtomsMatchingQuery(qa)), (0, 4))
6135
6136  def testAdjustQueryPropertiesFiveRings(self):
6137    mol = Chem.MolFromSmiles('c1cc[nH]c1')
6138    nops = Chem.AdjustQueryParameters.NoAdjustments()
6139    nmol = Chem.AdjustQueryProperties(mol, nops)
6140    self.assertEqual(Chem.MolToSmarts(nmol), "[#6]1:[#6]:[#6]:[#7H]:[#6]:1")
6141
6142    nops.adjustConjugatedFiveRings = True
6143    nmol = Chem.AdjustQueryProperties(mol, nops)
6144    self.assertEqual(Chem.MolToSmarts(nmol), "[#6]1-,=,:[#6]-,=,:[#6]-,=,:[#7H]-,=,:[#6]-,=,:1")
6145
6146  def testFindPotentialStereo(self):
6147    mol = Chem.MolFromSmiles('C[C@H](F)C=CC')
6148    si = Chem.FindPotentialStereo(mol)
6149    self.assertEqual(len(si), 2)
6150    self.assertEqual(si[0].type, Chem.StereoType.Atom_Tetrahedral)
6151    self.assertEqual(si[0].specified, Chem.StereoSpecified.Specified)
6152    self.assertEqual(si[0].centeredOn, 1)
6153    self.assertEqual(si[0].descriptor, Chem.StereoDescriptor.Tet_CCW)
6154    self.assertEqual(list(si[0].controllingAtoms), [0, 2, 3])
6155    self.assertEqual(si[1].type, Chem.StereoType.Bond_Double)
6156    self.assertEqual(si[1].specified, Chem.StereoSpecified.Unspecified)
6157    self.assertEqual(si[1].centeredOn, 3)
6158    self.assertEqual(si[1].descriptor, Chem.StereoDescriptor.NoValue)
6159    self.assertEqual(list(si[1].controllingAtoms),
6160                     [1, Chem.StereoInfo.NOATOM, 5, Chem.StereoInfo.NOATOM])
6161
6162  def testNewFindMolChiralCenters(self):
6163    mol = Chem.MolFromSmiles('C[C@H](F)C=CC(F)Cl')
6164    ctrs = Chem.FindMolChiralCenters(mol, useLegacyImplementation=False)
6165    self.assertEqual(len(ctrs), 1)
6166    self.assertEqual(ctrs, [(1, 'S')])
6167    ctrs = Chem.FindMolChiralCenters(mol, useLegacyImplementation=False, includeCIP=False)
6168    self.assertEqual(len(ctrs), 1)
6169    self.assertEqual(ctrs, [(1, 'Tet_CCW')])
6170    ctrs = Chem.FindMolChiralCenters(mol, useLegacyImplementation=False, includeUnassigned=True,
6171                                     includeCIP=False)
6172    self.assertEqual(len(ctrs), 2)
6173    self.assertEqual(ctrs, [(1, 'Tet_CCW'), (5, '?')])
6174    ctrs = Chem.FindMolChiralCenters(mol, useLegacyImplementation=False, includeUnassigned=True,
6175                                     includeCIP=True)
6176    self.assertEqual(len(ctrs), 2)
6177    self.assertEqual(ctrs, [(1, 'S'), (5, '?')])
6178
6179  def testMolFromPNG(self):
6180    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
6181                         'colchicine.png')
6182    mol = Chem.MolFromPNGFile(fileN)
6183    self.assertIsNotNone(mol)
6184    self.assertEqual(mol.GetNumAtoms(), 29)
6185
6186    with open(fileN, 'rb') as inf:
6187      d = inf.read()
6188    mol = Chem.MolFromPNGString(d)
6189    self.assertIsNotNone(mol)
6190    self.assertEqual(mol.GetNumAtoms(), 29)
6191
6192  def testMolToPNG(self):
6193    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
6194                         'colchicine.no_metadata.png')
6195
6196    with open(fileN, 'rb') as inf:
6197      d = inf.read()
6198    mol = Chem.MolFromSmiles("COc1cc2c(c(OC)c1OC)-c1ccc(OC)c(=O)cc1[C@@H](NC(C)=O)CC2")
6199    self.assertIsNotNone(mol)
6200    self.assertEqual(mol.GetNumAtoms(), 29)
6201
6202    nd = Chem.MolMetadataToPNGString(mol, d)
6203    mol = Chem.MolFromPNGString(nd)
6204    self.assertIsNotNone(mol)
6205    self.assertEqual(mol.GetNumAtoms(), 29)
6206
6207    nd = Chem.MolMetadataToPNGFile(mol, fileN)
6208    mol = Chem.MolFromPNGString(nd)
6209    self.assertIsNotNone(mol)
6210    self.assertEqual(mol.GetNumAtoms(), 29)
6211
6212  def testMolsFromPNG(self):
6213    refMols = [Chem.MolFromSmiles(x) for x in ('c1ccccc1', 'CCO', 'CC(=O)O', 'c1ccccn1')]
6214    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
6215                         'multiple_mols.png')
6216    mols = Chem.MolsFromPNGFile(fileN)
6217    self.assertEqual(len(mols), len(refMols))
6218    for mol, refMol in zip(mols, refMols):
6219      self.assertEqual(Chem.MolToSmiles(mol), Chem.MolToSmiles(refMol))
6220
6221  def testMetadataToPNG(self):
6222    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
6223                         'colchicine.png')
6224
6225    with open(fileN, 'rb') as inf:
6226      d = inf.read()
6227    mol = Chem.MolFromPNGString(d)
6228    nd = Chem.MolMetadataToPNGString(mol, d)
6229    vals = {'foo': '1', 'bar': '2'}
6230    nd = Chem.AddMetadataToPNGString(vals, nd)
6231    nvals = Chem.MetadataFromPNGString(nd)
6232    self.assertTrue('foo' in nvals)
6233    self.assertEqual(nvals['foo'], b'1')
6234    self.assertTrue('bar' in nvals)
6235    self.assertEqual(nvals['bar'], b'2')
6236
6237    nd = Chem.AddMetadataToPNGFile(vals, fileN)
6238    nvals = Chem.MetadataFromPNGString(nd)
6239    self.assertTrue('foo' in nvals)
6240    self.assertEqual(nvals['foo'], b'1')
6241    self.assertTrue('bar' in nvals)
6242    self.assertEqual(nvals['bar'], b'2')
6243
6244    vals = {'foo': 1, 'bar': '2'}
6245    with self.assertRaises(TypeError):
6246      nd = Chem.AddMetadataToPNGString(vals, d)
6247
6248  def test_github3403(self):
6249    core1 = "[$(C-!@[a])](=O)(Cl)"
6250    sma = Chem.MolFromSmarts(core1)
6251
6252    m = Chem.MolFromSmiles("c1ccccc1C(=O)Cl")
6253    self.assertFalse(m.HasSubstructMatch(sma, recursionPossible=False))
6254
6255    m = Chem.MolFromSmiles("c1ccccc1C(=O)Cl")
6256    self.assertTrue(m.HasSubstructMatch(sma))
6257
6258    m = Chem.MolFromSmiles("c1ccccc1C(=O)Cl")
6259    self.assertFalse(m.HasSubstructMatch(sma, recursionPossible=False))
6260
6261  def test_github3517(self):
6262    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
6263                         'NCI_aids_few.sdf')
6264    sdSup = Chem.SDMolSupplier(fileN)
6265    mols_list = list(sdSup)
6266    mols_list_compr = [m for m in sdSup]
6267    self.assertEqual(len(mols_list), len(mols_list_compr))
6268
6269  def test_github3492(self):
6270
6271    def read_smile(s):
6272      m = Chem.MolFromSmiles(s)
6273      rdkit.Chem.rdDepictor.Compute2DCoords(m)
6274      return m
6275
6276    def sq_dist(a, b):
6277      ab = [a[i] - b[i] for i, _ in enumerate(a)]
6278      return sum([d * d for d in ab])
6279
6280    self.assertIsNotNone(Chem.MolFromSmiles("OCCN").GetAtoms()[0].GetOwningMol())
6281    self.assertEqual([Chem.MolFromSmiles("OCCN").GetAtoms()[i].GetAtomicNum() for i in range(4)],
6282                     [8, 6, 6, 7])
6283    self.assertIsNotNone(Chem.MolFromSmiles("O=CCC=N").GetBonds()[0].GetOwningMol())
6284    self.assertEqual(
6285      [Chem.MolFromSmiles("O=CCC=N").GetBonds()[i].GetBondType() for i in range(4)],
6286      [Chem.BondType.DOUBLE, Chem.BondType.SINGLE, Chem.BondType.SINGLE, Chem.BondType.DOUBLE])
6287    self.assertIsNotNone(read_smile("CCC").GetConformers()[0].GetOwningMol())
6288    pos = read_smile("CCC").GetConformers()[0].GetPositions()
6289    self.assertAlmostEqual(sq_dist(pos[0], pos[1]), sq_dist(pos[1], pos[2]))
6290
6291  def test_github3553(self):
6292    from io import StringIO
6293    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'Wrap', 'test_data',
6294                         'github3553.sdf')
6295    sdSup = Chem.SDMolSupplier(fileN)
6296    for mol in sdSup:
6297      pval = mol.GetProp('boiling.point.predicted')
6298      sio = StringIO()
6299      w = Chem.SDWriter(sio)
6300      w.SetKekulize(True)
6301      w.SetForceV3000(True)
6302      w.write(mol)
6303      w.flush()
6304      txt = sio.getvalue()
6305      self.assertTrue(pval in txt)
6306
6307  def test_github1631(self):
6308    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
6309                         '1CRN.pdb')
6310
6311    m = Chem.MolFromPDBFile(fileN)
6312    info = m.GetAtomWithIdx(0).GetPDBResidueInfo()
6313    self.assertEqual(info.GetName(), " N  ")
6314    self.assertEqual(info.GetResidueName(), "THR")
6315    self.assertAlmostEqual(info.GetTempFactor(), 13.79, 2)
6316
6317    m2 = Chem.MolFromSmiles('CC(C(C(=O)O)N)O')
6318    self.assertTrue(m2.GetAtomWithIdx(6).GetPDBResidueInfo() is None)
6319    m2.GetAtomWithIdx(6).SetPDBResidueInfo(info)
6320    info2 = m2.GetAtomWithIdx(6).GetPDBResidueInfo()
6321    self.assertEqual(info2.GetName(), " N  ")
6322    self.assertEqual(info2.GetResidueName(), "THR")
6323    self.assertAlmostEqual(info2.GetTempFactor(), 13.79, 2)
6324
6325  def testMolzip(self):
6326    tests = [["C[*:1]", "N[*:1]", "CN", Chem.MolzipParams()]]
6327    for a, b, res, params in tests:
6328      self.assertEqual(
6329        Chem.CanonSmiles(res),
6330        Chem.MolToSmiles(Chem.molzip(Chem.MolFromSmiles(a), Chem.MolFromSmiles(b), params)))
6331
6332    # multiple arg test
6333    a = Chem.MolFromSmiles('C=C[1*]')
6334    b = Chem.MolFromSmiles('O/C=N/[1*]')
6335    p = Chem.MolzipParams()
6336    p.label = Chem.MolzipLabel.Isotope
6337    c = Chem.molzip(a, b, p)
6338    self.assertEqual(Chem.MolToSmiles(c), 'C=C/N=C/O')
6339
6340    # single argument test
6341    a = Chem.MolFromSmiles('C=C[1*].O/C=N/[1*]')
6342    p = Chem.MolzipParams()
6343    p.label = Chem.MolzipLabel.Isotope
6344    c = Chem.molzip(a, p)
6345    self.assertEqual(Chem.MolToSmiles(c), 'C=C/N=C/O')
6346
6347  def testContextManagers(self):
6348    from rdkit import RDLogger
6349    RDLogger.DisableLog('rdApp.*')
6350    from io import StringIO
6351    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'Wrap', 'test_data',
6352                         'github3553.sdf')
6353    with Chem.SDMolSupplier(fileN) as suppl:
6354      mols = [x for x in suppl if x is not None]
6355    sio = StringIO()
6356    with Chem.SDWriter(sio) as w:
6357      for m in mols:
6358        w.write(m)
6359    txt = sio.getvalue()
6360    self.assertEqual(txt.count('$$$$'), len(mols))
6361    with self.assertRaises(RuntimeError):
6362      w.write(mols[0])
6363
6364    with Chem.ForwardSDMolSupplier(fileN) as suppl:
6365      mols = [x for x in suppl if x is not None]
6366
6367    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
6368                         'first_200.tpsa.csv')
6369    with Chem.SmilesMolSupplier(fileN, ",", 0, -1) as suppl:
6370      ms = [x for x in suppl if x is not None]
6371
6372    sio = StringIO()
6373    with Chem.SmilesWriter(sio) as w:
6374      for m in mols:
6375        w.write(m)
6376    txt = sio.getvalue()
6377    self.assertEqual(txt.count('\n'), len(mols) + 1)
6378    with self.assertRaises(RuntimeError):
6379      w.write(mols[0])
6380
6381    data = """$SMI<Cc1nnc(N)nc1C>
6382CAS<17584-12-2>
6383|
6384$SMI<Cc1n[nH]c(=O)nc1N>
6385CAS<~>
6386|
6387$SMI<Cc1n[nH]c(=O)[nH]c1=O>
6388CAS<932-53-6>
6389|
6390$SMI<Cc1nnc(NN)nc1O>
6391CAS<~>
6392|"""
6393    with Chem.TDTMolSupplier() as suppl:
6394      suppl.SetData(data, "CAS")
6395      ms = [x for x in suppl if x is not None]
6396    sio = StringIO()
6397    with Chem.TDTWriter(sio) as w:
6398      for m in mols:
6399        w.write(m)
6400    txt = sio.getvalue()
6401    self.assertEqual(txt.count('$SMI'), len(mols))
6402    with self.assertRaises(RuntimeError):
6403      w.write(mols[0])
6404
6405    fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
6406                         '1CRN.pdb')
6407    m = Chem.MolFromPDBFile(fileN)
6408    mols = [m, m]
6409    sio = StringIO()
6410    with Chem.PDBWriter(sio) as w:
6411      for m in mols:
6412        w.write(m)
6413    txt = sio.getvalue()
6414    self.assertEqual(txt.count('COMPND    CRAMBIN'), len(mols))
6415    with self.assertRaises(RuntimeError):
6416      w.write(mols[0])
6417
6418    if hasattr(Chem, 'MaeMolSupplier'):
6419      fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
6420                           'NCI_aids_few.mae')
6421      with Chem.MaeMolSupplier(fileN) as suppl:
6422        ms = [x for x in suppl if x is not None]
6423
6424    RDLogger.EnableLog('rdApp.*')
6425
6426  def testInsertMol(self):
6427    m = Chem.MolFromSmiles("CNO")
6428    m2 = Chem.MolFromSmiles("c1ccccc1")
6429    m3 = Chem.MolFromSmiles("C1CC1")
6430
6431    rwmol = Chem.RWMol(m)
6432    rwmol.InsertMol(m2)
6433    rwmol.InsertMol(m3)
6434    self.assertEqual(Chem.MolToSmiles(rwmol),
6435                     Chem.CanonSmiles("CNO.c1ccccc1.C1CC1"))
6436
6437  def testBatchEdits(self):
6438    mol = Chem.MolFromSmiles("C1CCCO1")
6439
6440    for rwmol in [Chem.EditableMol(mol), Chem.RWMol(mol)]:
6441      rwmol.BeginBatchEdit()
6442      rwmol.RemoveAtom(2)
6443      rwmol.RemoveAtom(3)
6444      rwmol.CommitBatchEdit()
6445      nmol = rwmol.GetMol()
6446      self.assertEqual(Chem.MolToSmiles(nmol), "CCO")
6447
6448    for rwmol in [Chem.EditableMol(mol), Chem.RWMol(mol)]:
6449      rwmol = Chem.EditableMol(mol)
6450      rwmol.BeginBatchEdit()
6451      rwmol.RemoveAtom(3)
6452      rwmol.RemoveBond(4, 0)
6453      rwmol.CommitBatchEdit()
6454      nmol = rwmol.GetMol()
6455      self.assertEqual(Chem.MolToSmiles(nmol), "CCC.O")
6456
6457    for rwmol in [Chem.EditableMol(mol), Chem.RWMol(mol)]:
6458      rwmol.BeginBatchEdit()
6459      rwmol.RemoveAtom(2)
6460      rwmol.RemoveAtom(3)
6461      rwmol.RollbackBatchEdit()
6462      nmol = rwmol.GetMol()
6463      self.assertEqual(Chem.MolToSmiles(nmol), "C1CCOC1")
6464
6465  def testBatchEditContextManager(self):
6466    mol = Chem.MolFromSmiles("C1CCCO1")
6467    with Chem.RWMol(mol) as rwmol:
6468      rwmol.RemoveAtom(2)
6469      rwmol.RemoveAtom(3)
6470      # make sure we haven't actually changed anything yet:
6471      self.assertEqual(rwmol.GetNumAtoms(), mol.GetNumAtoms())
6472    self.assertEqual(rwmol.GetNumAtoms(), mol.GetNumAtoms() - 2)
6473
6474    # make sure no changes get made if we throw an exception
6475    try:
6476      with Chem.RWMol(mol) as rwmol:
6477        rwmol.RemoveAtom(2)
6478        rwmol.RemoveAtom(6)
6479    except:
6480      pass
6481    self.assertEqual(rwmol.GetNumAtoms(), mol.GetNumAtoms())
6482
6483  def testGithub4138(self):
6484    m = Chem.MolFromSmiles('C1CCCO1')
6485    q = Chem.MolFromSmarts('')
6486    self.assertFalse(m.HasSubstructMatch(q))
6487    self.assertEqual(m.GetSubstructMatch(q), ())
6488    self.assertEqual(m.GetSubstructMatches(q), ())
6489
6490    m = Chem.MolFromSmiles('')
6491    q = Chem.MolFromSmarts('C')
6492    self.assertFalse(m.HasSubstructMatch(q))
6493    self.assertEqual(m.GetSubstructMatch(q), ())
6494    self.assertEqual(m.GetSubstructMatches(q), ())
6495
6496  def testGithub4144(self):
6497    ''' the underlying problem with #4144 was that the
6498    includeRings argument could not be passed to ClearComputedProps()
6499    from Python. Make sure that's fixed
6500    '''
6501    m = Chem.MolFromSmiles('c1ccccc1')
6502    self.assertEqual(m.GetRingInfo().NumRings(), 1)
6503    m.ClearComputedProps(includeRings=False)
6504    self.assertEqual(m.GetRingInfo().NumRings(), 1)
6505    m.ClearComputedProps()
6506    with self.assertRaises(RuntimeError):
6507      m.GetRingInfo().NumRings()
6508
6509
6510if __name__ == '__main__':
6511  if "RDTESTCASE" in os.environ:
6512    suite = unittest.TestSuite()
6513    testcases = os.environ["RDTESTCASE"]
6514    for name in testcases.split(':'):
6515      suite.addTest(TestCase(name))
6516
6517    runner = unittest.TextTestRunner()
6518    runner.run(suite)
6519  else:
6520    unittest.main()
6521