// // Copyright (C) 2003-2017 Greg Landrum and Rational Discovery LLC // // @@ 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. // #define NO_IMPORT_ARRAY #include #include #include #include #include #include #include #include #include #include #include "seqs.hpp" #include "props.hpp" #include namespace python = boost::python; namespace RDKit { void expandQuery(QueryAtom *self, const QueryAtom *other, Queries::CompositeQueryType how, bool maintainOrder) { if (!other) { throw_value_error("other Atom is null"); } if (other->hasQuery()) { const QueryAtom::QUERYATOM_QUERY *qry = other->getQuery(); self->expandQuery(qry->copy(), how, maintainOrder); } } void setQuery(QueryAtom *self, const QueryAtom *other) { if (!other) { throw_value_error("other Atom is null"); } if (other->hasQuery()) { self->setQuery(other->getQuery()->copy()); } } template void AtomSetProp(const Atom *atom, const std::string &key, const T &val) { // std::cerr<<"asp: "<setProp(key, val); } int AtomHasProp(const Atom *atom, const std::string &key) { // std::cerr<<"ahp: "<hasProp(key); return res; } void AtomClearProp(const Atom *atom, const std::string &key) { if (!atom->hasProp(key)) { return; } atom->clearProp(key); } python::tuple AtomGetNeighbors(Atom *atom) { python::list res; for (auto nbr : atom->getOwningMol().atomNeighbors(atom)) { res.append(python::ptr(nbr)); } return python::tuple(res); } python::tuple AtomGetBonds(Atom *atom) { python::list res; for (auto bond : atom->getOwningMol().atomBonds(atom)) { res.append(python::ptr(bond)); } return python::tuple(res); } bool AtomIsInRing(const Atom *atom) { if (!atom->getOwningMol().getRingInfo()->isSssrOrBetter()) { MolOps::findSSSR(atom->getOwningMol()); } return atom->getOwningMol().getRingInfo()->numAtomRings(atom->getIdx()) != 0; } bool AtomIsInRingSize(const Atom *atom, int size) { if (!atom->getOwningMol().getRingInfo()->isSssrOrBetter()) { MolOps::findSSSR(atom->getOwningMol()); } return atom->getOwningMol().getRingInfo()->isAtomInRingOfSize(atom->getIdx(), size); } std::string AtomGetSmarts(const Atom *atom, bool doKekule, bool allHsExplicit, bool isomericSmiles) { std::string res; if (atom->hasQuery()) { res = SmartsWrite::GetAtomSmarts(static_cast(atom)); } else { // FIX: this should not be necessary res = SmilesWrite::GetAtomSmiles(atom, doKekule, nullptr, allHsExplicit, isomericSmiles); } return res; } void SetAtomMonomerInfo(Atom *atom, const AtomMonomerInfo *info) { if (!info) { atom->setMonomerInfo(nullptr); } else { atom->setMonomerInfo(info->copy()); } } AtomMonomerInfo *AtomGetMonomerInfo(Atom *atom) { return atom->getMonomerInfo(); } void AtomSetPDBResidueInfo(Atom *atom, const AtomMonomerInfo *info) { if (!info) { // This clears out the monomer info atom->setMonomerInfo(nullptr); return; } if (info->getMonomerType() != AtomMonomerInfo::PDBRESIDUE) { throw_value_error("MonomerInfo is not a PDB Residue"); } atom->setMonomerInfo(info->copy()); } AtomPDBResidueInfo *AtomGetPDBResidueInfo(Atom *atom) { AtomMonomerInfo *res = atom->getMonomerInfo(); if (!res) { return nullptr; } if (res->getMonomerType() != AtomMonomerInfo::PDBRESIDUE) { throw_value_error("MonomerInfo is not a PDB Residue"); } return (AtomPDBResidueInfo *)res; } namespace { int getExplicitValenceHelper(const Atom *atom) { RDLog::deprecationWarning( "please use GetValence(Chem.ValenceType.EXPLICIT) instead"); return atom->getValence(Atom::ValenceType::EXPLICIT); }; int getImplicitValenceHelper(const Atom *atom) { RDLog::deprecationWarning( "please use GetValence(Chem.ValenceType.IMPLICIT) instead"); return atom->getValence(Atom::ValenceType::IMPLICIT); }; } // namespace struct MDLDummy {}; struct DaylightDummy {}; // FIX: is there any reason at all to not just prevent the construction of // Atoms? std::string atomClassDoc = "The class to store Atoms.\n\ Note that, though it is possible to create one, having an Atom on its own\n\ (i.e not associated with a molecule) is not particularly useful.\n"; struct atom_wrapper { static void wrap() { #if defined(__GNUC__) or defined(__clang__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif python::class_( "Atom", atomClassDoc.c_str(), python::init(python::args("self", "what"))) .def(python::init(python::args("self", "other"))) .def(python::init(python::args("self", "num"), "Constructor, takes the atomic number")) .def_readonly("NOATOM", &Atom::NOATOM, "marker for unspecified int values") .def("__copy__", &Atom::copy, python::return_value_policy< python::manage_new_object, python::with_custodian_and_ward_postcall<0, 1>>(), python::args("self"), "Create a copy of the atom") .def("GetAtomicNum", &Atom::getAtomicNum, python::args("self"), "Returns the atomic number.") .def("SetAtomicNum", &Atom::setAtomicNum, python::args("self", "newNum"), "Sets the atomic number, takes an integer value as an argument") .def("GetSymbol", &Atom::getSymbol, python::args("self"), "Returns the atomic symbol (a string)\n") .def("GetIdx", &Atom::getIdx, python::args("self"), "Returns the atom's index (ordering in the molecule)\n") .def("GetDegree", &Atom::getDegree, python::args("self"), "Returns the degree of the atom in the molecule.\n\n" " The degree of an atom is defined to be its number of\n" " directly-bonded neighbors.\n" " The degree is independent of bond orders, but is dependent\n" " on whether or not Hs are explicit in the graph.\n") .def("GetTotalDegree", &Atom::getTotalDegree, python::args("self"), "Returns the degree of the atom in the molecule including Hs.\n\n" " The degree of an atom is defined to be its number of\n" " directly-bonded neighbors.\n" " The degree is independent of bond orders.\n") .def("GetTotalNumHs", &Atom::getTotalNumHs, (python::arg("self"), python::arg("includeNeighbors") = false), "Returns the total number of Hs (explicit and implicit) on the " "atom.\n\n" " ARGUMENTS:\n\n" " - includeNeighbors: (optional) toggles inclusion of " "neighboring H atoms in the sum.\n" " Defaults to 0.\n") .def("GetNumImplicitHs", &Atom::getNumImplicitHs, python::args("self"), "Returns the total number of implicit Hs on the atom.\n") .def( "GetExplicitValence", &getExplicitValenceHelper, python::args("self"), "DEPRECATED, please use GetValence(Chem.ValenceType.EXPLICIT) instead.\nReturns the explicit valence of the atom.\n") .def( "GetImplicitValence", &getImplicitValenceHelper, python::args("self"), "DEPRECATED, please use GetValence(Chem.ValenceType.IMPLICIT) instead.\nReturns the number of implicit Hs on the atom.\n") .def( "GetValence", &Atom::getValence, (python::args("self"), python::args("which")), "Returns the valence (Chem.ValenceType.EXPLICIT or Chem.ValenceType.IMPLICIT) of the atom.\n") .def("GetTotalValence", &Atom::getTotalValence, python::args("self"), "Returns the total valence (explicit + implicit) of the atom.\n\n") .def("HasValenceViolation", &Atom::hasValenceViolation, "Returns whether the atom has a valence violation or not.\n\n") .def("GetFormalCharge", &Atom::getFormalCharge, python::args("self")) .def("SetFormalCharge", &Atom::setFormalCharge, python::args("self", "what")) .def("SetNoImplicit", &Atom::setNoImplicit, python::args("self", "what"), "Sets a marker on the atom that *disallows* implicit Hs.\n" " This holds even if the atom would otherwise have implicit Hs " "added.\n") .def("GetNoImplicit", &Atom::getNoImplicit, python::args("self"), "Returns whether or not the atom is *allowed* to have implicit " "Hs.\n") .def("SetNumExplicitHs", &Atom::setNumExplicitHs, python::args("self", "what")) .def("GetNumExplicitHs", &Atom::getNumExplicitHs, python::args("self")) .def("SetIsAromatic", &Atom::setIsAromatic, python::args("self", "what")) .def("GetIsAromatic", &Atom::getIsAromatic, python::args("self")) .def("GetMass", &Atom::getMass, python::args("self")) .def("SetIsotope", &Atom::setIsotope, python::args("self", "what")) .def("GetIsotope", &Atom::getIsotope, python::args("self")) .def("SetNumRadicalElectrons", &Atom::setNumRadicalElectrons, python::args("self", "num")) .def("GetNumRadicalElectrons", &Atom::getNumRadicalElectrons, python::args("self")) .def("GetQueryType", &Atom::getQueryType, python::args("self")) .def("SetChiralTag", &Atom::setChiralTag, python::args("self", "what")) .def("InvertChirality", &Atom::invertChirality, python::args("self")) .def("GetChiralTag", &Atom::getChiralTag, python::args("self")) .def("SetHybridization", &Atom::setHybridization, python::args("self", "what"), "Sets the hybridization of the atom.\n" " The argument should be a HybridizationType\n") .def("GetHybridization", &Atom::getHybridization, python::args("self"), "Returns the atom's hybridization.\n") .def("HasOwningMol", &Atom::hasOwningMol, python::args("self"), "Returns whether or not this instance belongs to a molecule.\n") .def("GetOwningMol", &Atom::getOwningMol, "Returns the Mol that owns this atom.\n", python::return_internal_reference<>(), python::args("self")) .def("GetNeighbors", AtomGetNeighbors, python::args("self"), "Returns a read-only sequence of the atom's neighbors\n") .def("GetBonds", AtomGetBonds, python::args("self"), "Returns a read-only sequence of the atom's bonds\n") .def("Match", (bool(Atom::*)(const Atom *) const) & Atom::Match, python::args("self", "what"), "Returns whether or not this atom matches another Atom.\n\n" " Each Atom (or query Atom) has a query function which is\n" " used for this type of matching.\n\n" " ARGUMENTS:\n" " - other: the other Atom to which to compare\n") .def("IsInRingSize", AtomIsInRingSize, python::args("self", "size"), "Returns whether or not the atom is in a ring of a particular " "size.\n\n" " ARGUMENTS:\n" " - size: the ring size to look for\n") .def("IsInRing", AtomIsInRing, python::args("self"), "Returns whether or not the atom is in a ring\n\n") .def("HasQuery", &Atom::hasQuery, python::args("self"), "Returns whether or not the atom has an associated query\n\n") .def("DescribeQuery", describeQuery, python::args("self"), "returns a text description of the query. Primarily intended for " "debugging purposes.\n\n") .def("GetSmarts", AtomGetSmarts, (python::arg("self"), python::arg("doKekule") = false, python::arg("allHsExplicit") = false, python::arg("isomericSmiles") = true), "returns the SMARTS (or SMILES) string for an Atom\n\n") // properties .def("SetProp", AtomSetProp, (python::arg("self"), python::arg("key"), python::arg("val")), "Sets an atomic property\n\n" " ARGUMENTS:\n" " - key: the name of the property to be set (a string).\n" " - value: the property value (a string).\n\n") .def( "GetProp", GetPyProp, (python::arg("self"), python::arg("key"), python::arg("autoConvert") = false), "Returns the value of the property.\n\n" " ARGUMENTS:\n" " - key: the name of the property to return (a string).\n\n" " - autoConvert: if True attempt to convert the property into a python object\n\n" " RETURNS: a string\n\n" " NOTE:\n" " - If the property has not been set, a KeyError exception " "will be raised.\n", boost::python::return_value_policy()) .def( "GetProp", GetPyPropOrDefault, (python::arg("self"), python::arg("key"), python::arg("autoConvert") = false, python::arg("default")), "Returns the value of the property.\n\n" " ARGUMENTS:\n" " - key: the name of the property to return (a string).\n\n" " - autoConvert: if True attempt to convert the property into a python object\n\n" " - default: value to return if the property is not present.\n\n" " RETURNS: the property value, or default if the property is not present.\n", boost::python::return_value_policy()) .def("SetIntProp", AtomSetProp, (python::arg("self"), python::arg("key"), python::arg("val")), "Sets an atomic property\n\n" " ARGUMENTS:\n" " - key: the name of the property to be set (a int).\n" " - value: the property value (a int).\n\n") .def("SetUnsignedProp", AtomSetProp, (python::arg("self"), python::arg("key"), python::arg("val")), "Sets an atomic property\n\n" " ARGUMENTS:\n" " - key: the name of the property to be set (an unsigned " "integer).\n" " - value: the property value (a int >= 0).\n\n") .def("GetIntProp", GetProp, python::args("self", "key"), "Returns the value of the property.\n\n" " ARGUMENTS:\n" " - key: the name of the property to return (an int).\n\n" " RETURNS: an int\n\n" " NOTE:\n" " - If the property has not been set, a KeyError exception " "will be raised.\n", boost::python::return_value_policy()) .def("GetIntProp", GetPropOrDefault, (python::arg("self"), python::arg("key"), python::arg("default")), "Returns the value of the property.\n\n" " ARGUMENTS:\n" " - key: the name of the property to return (an int).\n\n" " - default: value to return if the property is not present.\n\n" " RETURNS: an int, or default if the property is not present.\n", boost::python::return_value_policy()) .def("GetUnsignedProp", GetProp, python::args("self", "key"), "Returns the value of the property.\n\n" " ARGUMENTS:\n" " - key: the name of the property to return (an unsigned " "integer).\n\n" " RETURNS: an integer (Python has no unsigned type)\n\n" " NOTE:\n" " - If the property has not been set, a KeyError exception " "will be raised.\n", boost::python::return_value_policy()) .def("GetUnsignedProp", GetPropOrDefault, (python::arg("self"), python::arg("key"), python::arg("default")), "Returns the value of the property.\n\n" " ARGUMENTS:\n" " - key: the name of the property to return (an unsigned integer).\n\n" " - default: value to return if the property is not present.\n\n" " RETURNS: an integer, or default if the property is not present.\n", boost::python::return_value_policy()) .def("SetDoubleProp", AtomSetProp, (python::arg("self"), python::arg("key"), python::arg("val")), "Sets an atomic property\n\n" " ARGUMENTS:\n" " - key: the name of the property to be set (a double).\n" " - value: the property value (a double).\n\n") .def("GetDoubleProp", GetProp, python::args("self", "key"), "Returns the value of the property.\n\n" " ARGUMENTS:\n" " - key: the name of the property to return (a double).\n\n" " RETURNS: a double\n\n" " NOTE:\n" " - If the property has not been set, a KeyError exception " "will be raised.\n", boost::python::return_value_policy()) .def("GetDoubleProp", GetPropOrDefault, (python::arg("self"), python::arg("key"), python::arg("default")), "Returns the value of the property.\n\n" " ARGUMENTS:\n" " - key: the name of the property to return (a double).\n\n" " - default: value to return if the property is not present.\n\n" " RETURNS: a double, or default if the property is not present.\n", boost::python::return_value_policy()) .def("SetBoolProp", AtomSetProp, (python::arg("self"), python::arg("key"), python::arg("val")), "Sets an atomic property\n\n" " ARGUMENTS:\n" " - key: the name of the property to be set (a bool).\n" " - value: the property value (a bool).\n\n") .def("GetBoolProp", GetProp, python::args("self", "key"), "Returns the value of the property.\n\n" " ARGUMENTS:\n" " - key: the name of the property to return (a bool).\n\n" " RETURNS: a bool\n\n" " NOTE:\n" " - If the property has not been set, a KeyError exception " "will be raised.\n", boost::python::return_value_policy()) .def("GetBoolProp", GetPropOrDefault, (python::arg("self"), python::arg("key"), python::arg("default")), "Returns the value of the property.\n\n" " ARGUMENTS:\n" " - key: the name of the property to return (a bool).\n\n" " - default: value to return if the property is not present.\n\n" " RETURNS: a bool, or default if the property is not present.\n", boost::python::return_value_policy()) .def("SetExplicitBitVectProp", AtomSetProp, (python::arg("self"), python::arg("key"), python::arg("val")), "Sets an atomic property\n\n" " ARGUMENTS:\n" " - key: the name of the property to be set (an " "ExplicitBitVect).\n" " - value: the property value (an ExplicitBitVect).\n\n") .def("GetExplicitBitVectProp", GetProp, python::args("self", "key"), "Returns the value of the property.\n\n" " ARGUMENTS:\n" " - key: the name of the property to return (a " "ExplicitBitVect).\n\n" " RETURNS: an ExplicitBitVect \n\n" " NOTE:\n" " - If the property has not been set, a KeyError exception " "will be raised.\n", boost::python::return_value_policy()) .def("HasProp", AtomHasProp, python::args("self", "key"), "Queries a Atom to see if a particular property has been " "assigned.\n\n" " ARGUMENTS:\n" " - key: the name of the property to check for (a string).\n") .def("ClearProp", AtomClearProp, python::args("self", "key"), "Removes a particular property from an Atom (does nothing if not " "already set).\n\n" " ARGUMENTS:\n" " - key: the name of the property to be removed.\n") .def("GetPropNames", &Atom::getPropList, (python::arg("self"), python::arg("includePrivate") = false, python::arg("includeComputed") = false), "Returns a list of the properties set on the Atom.\n\n") .def("GetPropsAsDict", GetPropsAsDict, (python::arg("self"), python::arg("includePrivate") = true, python::arg("includeComputed") = true, python::arg("autoConvertStrings") = true), getPropsAsDictDocString.c_str()) .def("UpdatePropertyCache", &Atom::updatePropertyCache, (python::arg("self"), python::arg("strict") = true), "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 " "valence of the molecule have already been calculated.\n\n") .def("ClearPropertyCache", &Atom::clearPropertyCache, (python::arg("self")), "Clears implicit and explicit valence information.\n\n") .def("GetMonomerInfo", AtomGetMonomerInfo, python::return_internal_reference< 1, python::with_custodian_and_ward_postcall<0, 1>>(), python::args("self"), "Returns the atom's MonomerInfo object, if there is one.\n\n") .def("GetPDBResidueInfo", AtomGetPDBResidueInfo, python::return_internal_reference< 1, python::with_custodian_and_ward_postcall<0, 1>>(), python::args("self"), "Returns the atom's MonomerInfo object, if there is one.\n\n") .def("SetMonomerInfo", SetAtomMonomerInfo, python::args("self", "info"), "Sets the atom's MonomerInfo object.\n\n") .def("SetPDBResidueInfo", AtomSetPDBResidueInfo, python::args("self", "info"), "Sets the atom's MonomerInfo object.\n\n") .def("GetAtomMapNum", &Atom::getAtomMapNum, python::args("self"), "Gets the atoms map number, returns 0 if not set") .def("SetAtomMapNum", &Atom::setAtomMapNum, (python::arg("self"), python::arg("mapno"), python::arg("strict") = false), "Sets the atoms map number, a value of 0 clears the atom map"); #if defined(__GNUC__) or defined(__clang__) #pragma GCC diagnostic pop #endif python::enum_("HybridizationType") .value("UNSPECIFIED", Atom::UNSPECIFIED) .value("S", Atom::S) .value("SP", Atom::SP) .value("SP2", Atom::SP2) .value("SP3", Atom::SP3) .value("SP2D", Atom::SP2D) .value("SP3D", Atom::SP3D) .value("SP3D2", Atom::SP3D2) .value("OTHER", Atom::OTHER); python::enum_("ChiralType") .value("CHI_UNSPECIFIED", Atom::CHI_UNSPECIFIED) .value("CHI_TETRAHEDRAL_CW", Atom::CHI_TETRAHEDRAL_CW) .value("CHI_TETRAHEDRAL_CCW", Atom::CHI_TETRAHEDRAL_CCW) .value("CHI_OTHER", Atom::CHI_OTHER) .value("CHI_TETRAHEDRAL", Atom::CHI_TETRAHEDRAL) .value("CHI_ALLENE", Atom::CHI_ALLENE) .value("CHI_SQUAREPLANAR", Atom::CHI_SQUAREPLANAR) .value("CHI_TRIGONALBIPYRAMIDAL", Atom::CHI_TRIGONALBIPYRAMIDAL) .value("CHI_OCTAHEDRAL", Atom::CHI_OCTAHEDRAL) .export_values(); python::enum_("ValenceType") .value("IMPLICIT", Atom::ValenceType::IMPLICIT) .value("EXPLICIT", Atom::ValenceType::EXPLICIT) .export_values(); python::enum_("CompositeQueryType") .value("COMPOSITE_AND", Queries::COMPOSITE_AND) .value("COMPOSITE_OR", Queries::COMPOSITE_OR) .value("COMPOSITE_XOR", Queries::COMPOSITE_XOR) .export_values(); atomClassDoc = "The class to store QueryAtoms.\n\ These cannot currently be constructed directly from Python\n"; python::class_>( "QueryAtom", atomClassDoc.c_str(), python::no_init) .def("ExpandQuery", expandQuery, (python::arg("self"), python::arg("other"), python::arg("how") = Queries::COMPOSITE_AND, python::arg("maintainOrder") = true), "combines the query from other with ours") .def("SetQuery", setQuery, (python::arg("self"), python::arg("other")), "Replace our query with a copy of the other query"); python::def( "GetAtomRLabel", getAtomRLabel, (python::arg("atom")), "Returns the atom's MDL AtomRLabel (this is an integer from 0 to 99)"); python::def("SetAtomRLabel", setAtomRLabel, (python::arg("atom"), python::arg("rlabel")), "Sets the atom's MDL RLabel (this is an integer from 0 to " "99).\nSetting to 0 clears the rlabel."); python::def("GetAtomAlias", getAtomAlias, (python::arg("atom")), "Returns the atom's MDL alias text"); python::def("SetAtomAlias", setAtomAlias, (python::arg("atom"), python::arg("rlabel")), "Sets the atom's MDL alias text.\nSetting to an empty string " "clears the alias."); python::def("GetAtomValue", getAtomValue, (python::arg("atom")), "Returns the atom's MDL alias text"); python::def("SetAtomValue", setAtomValue, (python::arg("atom"), python::arg("rlabel")), "Sets the atom's MDL alias text.\nSetting to an empty string " "clears the alias."); python::def("GetSupplementalSmilesLabel", getSupplementalSmilesLabel, (python::arg("atom")), "Gets the supplemental smiles label on an atom, returns an " "empty string if not present."); python::def( "SetSupplementalSmilesLabel", setSupplementalSmilesLabel, (python::arg("atom"), python::arg("label")), "Sets a supplemental label on an atom that is written to the smiles " "string.\n\n" ">>> m = Chem.MolFromSmiles(\"C\")\n" ">>> Chem.SetSupplementalSmilesLabel(m.GetAtomWithIdx(0), '')\n" ">>> Chem.MolToSmiles(m)\n" "'C'\n"); python::def( "GetNumPiElectrons", numPiElectrons, (python::arg("atom")), "Returns the number of electrons an atom is using for pi bonding"); } }; } // namespace RDKit void wrap_atom() { RDKit::atom_wrapper::wrap(); }