diff --git a/Code/GraphMol/Wrap/Atom.cpp b/Code/GraphMol/Wrap/Atom.cpp index 32ffcb73e..1cba918a3 100644 --- a/Code/GraphMol/Wrap/Atom.cpp +++ b/Code/GraphMol/Wrap/Atom.cpp @@ -445,14 +445,14 @@ struct atom_wrapper { (python::arg("self"), python::arg("includePrivate") = true, python::arg("includeComputed") = true, python::arg("autoConvertStrings") = true), - "Returns a dictionary of the properties set on the Atom.\n" - " n.b. some properties cannot be converted to python types.\n") + getPropsAsDictDocString.c_str()) .def("UpdatePropertyCache", &Atom::updatePropertyCache, (python::arg("self"), python::arg("strict") = true), - "Regenerates computed properties like implicit valence and ring " + "Regenerates computed properties like implicit valence and ring " "information.\n\n") + .def("NeedsUpdatePropertyCache", &Atom::needsUpdatePropertyCache, (python::arg("self")), "Returns true or false depending on whether implicit and explicit " diff --git a/Code/GraphMol/Wrap/Bond.cpp b/Code/GraphMol/Wrap/Bond.cpp index e4a4c4ab0..0863ca407 100644 --- a/Code/GraphMol/Wrap/Bond.cpp +++ b/Code/GraphMol/Wrap/Bond.cpp @@ -301,9 +301,7 @@ struct bond_wrapper { (python::arg("self"), python::arg("includePrivate") = true, python::arg("includeComputed") = true, python::arg("autoConvertStrings") = true), - "Returns a dictionary of the properties set on the Bond.\n" - " n.b. some properties cannot be converted to python types.\n") - + getPropsAsDictDocString.c_str()) ; python::enum_("BondType") diff --git a/Code/GraphMol/Wrap/Conformer.cpp b/Code/GraphMol/Wrap/Conformer.cpp index d1b7e3185..5d939d321 100644 --- a/Code/GraphMol/Wrap/Conformer.cpp +++ b/Code/GraphMol/Wrap/Conformer.cpp @@ -294,17 +294,7 @@ struct conformer_wrapper { (python::arg("self"), python::arg("includePrivate") = false, python::arg("includeComputed") = false, python::arg("autoConvertStrings") = true), - "Returns a dictionary populated with the conformer's properties.\n" - " n.b. Some properties are not able to be converted to python " - "types.\n\n" - " ARGUMENTS:\n" - " - includePrivate: (optional) toggles inclusion of private " - "properties in the result set.\n" - " Defaults to False.\n" - " - includeComputed: (optional) toggles inclusion of computed " - "properties in the result set.\n" - " Defaults to False.\n\n" - " RETURNS: a dictionary\n"); + getPropsAsDictDocString.c_str()); }; }; } // namespace RDKit diff --git a/Code/GraphMol/Wrap/Mol.cpp b/Code/GraphMol/Wrap/Mol.cpp index e34109caa..bf45f45ae 100644 --- a/Code/GraphMol/Wrap/Mol.cpp +++ b/Code/GraphMol/Wrap/Mol.cpp @@ -797,17 +797,7 @@ struct mol_wrapper { (python::arg("self"), python::arg("includePrivate") = false, python::arg("includeComputed") = false, python::arg("autoConvertStrings") = true), - "Returns a dictionary populated with the molecules properties.\n" - " n.b. Some properties are not able to be converted to python " - "types.\n\n" - " ARGUMENTS:\n" - " - includePrivate: (optional) toggles inclusion of private " - "properties in the result set.\n" - " Defaults to False.\n" - " - includeComputed: (optional) toggles inclusion of computed " - "properties in the result set.\n" - " Defaults to False.\n\n" - " RETURNS: a dictionary\n") + getPropsAsDictDocString.c_str()) .def("GetAromaticAtoms", MolGetAromaticAtoms, python::return_value_policy< diff --git a/Code/GraphMol/Wrap/props.hpp b/Code/GraphMol/Wrap/props.hpp index b14117537..17afdc604 100644 --- a/Code/GraphMol/Wrap/props.hpp +++ b/Code/GraphMol/Wrap/props.hpp @@ -36,6 +36,7 @@ #include #include #include +#include namespace RDKit { @@ -75,6 +76,20 @@ bool AddToDict(const U &ob, boost::python::dict &dict, const std::string &key) { return true; } +const std::string getPropsAsDictDocString = "Returns a dictionary populated with the conformer's properties.\n" + "When possible, string values will be trimmed and converted to integers and doubles\n" + + " n.b. Some properties are not able to be converted to python " + "types.\n\n" + " ARGUMENTS:\n" + " - includePrivate: (optional) toggles inclusion of private " + "properties in the result set.\n" + " Defaults to False.\n" + " - includeComputed: (optional) toggles inclusion of computed " + "properties in the result set.\n" + " Defaults to False.\n\n" + " RETURNS: a dictionary\n"; + template boost::python::dict GetPropsAsDict(const T &obj, bool includePrivate, bool includeComputed, @@ -98,6 +113,7 @@ boost::python::dict GetPropsAsDict(const T &obj, bool includePrivate, break; case RDTypeTag::StringTag: { auto value = from_rdvalue(rdvalue.val); + boost::trim(value); if (autoConvertStrings) { // Auto convert strings to ints and double if possible int ivalue; diff --git a/Code/GraphMol/Wrap/rough_test.py b/Code/GraphMol/Wrap/rough_test.py index 1b018e388..7dd3880f4 100644 --- a/Code/GraphMol/Wrap/rough_test.py +++ b/Code/GraphMol/Wrap/rough_test.py @@ -613,7 +613,9 @@ class TestCase(unittest.TestCase): m = Chem.MolFromSmiles('C1=CN=CC=C1') m.SetProp("int", "1000") m.SetProp("double", "10000.123") - self.assertEqual(m.GetPropsAsDict(), {"int": 1000, "double": 10000.123}) + m.SetProp("double spaces", " 10000.123 ") + self.assertEqual(m.GetPropsAsDict(), { + "int": 1000, "double": 10000.123, "double spaces": 10000.123 }) self.assertEqual(type(m.GetPropsAsDict()['int']), int) self.assertEqual(type(m.GetPropsAsDict()['double']), float) diff --git a/Code/RDGeneral/RDValue.h b/Code/RDGeneral/RDValue.h index 5d754e28c..1bdbe00a2 100644 --- a/Code/RDGeneral/RDValue.h +++ b/Code/RDGeneral/RDValue.h @@ -39,6 +39,8 @@ #include "RDValue-taggedunion.h" #endif +#include + namespace RDKit { // Common Casts (POD Casts are implementation dependent) // string casts @@ -274,7 +276,11 @@ typename boost::enable_if, T>::type from_rdvalue( res = rdvalue_cast(arg); } catch (const std::bad_any_cast &exc) { try { - res = boost::lexical_cast(rdvalue_cast(arg)); + std::string val = rdvalue_cast(arg); + // trim only the right characters, this mimics how SD values + // work on read, they will be trimmed by the MolFile parser + boost::trim_right(val); + res = boost::lexical_cast(val); } catch (...) { throw exc; } diff --git a/Code/RDGeneral/testRDValue.cpp b/Code/RDGeneral/testRDValue.cpp index 0eef89892..e778c26f5 100644 --- a/Code/RDGeneral/testRDValue.cpp +++ b/Code/RDGeneral/testRDValue.cpp @@ -276,3 +276,13 @@ TEST_CASE("testIntConversions") { REQUIRE_THROWS_AS(p.getProp("foo"), boost::numeric::negative_overflow); } + +TEST_CASE("testStringToDouble") { + RDProps p; + p.setProp("foo", "123.0 "); + p.setProp("bar", " 123.0 "); + REQUIRE(p.getProp("foo") == 123.0); + REQUIRE(p.getProp("foo") == 123.0f); + REQUIRE_THROWS_AS(p.getProp("bar"), + std::bad_any_cast); +}