#pragma once #define PY_SSIZE_T_CLEAN #include <Python.h> #include <util/generic/string.h> #include <util/generic/map.h> #include <util/generic/set.h> #include <util/generic/vector.h> #include <util/generic/ptr.h> #include "cast.h" #include "exceptions.h" namespace NPyBind { // TBaseAttrGetter template <typename TObjType> class TBaseAttrGetter { public: virtual ~TBaseAttrGetter() { } virtual bool GetAttr(PyObject* owner, const TObjType& self, const TString& attr, PyObject*& res) const = 0; virtual bool HasAttr(PyObject* owner, const TObjType& self, const TString& attr, const TSet<TString>& hiddenNames) const { if (hiddenNames.find(attr) != hiddenNames.end()) return false; PyObject* res = nullptr; if (!GetAttr(owner, self, attr, res)) return false; Py_XDECREF(res); return true; } }; template <typename TObjType> class TBaseAttrSetter { public: virtual ~TBaseAttrSetter() { } virtual bool SetAttr(PyObject* owner, TObjType& self, const TString& attr, PyObject* val) = 0; }; template <typename TObjType> class TAttrGetters { public: typedef TSimpleSharedPtr<TBaseAttrGetter<TObjType>> TGetterPtr; private: typedef TVector<TGetterPtr> TGetterList; typedef TMap<TString, TGetterList> TGetterMap; const TSet<TString>& HiddenAttrNames; TGetterMap Getters; public: TAttrGetters(const TSet<TString>& hiddenNames) : HiddenAttrNames(hiddenNames) { } void AddGetter(const TString& attr, TGetterPtr getter) { Getters[attr].push_back(getter); } PyObject* GetAttr(PyObject* owner, const TObjType& self, const TString& attr) const { typename TGetterMap::const_iterator it1 = Getters.find(attr); if (it1 == Getters.end()) it1 = Getters.find(""); if (it1 == Getters.end()) return nullptr; const TGetterList& lst = it1->second; for (typename TGetterList::const_iterator it2 = lst.begin(), end = lst.end(); it2 != end; ++it2) { PyObject* res = nullptr; if ((*it2)->GetAttr(owner, self, attr, res)) return res; // IMPORTANT! // we have to fail GetAttr right there because we've failed because of internal python error/exception and can't continue iterating because // it cause subsequent exceptions during call to Py_BuildValue // moreover we have to preserve original exception right there if (PyErr_Occurred()) { break; } } return nullptr; } bool HasAttr(PyObject* owner, const TObjType& self, const TString& attr) const { typename TGetterMap::const_iterator it1 = Getters.find(attr); if (it1 == Getters.end()) return false; const TGetterList& lst = it1->second; for (typename TGetterList::const_iterator it2 = lst.begin(), end = lst.end(); it2 != end; ++it2) { if ((*it2)->HasAttr(owner, self, attr, HiddenAttrNames)) return true; } return false; } void GetAttrsDictionary(PyObject* owner, const TObjType& self, TMap<TString, PyObject*>& res) const { for (typename TGetterMap::const_iterator it = Getters.begin(), end = Getters.end(); it != end; ++it) { try { if (HasAttr(owner, self, it->first)) { auto attrPtr = GetAttr(owner, self, it->first); if (attrPtr) { res[it->first] = attrPtr; } if (PyErr_Occurred()) { PyErr_Clear(); // Skip python errors as well } } } catch (const std::exception&) { // ignore this field } } } void GetAttrsNames(PyObject* owner, const TObjType& self, TVector<TString>& resultNames) const { for (typename TGetterMap::const_iterator it = Getters.begin(), end = Getters.end(); it != end; ++it) { if (HasAttr(owner, self, it->first)) resultNames.push_back(it->first); } } }; template <typename TObjType> class TGenericAttrGetter: public TBaseAttrGetter<TObjType> { private: TString AttrName; public: TGenericAttrGetter(const TString& attrName) : AttrName(attrName) { } bool GetAttr(PyObject* obj, const TObjType&, const TString&, PyObject*& res) const override { auto str = NameFromString(AttrName); res = PyObject_GenericGetAttr(obj, str.Get()); if (!res && !PyErr_Occurred()) ythrow TPyErr(PyExc_AttributeError) << "Can't get generic attribute '" << AttrName << "'"; return res; } }; template <typename TObjType> class TAttrSetters { private: typedef TSimpleSharedPtr<TBaseAttrSetter<TObjType>> TSetterPtr; typedef TVector<TSetterPtr> TSetterList; typedef TMap<TString, TSetterList> TSetterMap; TSetterMap Setters; public: void AddSetter(const TString& attr, TSetterPtr setter) { Setters[attr].push_back(setter); } bool SetAttr(PyObject* owner, TObjType& self, const TString& attr, PyObject* val) { typename TSetterMap::const_iterator it1 = Setters.find(attr); if (it1 == Setters.end()) it1 = Setters.find(""); if (it1 == Setters.end()) return false; const TSetterList& lst = it1->second; for (typename TSetterList::const_iterator it2 = lst.begin(), end = lst.end(); it2 != end; ++it2) { if ((*it2)->SetAttr(owner, self, attr, val)) return true; } return false; } bool SetAttrDictionary(PyObject* owner, TObjType& self, TMap<TString, PyObject*>& dict) { for (TMap<TString, PyObject*>::const_iterator it = dict.begin(), end = dict.end(); it != end; ++it) { try { SetAttr(owner, self, it->first, it->second); } catch (std::exception&) { // ignore this field } } return true; } }; /** * TMethodAttrGetter - this class maps Python attribute read to C++ method call */ template <typename TObjType, typename TResult, typename TSubObject> class TMethodAttrGetter: public TBaseAttrGetter<TObjType> { private: typedef TResult (TSubObject::*TMethod)() const; TMethod Method; public: TMethodAttrGetter(TMethod method) : Method(method) { } bool GetAttr(PyObject*, const TObjType& self, const TString&, PyObject*& res) const override { const TSubObject* sub = dynamic_cast<const TSubObject*>(&self); if (sub == nullptr) return false; res = BuildPyObject((sub->*Method)()); return (res != nullptr); } }; template <typename TObjType, typename TFunctor> class TFunctorAttrGetter: public TBaseAttrGetter<TObjType> { TFunctor Functor; public: explicit TFunctorAttrGetter(TFunctor functor) : Functor(functor) { } bool GetAttr(PyObject*, const TObjType& self, const TString&, PyObject*& res) const override { res = BuildPyObject(Functor(self)); return (res != nullptr); } }; /** * TMethodAttrGetterWithCheck - this class maps Python attribute read to C++ HasAttr/GetAttr call * If HasAttr returns false, None is returned. * Otherwise GetAttr is called. */ template <typename TObjType, typename TResult, typename TSubObject> class TMethodAttrGetterWithCheck: public TBaseAttrGetter<TObjType> { private: typedef TResult (TSubObject::*TMethod)() const; typedef bool (TSubObject::*TCheckerMethod)() const; TMethod Method; TCheckerMethod CheckerMethod; public: TMethodAttrGetterWithCheck(TMethod method, TCheckerMethod checkerMethod) : Method(method) , CheckerMethod(checkerMethod) { } bool GetAttr(PyObject*, const TObjType& self, const TString&, PyObject*& res) const override { const TSubObject* sub = dynamic_cast<const TSubObject*>(&self); if (sub == nullptr) return false; if ((sub->*CheckerMethod)()) res = BuildPyObject((sub->*Method)()); else { Py_INCREF(Py_None); res = Py_None; } return (res != nullptr); } }; template <typename TObjType, typename TResult, typename TSubObject, typename TMapper> class TMethodAttrMappingGetter: public TBaseAttrGetter<TObjType> { private: typedef TResult (TSubObject::*TMethod)() const; TMethod Method; TMapper Mapper; public: TMethodAttrMappingGetter(TMethod method, TMapper mapper) : Method(method) , Mapper(mapper) { } bool GetAttr(PyObject*, const TObjType& self, const TString&, PyObject*& res) const override { const TSubObject* sub = dynamic_cast<const TSubObject*>(&self); if (sub == nullptr) return false; res = BuildPyObject(Mapper((sub->*Method)())); return (res != nullptr); } }; template <typename TObjType, typename TResult, typename TSubObject, typename TMapper> TSimpleSharedPtr<TBaseAttrGetter<TObjType>> CreateMethodAttrMappingGetter(TResult (TSubObject::*method)() const, TMapper mapper) { return new TMethodAttrMappingGetter<TObjType, TResult, TSubObject, TMapper>(method, mapper); } template <typename TObjType, typename TResult, typename TValue, typename TSubObject> class TMethodAttrSetter: public TBaseAttrSetter<TObjType> { private: typedef TResult (TSubObject::*TMethod)(TValue&); TMethod Method; public: TMethodAttrSetter(TMethod method) : Method(method) { } virtual bool SetAttr(PyObject*, TObjType& self, const TString&, PyObject* val) { TSubObject* sub = dynamic_cast<TSubObject*>(&self); if (sub == nullptr) return false; TValue value; if (!FromPyObject(val, value)) return false; (sub->*Method)(value); return true; } }; template <typename TObjType, typename TValue, typename TFunctor> class TFunctorAttrSetter: public TBaseAttrSetter<TObjType> { TFunctor Functor; public: explicit TFunctorAttrSetter(TFunctor functor) : Functor(functor) { } bool SetAttr(PyObject*, TObjType& self, const TString&, PyObject* val) const override { TValue value; if (!FromPyObject(val, value)) return false; auto res = BuildPyObject(Functor(self, value)); return (res != nullptr); } }; template <typename TObjType, typename TResult, typename TSubObject> TSimpleSharedPtr<TBaseAttrGetter<TObjType>> CreateMethodAttrGetter(TResult (TSubObject::*method)() const) { return new TMethodAttrGetter<TObjType, TResult, TSubObject>(method); } template <typename TObjType, typename TFunctor> TSimpleSharedPtr<TFunctorAttrGetter<TObjType, TFunctor>> CreateFunctorAttrGetter(TFunctor functor) { return MakeSimpleShared<TFunctorAttrGetter<TObjType, TFunctor>>(functor); } template <typename TObjType, typename TResult, typename TSubObject> TSimpleSharedPtr<TBaseAttrGetter<TObjType>> CreateMethodAttrGetterWithCheck( TResult (TSubObject::*method)() const, bool (TSubObject::*checkerMethod)() const) { return new TMethodAttrGetterWithCheck<TObjType, TResult, TSubObject>(method, checkerMethod); } template <typename TObjType, typename TResult, typename TValue, typename TSubObject> TSimpleSharedPtr<TBaseAttrSetter<TObjType>> CreateMethodAttrSetter(TResult (TSubObject::*method)(TValue&)) { return new TMethodAttrSetter<TObjType, TResult, TValue, TSubObject>(method); } template <typename TObjType, typename TFunctor, typename TValue> TSimpleSharedPtr<TFunctorAttrSetter<TObjType, TValue, TFunctor>> CreateFunctorAttrSetter(TFunctor functor) { return MakeSimpleShared<TFunctorAttrSetter<TObjType, TValue, TFunctor>>(functor); } template <typename TObjType, typename TValue, typename TSubObject> class TDirectAttrSetter: public TBaseAttrSetter<TObjType> { private: typedef TValue TSubObject::*TValueType; TValueType Value; public: TDirectAttrSetter(TValueType value) : Value(value) { } bool SetAttr(PyObject*, TObjType& self, const TString&, PyObject* val) override { TSubObject* sub = dynamic_cast<TSubObject*>(&self); if (sub == NULL) return false; if (!FromPyObject(val, sub->*Value)) return false; return true; } }; template <typename TObjType, typename TValue, typename TSubObject> TSimpleSharedPtr<TBaseAttrSetter<TObjType>> CreateAttrSetter(TValue TSubObject::*value) { return new TDirectAttrSetter<TObjType, TValue, TSubObject>(value); } template <typename TObjType, typename TValue, typename TSubObject> class TDirectAttrGetter: public TBaseAttrGetter<TObjType> { private: typedef TValue TSubObject::*TValueType; TValueType Value; public: TDirectAttrGetter(TValueType value) : Value(value) { } bool GetAttr(PyObject*, const TObjType& self, const TString&, PyObject*& res) const override { const TSubObject* sub = dynamic_cast<const TSubObject*>(&self); if (sub == nullptr) return false; res = BuildPyObject(sub->*Value); return (res != nullptr); } }; template <typename TObjType, typename TValue, typename TSubObject> TSimpleSharedPtr<TBaseAttrGetter<TObjType>> CreateAttrGetter(TValue TSubObject::*value) { return new TDirectAttrGetter<TObjType, TValue, TSubObject>(value); } }