#pragma once #define PY_SSIZE_T_CLEAN #include #include #include #include #include #include #include #include #include "cast.h" #include "exceptions.h" namespace NPyBind { template class TBaseMethodCaller { public: virtual ~TBaseMethodCaller() { } virtual bool CallMethod(PyObject* owner, TObjType* self, PyObject* args, PyObject* kwargs, PyObject*& res) const = 0; virtual bool HasMethod(PyObject*, TObjType*, const TString&, const TSet&) { return true; } }; template class TIsACaller; template class TMethodCallers { private: typedef TSimpleSharedPtr> TCallerPtr; typedef TVector TCallerList; typedef TMap TCallerMap; const TSet& HiddenAttrNames; TCallerMap Callers; public: TMethodCallers(const TSet& hiddenNames) : HiddenAttrNames(hiddenNames) { } void AddCaller(const TString& name, TCallerPtr caller) { Callers[name].push_back(caller); } bool HasCaller(const TString& name) const { return Callers.has(name); } PyObject* CallMethod(PyObject* owner, TObjType* self, PyObject* args, PyObject* kwargs, const TString& name) const { const TCallerList* lst = Callers.FindPtr(name); if (!lst) return nullptr; for (const auto& caller : *lst) { PyObject* res = nullptr; PyErr_Clear(); if (caller->CallMethod(owner, self, args, kwargs, res)) return res; } return nullptr; } bool HasMethod(PyObject* owner, TObjType* self, const TString& name) const { const TCallerList* lst = Callers.FindPtr(name); if (!lst) return false; for (const auto& caller : *lst) { if (caller->HasMethod(owner, self, name, HiddenAttrNames)) return true; } return false; } void GetMethodsNames(PyObject* owner, TObjType* self, TVector& resultNames) const { for (const auto& it : Callers) { if (HasMethod(owner, self, it.first) && !HiddenAttrNames.contains(it.first)) resultNames.push_back(it.first); } } void GetAllMethodsNames(TVector& resultNames) const { for (const auto& it : Callers) { resultNames.push_back(it.first); } } void GetPropertiesNames(PyObject*, TObjType* self, TVector& resultNames) const { const TCallerList* lst = Callers.FindPtr("IsA"); if (!lst) return; for (const auto& caller : *lst) { TIsACaller* isACaller = dynamic_cast*>(caller.Get()); if (isACaller) { resultNames = isACaller->GetPropertiesNames(self); return; } } } }; template class TIsACaller: public TBaseMethodCaller { private: class TIsAChecker { public: virtual ~TIsAChecker() { } virtual bool Check(const TObjType* obj) const = 0; }; template class TIsAConcreteChecker: public TIsAChecker { public: bool Check(const TObjType* obj) const override { return dynamic_cast(obj) != nullptr; } }; typedef TSimpleSharedPtr TCheckerPtr; typedef TMap TCheckersMap; TCheckersMap Checkers; bool Check(const TString& name, const TObjType* obj) const { const TCheckerPtr* checker = Checkers.FindPtr(name); if (!checker) { PyErr_Format(PyExc_KeyError, "unknown class name: %s", name.data()); return false; } return (*checker)->Check(obj); } protected: TIsACaller() { } template void AddChecker(const TString& name) { Checkers[name] = new TIsAConcreteChecker; } public: bool CallMethod(PyObject*, TObjType* self, PyObject* args, PyObject*, PyObject*& res) const override { if (args == nullptr || !PyTuple_Check(args)) return false; size_t cnt = PyTuple_Size(args); bool result = true; for (size_t i = 0; i < cnt; ++i) { result = result && Check( #if PY_MAJOR_VERSION >= 3 PyUnicode_AsUTF8( #else PyString_AsString( #endif PyTuple_GetItem(args, i)), self); } if (PyErr_Occurred()) { return false; } res = BuildPyObject(result); return true; } TVector GetPropertiesNames(const TObjType* obj) const { TVector names; for (const auto& it : Checkers) { if (it.second->Check(obj)) { names.push_back(it.first); } } return names; } }; template class TGenericMethodCaller: public TBaseMethodCaller { private: TString AttrName; public: TGenericMethodCaller(const TString& attrName) : AttrName(attrName) { } bool CallMethod(PyObject* obj, TObjType*, PyObject* args, PyObject*, PyObject*& res) const override { auto str = NameFromString(AttrName); PyObject* attr = PyObject_GenericGetAttr(obj, str.Get()); if (!attr) ythrow yexception() << "Can't get generic attribute '" << AttrName << "'"; res = PyObject_CallObject(attr, args); return res != nullptr; } }; template class TSubObjectChecker: public TBaseMethodCaller { public: ~TSubObjectChecker() override { } bool HasMethod(PyObject*, TObjType* self, const TString&, const TSet&) override { return dynamic_cast(self) != nullptr; } }; template >> void ApplyFunctor(TFunctor functor, Tuple resultArgs, PyObject*& res) { res = BuildPyObject(std::move(std::apply(functor, resultArgs))); } template >, typename=void> void ApplyFunctor(TFunctor functor, Tuple resultArgs, PyObject*& res) { Py_INCREF(Py_None); res = Py_None; std::apply(functor, resultArgs); } template class TFunctorCaller: public TBaseMethodCaller { using TFunctor = std::function; TFunctor Functor; public: explicit TFunctorCaller(TFunctor functor): Functor(functor){} bool CallMethod(PyObject*, TObjType* self, PyObject* args, PyObject*, PyObject*& res) const { auto methodArgsTuple = GetArguments(args); auto resultArgs = std::tuple_cat(std::tie(*self), methodArgsTuple); ApplyFunctor(Functor, resultArgs, res); return true; } }; template class TGetStateCaller: public TSubObjectChecker { protected: TPyObjectPtr AddFromCaller(PyObject* obj, const TString& methodName) const { PyObject* res = PyObject_CallMethod(obj, const_cast(methodName.c_str()), const_cast("")); if (!res) { PyErr_Clear(); return TPyObjectPtr(Py_None); } return TPyObjectPtr(res, true); } void GetStandartAttrsDictionary(PyObject* obj, TRealType*, TMap& dict) const { TPyObjectPtr attrsDict(PyObject_GetAttrString(obj, "__dict__"), true); TMap attrs; if (!FromPyObject(attrsDict.Get(), attrs)) ythrow yexception() << "Can't get '__dict__' attribute"; dict.insert(attrs.begin(), attrs.end()); } virtual void GetAttrsDictionary(PyObject* obj, TRealType* self, TMap& dict) const = 0; public: bool CallMethod(PyObject* obj, TObjType* self, PyObject* args, PyObject*, PyObject*& res) const override { if (!ExtractArgs(args)) ythrow yexception() << "Can't parse arguments: it should be none"; TRealType* rself = dynamic_cast(self); if (!rself) return false; TMap dict; GetAttrsDictionary(obj, rself, dict); res = BuildPyObject(dict); return true; } }; template class TSetStateCaller: public TSubObjectChecker { protected: void SetStandartAttrsDictionary(PyObject* obj, TRealType*, TMap& dict) const { TPyObjectPtr value(BuildPyObject(dict), true); PyObject_SetAttrString(obj, "__dict__", value.Get()); } virtual void SetAttrsDictionary(PyObject* obj, TRealType* self, TMap& dict) const = 0; public: bool CallMethod(PyObject* obj, TObjType* self, PyObject* args, PyObject*, PyObject*& res) const override { TMap dict; if (!ExtractArgs(args, dict)) ythrow yexception() << "Can't parse arguments: it should be one dictionary"; TRealType* rself = dynamic_cast(self); if (!rself) return false; SetAttrsDictionary(obj, rself, dict); Py_INCREF(Py_None); res = Py_None; return true; } }; template class TAnyParameterMethodCaller: public TSubObjectChecker { private: TMethod Method; public: TAnyParameterMethodCaller(TMethod method) : Method(method) { } public: bool CallMethod(PyObject*, TObjType* self, PyObject* args, PyObject*, PyObject*& res) const override { TSubObject* sub = dynamic_cast(self); if (sub == nullptr) return false; if (args && (!PyTuple_Check(args) || PyTuple_Size(args) != TFunctionArgs::Length)) { //ythrow yexception() << "Method takes " << (size_t)(TFunctionArgs::Length) << " arguments, " << PyTuple_Size(args) << " provided"; return false; } try { class Applicant { public: TResult operator()(Args... theArgs) { return (Sub->*Method)(theArgs...); } TSubObject* Sub; TMethod Method; }; res = BuildPyObject(std::move(std::apply(Applicant{sub, Method}, GetArguments(args)))); } catch (cast_exception) { return false; } catch (const TPyNativeErrorException&) { if (!PyErr_Occurred()) { PyErr_SetString(PyExc_RuntimeError, "Some PY error occurred, but it is not set."); } return true; } catch (...) { if (PyExc_StopIteration == PyErr_Occurred()) { // NB: it's replacement for geo_boost::python::throw_error_already_set(); return true; } PyErr_SetString(PyExc_RuntimeError, CurrentExceptionMessage().data()); return true; } return true; } }; template class TAnyParameterMethodCaller: public TSubObjectChecker { private: TMethod Method; public: TAnyParameterMethodCaller(TMethod method) : Method(method) { } public: bool CallMethod(PyObject*, TObjType* self, PyObject* args, PyObject*, PyObject*& res) const override { TSubObject* sub = dynamic_cast(self); if (sub == nullptr) { return false; } if (args && (!PyTuple_Check(args) || PyTuple_Size(args) != TFunctionArgs::Length)) { return false; } try { class Applicant { public: void operator()(Args... theArgs) { (Sub->*Method)(theArgs...); } TSubObject* Sub; TMethod Method; }; std::apply(Applicant{sub, Method}, GetArguments(args)); Py_INCREF(Py_None); res = Py_None; } catch (cast_exception) { return false; } catch (const TPyNativeErrorException&) { if (!PyErr_Occurred()) { PyErr_SetString(PyExc_RuntimeError, "Some PY error occurred, but it is not set."); } return true; } catch (...) { PyErr_SetString(PyExc_RuntimeError, CurrentExceptionMessage().data()); return true; } return true; } }; template struct TConstTraits { typedef TResult (TSubObject::*TMethod)(Args... args) const; }; template struct TNonConstTraits { typedef TResult (TSubObject::*TMethod)(Args... args); }; template class TConstMethodCaller: public TAnyParameterMethodCaller::TMethod, Args...> { public: TConstMethodCaller(typename TConstTraits::TMethod method) : TAnyParameterMethodCaller::TMethod, Args...>(method) { } }; template TSimpleSharedPtr> CreateConstMethodCaller(TResult (TSubObject::*method)(Args...) const) { return new TConstMethodCaller(method); } template TSimpleSharedPtr> CreateFunctorCaller(std::function functor) { return new TFunctorCaller(functor); } template class TMethodCaller: public TAnyParameterMethodCaller::TMethod, Args...> { public: TMethodCaller(typename TNonConstTraits::TMethod method) : TAnyParameterMethodCaller::TMethod, Args...>(method) { } }; template TSimpleSharedPtr> CreateMethodCaller(TResult (TSubObject::*method)(Args...)) { return new TMethodCaller(method); } }