* Fixes #3553

* add another test

* Apply suggestions from code review

Co-authored-by: Paolo Tosco <paolo.tosco.mail@gmail.com>

* add an additional test for that

Co-authored-by: Paolo Tosco <paolo.tosco.mail@gmail.com>
This commit is contained in:
Greg Landrum
2020-11-09 14:06:14 +01:00
committed by GitHub
parent 5f4d12b33b
commit 1c2e3f1923
3 changed files with 285 additions and 28 deletions

View File

@@ -5917,11 +5917,14 @@ M END
self.assertEqual(m_noh.GetNumAtoms(), m.GetNumAtoms() - 2)
self.assertTrue(m_noh.GetAtomWithIdx(2).HasProp("_isotopicHs"))
self.assertEqual(tuple(map(int,
m_noh.GetAtomWithIdx(2).GetProp("_isotopicHs").split())), (2,2))
m_noh.GetAtomWithIdx(2).GetProp("_isotopicHs").split())), (2, 2))
m_h = Chem.AddHs(m_noh)
self.assertFalse(m_h.GetAtomWithIdx(2).HasProp("_isotopicHs"))
self.assertEqual(sum([1 for nbr in m_h.GetAtomWithIdx(2).GetNeighbors()
if (nbr.GetAtomicNum() == 1 and nbr.GetIsotope())]), 2)
self.assertEqual(
sum([
1 for nbr in m_h.GetAtomWithIdx(2).GetNeighbors()
if (nbr.GetAtomicNum() == 1 and nbr.GetIsotope())
]), 2)
m = Chem.MolFromSmiles('*[H]', smips)
ps = Chem.RemoveHsParameters()
@@ -6166,36 +6169,36 @@ M END
with open(fileN, 'rb') as inf:
d = inf.read()
mol = Chem.MolFromPNGString(d)
nd = Chem.MolMetadataToPNGString(mol,d)
vals = {'foo':'1','bar':'2'}
nd = Chem.MolMetadataToPNGString(mol, d)
vals = {'foo': '1', 'bar': '2'}
nd = Chem.AddMetadataToPNGString(vals, nd)
nvals = Chem.MetadataFromPNGString(nd)
self.assertTrue('foo' in nvals)
self.assertEqual(nvals['foo'],b'1')
self.assertEqual(nvals['foo'], b'1')
self.assertTrue('bar' in nvals)
self.assertEqual(nvals['bar'],b'2')
self.assertEqual(nvals['bar'], b'2')
nd = Chem.AddMetadataToPNGFile(vals, fileN)
nvals = Chem.MetadataFromPNGString(nd)
self.assertTrue('foo' in nvals)
self.assertEqual(nvals['foo'],b'1')
self.assertEqual(nvals['foo'], b'1')
self.assertTrue('bar' in nvals)
self.assertEqual(nvals['bar'],b'2')
self.assertEqual(nvals['bar'], b'2')
vals = {'foo':1,'bar':'2'}
vals = {'foo': 1, 'bar': '2'}
with self.assertRaises(TypeError):
nd = Chem.AddMetadataToPNGString(vals,d)
nd = Chem.AddMetadataToPNGString(vals, d)
def test_github3403(self):
core1 = "[$(C-!@[a])](=O)(Cl)"
sma = Chem.MolFromSmarts(core1)
m = Chem.MolFromSmiles("c1ccccc1C(=O)Cl")
self.assertFalse(m.HasSubstructMatch(sma, recursionPossible=False))
m = Chem.MolFromSmiles("c1ccccc1C(=O)Cl")
self.assertTrue(m.HasSubstructMatch(sma))
m = Chem.MolFromSmiles("c1ccccc1C(=O)Cl")
self.assertFalse(m.HasSubstructMatch(sma, recursionPossible=False))
@@ -6208,24 +6211,44 @@ M END
self.assertEqual(len(mols_list), len(mols_list_compr))
def test_github3492(self):
def read_smile(s):
m = Chem.MolFromSmiles(s)
rdkit.Chem.rdDepictor.Compute2DCoords(m)
return m
m = Chem.MolFromSmiles(s)
rdkit.Chem.rdDepictor.Compute2DCoords(m)
return m
def sq_dist(a, b):
ab = [a[i] - b[i] for i, _ in enumerate(a)]
return sum([d * d for d in ab])
self.assertIsNotNone(Chem.MolFromSmiles("OCCN").GetAtoms()[0].GetOwningMol())
self.assertEqual([Chem.MolFromSmiles("OCCN").GetAtoms()[i].GetAtomicNum() for i in range(4)], [8, 6, 6, 7])
self.assertEqual([Chem.MolFromSmiles("OCCN").GetAtoms()[i].GetAtomicNum() for i in range(4)],
[8, 6, 6, 7])
self.assertIsNotNone(Chem.MolFromSmiles("O=CCC=N").GetBonds()[0].GetOwningMol())
self.assertEqual([Chem.MolFromSmiles("O=CCC=N").GetBonds()[i].GetBondType() for i in range(4)],
[Chem.BondType.DOUBLE, Chem.BondType.SINGLE, Chem.BondType.SINGLE, Chem.BondType.DOUBLE])
self.assertEqual(
[Chem.MolFromSmiles("O=CCC=N").GetBonds()[i].GetBondType() for i in range(4)],
[Chem.BondType.DOUBLE, Chem.BondType.SINGLE, Chem.BondType.SINGLE, Chem.BondType.DOUBLE])
self.assertIsNotNone(read_smile("CCC").GetConformers()[0].GetOwningMol())
pos = read_smile("CCC").GetConformers()[0].GetPositions()
self.assertAlmostEqual(sq_dist(pos[0], pos[1]), sq_dist(pos[1], pos[2]))
def test_github3553(self):
from io import StringIO
fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'Wrap', 'test_data',
'github3553.sdf')
sdSup = Chem.SDMolSupplier(fileN)
for mol in sdSup:
pval = mol.GetProp('boiling.point.predicted')
sio = StringIO()
w = Chem.SDWriter(sio)
w.SetKekulize(True)
w.SetForceV3000(True)
w.write(mol)
w.flush()
txt = sio.getvalue()
self.assertTrue(pval in txt)
if __name__ == '__main__':
if "RDTESTCASE" in os.environ:
suite = unittest.TestSuite()

