// // Created by Gareth Jones on 5/30/2020. // // Copyright 2020-2022 Schrodinger, Inc and other RDKit contributors // @@ All Rights Reserved @@ // This file is part of the RDKit. // The contents are covered by the terms of the BSD license // which is included in the file license.txt, found at the root // of the RDKit source tree. #include #include #include #include #include #define PY_ARRAY_UNIQUE_SYMBOL rdtautomerquery_array_API namespace python = boost::python; using boost_adaptbx::python::streambuf; using namespace RDKit; namespace { TautomerQuery *createDefaultTautomerQuery(const ROMol &mol) { return TautomerQuery::fromMol(mol); } bool tautomerIsSubstructOf(const TautomerQuery &self, const ROMol &target, bool recursionPossible = true, bool useChirality = false, bool useQueryQueryMatches = false) { return HasSubstructMatch(target, self, recursionPossible, useChirality, useQueryQueryMatches); } bool tautomerIsSubstructOfWithParams(const TautomerQuery &self, const ROMol &target, const SubstructMatchParameters ¶ms) { return helpHasSubstructMatch(target, self, params); } PyObject *tautomerGetSubstructMatch(const TautomerQuery &self, const ROMol &target, bool useChirality = false, bool useQueryQueryMatches = false) { return GetSubstructMatch(target, self, useChirality, useQueryQueryMatches); } PyObject *tautomerGetSubstructMatchWithParams( const TautomerQuery &self, const ROMol &target, const SubstructMatchParameters ¶ms) { return helpGetSubstructMatch(target, self, params); } PyObject *tautomerGetSubstructMatchesWithParams( const TautomerQuery &self, const ROMol &target, const SubstructMatchParameters ¶ms) { return helpGetSubstructMatches(target, self, params); } PyObject *tautomerGetSubstructMatches(const TautomerQuery &self, const ROMol &target, bool uniquify = true, bool useChirality = false, bool useQueryQueryMatches = false, unsigned int maxMatches = 1000) { return GetSubstructMatches(target, self, uniquify, useChirality, useQueryQueryMatches, maxMatches); } PyObject *getTautomers(const TautomerQuery &self) { auto tauts = self.getTautomers(); auto nTauts = tauts.size(); auto tuple = PyTuple_New(nTauts); for (size_t i = 0; i < nTauts; i++) { PyTuple_SetItem(tuple, i, python::converter::shared_ptr_to_python(tauts[i])); } return tuple; } PyObject *matchesWithTautomersToTuple(std::vector &matches, const MOL_SPTR_VECT &matchingTautomers) { int const numberMatches = matches.size(); PyObject *res = PyTuple_New(numberMatches); for (int idx = 0; idx < numberMatches; idx++) { PyObject *pair = PyTuple_New(2); PyTuple_SetItem(pair, 0, convertMatches(matches[idx])); PyTuple_SetItem( pair, 1, python::converter::shared_ptr_to_python(matchingTautomers[idx])); PyTuple_SetItem(res, idx, pair); } return res; } PyObject *tautomerGetSubstructMatchesWithTautomersWithParams( const TautomerQuery &self, const ROMol &target, const SubstructMatchParameters ¶ms) { std::vector matches; std::vector matchingTautomers; if (params.extraFinalCheck) { // NOTE: Because we are going into/out of python here, we can't // run with NOGIL matches = self.substructOf(target, params, &matchingTautomers); } else { NOGIL gil; matches = self.substructOf(target, params, &matchingTautomers); } return matchesWithTautomersToTuple(matches, matchingTautomers); } PyObject *tautomerGetSubstructMatchesWithTautomers( const TautomerQuery &self, const ROMol &target, bool uniquify = true, bool useChirality = false, bool useQueryQueryMatches = false, unsigned int maxMatches = 1000) { std::vector matches; std::vector matchingTautomers; SubstructMatchParameters params; params.uniquify = uniquify; params.useChirality = useChirality; params.useQueryQueryMatches = useQueryQueryMatches; params.maxMatches = maxMatches; { NOGIL gil; matches = self.substructOf(target, params, &matchingTautomers); } return matchesWithTautomersToTuple(matches, matchingTautomers); } } // namespace namespace RDKit { struct tautomerquery_pickle_suite : rdkit_pickle_suite { static python::tuple getinitargs(const TautomerQuery &self) { if (!TautomerQueryCanSerialize()) { throw_runtime_error("Pickling of TautomerQuery instances is not enabled"); } auto res = self.serialize(); return python::make_tuple(python::object(python::handle<>( PyBytes_FromStringAndSize(res.c_str(), res.length())))); }; }; void toStream(const TautomerQuery &tq, python::object &fileobj) { streambuf ss(fileobj, 't'); streambuf::ostream ost(ss); tq.toStream(ost); } void initFromStream(TautomerQuery &tq, python::object &fileobj) { streambuf ss(fileobj, 'b'); // python StringIO can't seek, so need binary data streambuf::istream is(ss); tq.initFromStream(is); } python::object TQToBinary(const TautomerQuery &tq) { auto res = tq.serialize(); return python::object( python::handle<>(PyBytes_FromStringAndSize(res.c_str(), res.length()))); } } // namespace RDKit struct TautomerQuery_wrapper { static void wrap() { auto docString = "The Tautomer Query Class.\n\ Creates a query that enables structure search accounting for matching of\n\ Tautomeric forms\n"; python::class_( "TautomerQuery", docString, python::no_init) .def("__init__", python::make_constructor(createDefaultTautomerQuery)) .def("__init__", python::make_constructor(&TautomerQuery::fromMol)) .def(python::init(python::args("self", "pickle"))) .def("IsSubstructOf", tautomerIsSubstructOf, (python::arg("self"), python::arg("target"), python::arg("recursionPossible") = true, python::arg("useChirality") = false, python::arg("useQueryQueryMatches") = false)) .def( "IsSubstructOf", tautomerIsSubstructOfWithParams, (python::arg("self"), python::arg("target"), python::arg("params"))) .def("GetSubstructMatch", tautomerGetSubstructMatch, (python::arg("self"), python::arg("target"), python::arg("useChirality") = false, python::arg("useQueryQueryMatches") = false)) .def( "GetSubstructMatch", tautomerGetSubstructMatchWithParams, (python::arg("self"), python::arg("target"), python::arg("params"))) .def("GetSubstructMatches", tautomerGetSubstructMatches, (python::arg("self"), python::arg("target"), python::arg("uniquify") = true, python::arg("useChirality") = false, python::arg("useQueryQueryMatches") = false, python::arg("maxMatches") = 1000)) .def( "GetSubstructMatches", tautomerGetSubstructMatchesWithParams, (python::arg("self"), python::arg("target"), python::arg("params"))) .def("GetSubstructMatchesWithTautomers", tautomerGetSubstructMatchesWithTautomers, (python::arg("self"), python::arg("target"), python::arg("uniquify") = true, python::arg("useChirality") = false, python::arg("useQueryQueryMatches") = false, python::arg("maxMatches") = 1000)) .def( "GetSubstructMatchesWithTautomers", tautomerGetSubstructMatchesWithTautomersWithParams, (python::arg("self"), python::arg("target"), python::arg("params"))) .def("PatternFingerprintTemplate", &TautomerQuery::patternFingerprintTemplate, ((python::arg("self"), python::arg("fingerprintSize") = 2048)), python::return_value_policy()) .def("GetTemplateMolecule", &TautomerQuery::getTemplateMolecule, python::return_internal_reference<>(), python::args("self")) .def("GetModifiedAtoms", &TautomerQuery::getModifiedAtoms, python::args("self")) .def("GetModifiedBonds", &TautomerQuery::getModifiedBonds, python::args("self")) .def("GetTautomers", getTautomers, python::args("self")) .def("ToBinary", TQToBinary, python::args("self")) .def_pickle(tautomerquery_pickle_suite()); python::def("PatternFingerprintTautomerTarget", &TautomerQuery::patternFingerprintTarget, (python::arg("target"), python::arg("fingerprintSize") = 2048), python::return_value_policy()); python::def( "TautomerQueryCanSerialize", TautomerQueryCanSerialize, "Returns True if the TautomerQuery is serializable " "(requires that the RDKit was built with boost::serialization)"); } }; void wrap_TautomerQuery() { TautomerQuery_wrapper::wrap(); } BOOST_PYTHON_MODULE(rdTautomerQuery) { wrap_TautomerQuery(); }