View File

@@ -0,0 +1,208 @@
some padding that is just the ri
RDKit 2D
0 0 0 0 0 0 0 0 0 0999 V3000
M V30 BEGIN CTAB
M V30 COUNTS 12 12 0 0 0
M V30 BEGIN ATOM
M V30 1 C -1.08313 -0.7909 0 0
M V30 2 O -1.08393 -1.7909 0 0
M V30 3 O -1.94873 -0.2901 0 0
M V30 4 Cl -1.08153 1.2091 0 0
M V30 5 Cl 0.648067 -1.7921 0 0
M V30 6 N 0.651267 2.2079 0 0
M V30 7 C -0.216733 -0.2915 0 0
M V30 8 C -0.215933 0.7085 0 0
M V30 9 C 0.648867 -0.7921 0 0
M V30 10 C 0.650467 1.2079 0 0
M V30 11 C 1.51527 -0.2929 0 0
M V30 12 C 1.51607 0.7071 0 0
M V30 END ATOM
M V30 BEGIN BOND
M V30 1 1 1 2
M V30 2 2 1 3
M V30 3 1 1 7
M V30 4 1 4 8
M V30 5 1 5 9
M V30 6 1 6 10
M V30 7 2 7 8
M V30 8 1 7 9
M V30 9 1 8 10
M V30 10 2 9 11
M V30 11 2 10 12
M V30 12 1 11 12
M V30 END BOND
M V30 END CTAB
M END
> <cas.rn> (1)
paddingpad
> <cas.index.name> (1)
paddingpaddingpaddingpaddingpadding
> <molecular.formula> (1)
C7H5Cl2NO2
> <molecular.weight> (1)
206.03
> <boiling.point.predicted> (1)
123.4±56.7 𐍈C paddingpaddingp
$$$$
some padding that is just the ri
RDKit 2D
0 0 0 0 0 0 0 0 0 0999 V3000
M V30 BEGIN CTAB
M V30 COUNTS 12 12 0 0 0
M V30 BEGIN ATOM
M V30 1 C -1.08313 -0.7909 0 0
M V30 2 O -1.08393 -1.7909 0 0
M V30 3 O -1.94873 -0.2901 0 0
M V30 4 Cl -1.08153 1.2091 0 0
M V30 5 Cl 0.648067 -1.7921 0 0
M V30 6 N 0.651267 2.2079 0 0
M V30 7 C -0.216733 -0.2915 0 0
M V30 8 C -0.215933 0.7085 0 0
M V30 9 C 0.648867 -0.7921 0 0
M V30 10 C 0.650467 1.2079 0 0
M V30 11 C 1.51527 -0.2929 0 0
M V30 12 C 1.51607 0.7071 0 0
M V30 END ATOM
M V30 BEGIN BOND
M V30 1 1 1 2
M V30 2 2 1 3
M V30 3 1 1 7
M V30 4 1 4 8
M V30 5 1 5 9
M V30 6 1 6 10
M V30 7 2 7 8
M V30 8 1 7 9
M V30 9 1 8 10
M V30 10 2 9 11
M V30 11 2 10 12
M V30 12 1 11 12
M V30 END BOND
M V30 END CTAB
M END
> <cas.rn> (1)
paddingpad
> <cas.index.name> (1)
paddingpaddingpaddingpaddingpadding
> <molecular.formula> (1)
C7H5Cl2NO2
> <molecular.weight> (1)
206.03
> <boiling.point.predicted> (1)
123.4±56.7 €C paddingpaddingp
$$$$
some padding that is just the ri
RDKit 2D
0 0 0 0 0 0 0 0 0 0999 V3000
M V30 BEGIN CTAB
M V30 COUNTS 12 12 0 0 0
M V30 BEGIN ATOM
M V30 1 C -1.08313 -0.7909 0 0
M V30 2 O -1.08393 -1.7909 0 0
M V30 3 O -1.94873 -0.2901 0 0
M V30 4 Cl -1.08153 1.2091 0 0
M V30 5 Cl 0.648067 -1.7921 0 0
M V30 6 N 0.651267 2.2079 0 0
M V30 7 C -0.216733 -0.2915 0 0
M V30 8 C -0.215933 0.7085 0 0
M V30 9 C 0.648867 -0.7921 0 0
M V30 10 C 0.650467 1.2079 0 0
M V30 11 C 1.51527 -0.2929 0 0
M V30 12 C 1.51607 0.7071 0 0
M V30 END ATOM
M V30 BEGIN BOND
M V30 1 1 1 2
M V30 2 2 1 3
M V30 3 1 1 7
M V30 4 1 4 8
M V30 5 1 5 9
M V30 6 1 6 10
M V30 7 2 7 8
M V30 8 1 7 9
M V30 9 1 8 10
M V30 10 2 9 11
M V30 11 2 10 12
M V30 12 1 11 12
M V30 END BOND
M V30 END CTAB
M END
> <cas.rn> (1)
paddingpad
> <cas.index.name> (1)
paddingpaddingpaddingpaddingpadding
> <molecular.formula> (1)
C7H5Cl2NO2
> <molecular.weight> (1)
206.03
> <boiling.point.predicted> (1)
123.4±56.7 °C paddingpaddingp
$$$$
some padding that is just the ri
RDKit 2D
0 0 0 0 0 0 0 0 0 0999 V3000
M V30 BEGIN CTAB
M V30 COUNTS 12 12 0 0 0
M V30 BEGIN ATOM
M V30 1 C -1.08313 -0.7909 0 0
M V30 2 O -1.08393 -1.7909 0 0
M V30 3 O -1.94873 -0.2901 0 0
M V30 4 Cl -1.08153 1.2091 0 0
M V30 5 Cl 0.648067 -1.7921 0 0
M V30 6 N 0.651267 2.2079 0 0
M V30 7 C -0.216733 -0.2915 0 0
M V30 8 C -0.215933 0.7085 0 0
M V30 9 C 0.648867 -0.7921 0 0
M V30 10 C 0.650467 1.2079 0 0
M V30 11 C 1.51527 -0.2929 0 0
M V30 12 C 1.51607 0.7071 0 0
M V30 END ATOM
M V30 BEGIN BOND
M V30 1 1 1 2
M V30 2 2 1 3
M V30 3 1 1 7
M V30 4 1 4 8
M V30 5 1 5 9
M V30 6 1 6 10
M V30 7 2 7 8
M V30 8 1 7 9
M V30 9 1 8 10
M V30 10 2 9 11
M V30 11 2 10 12
M V30 12 1 11 12
M V30 END BOND
M V30 END CTAB
M END
> <cas.rn> (1)
paddingpad
> <cas.index.name> (1)
paddingpaddingpaddingpaddingpadding
> <molecular.formula> (1)
C7H5Cl2NO2
> <molecular.weight> (1)
206.03
> <boiling.point.predicted> (1)
123.4±5°°°°°C paddingpaddingp
$$$$

View File

@@ -211,17 +211,17 @@ class streambuf : public std::basic_streambuf<char> {
CHECK_INVARIANT(iobase, "base class not found");
#endif
bool isTextMode = PyObject_IsInstance(python_file_obj.ptr(), iobase.ptr());
df_isTextMode = PyObject_IsInstance(python_file_obj.ptr(), iobase.ptr());
switch (mode) {
case 's': /// yeah, is redundant, but it is somehow natural to do "s"
case 't':
if (!isTextMode)
if (!df_isTextMode)
throw ValueErrorException(
"Need a text mode file object like StringIO or a file opened "
"with mode 't'");
break;
case 'b':
if (isTextMode)
if (df_isTextMode)
throw ValueErrorException(
"Need a binary mode file object like BytesIO or a file opened "
"with mode 'b'");
@@ -280,17 +280,42 @@ class streambuf : public std::basic_streambuf<char> {
}
farthest_pptr = std::max(farthest_pptr, pptr());
off_type n_written = (off_type)(farthest_pptr - pbase());
bp::str chunk(pbase(), farthest_pptr);
off_type orig_n_written = n_written;
const unsigned int STD_ASCII = 0x7F;
if (df_isTextMode && c > STD_ASCII) {
// we're somewhere in the middle of a utf8 block. If we
// only write part of it we'll end up with an exception,
// so push everything that could be utf8 into the next block
while (n_written > 0 &&
static_cast<unsigned int>(write_buffer[n_written - 1]) > STD_ASCII) {
--n_written;
}
}
bp::str chunk(pbase(), pbase() + n_written);
py_write(chunk);
if (!traits_type::eq_int_type(c, traits_type::eof())) {
if ((!df_isTextMode || c <= STD_ASCII) &&
!traits_type::eq_int_type(c, traits_type::eof())) {
py_write(traits_type::to_char_type(c));
n_written++;
}
setp(pbase(), epptr());
// ^^^ 27.5.2.4.5 (5)
farthest_pptr = pptr();
if (n_written) {
pos_of_write_buffer_end_in_py_file += n_written;
setp(pbase(), epptr());
// ^^^ 27.5.2.4.5 (5)
farthest_pptr = pptr();
if (df_isTextMode && c > STD_ASCII &&
!traits_type::eq_int_type(c, traits_type::eof())) {
size_t n_to_copy = orig_n_written - n_written;
for (size_t i = 0; i < n_to_copy; ++i) {
sputc(write_buffer[n_written + i]);
++farthest_pptr;
}
sputc(c);
++farthest_pptr;
}
}
return traits_type::eq_int_type(c, traits_type::eof())
? traits_type::not_eof(c)
@@ -405,6 +430,7 @@ class streambuf : public std::basic_streambuf<char> {
de-allocated only at destruction time.
*/
char* write_buffer;
bool df_isTextMode;
off_type pos_of_read_buffer_end_in_py_file,
pos_of_write_buffer_end_in_py_file;