123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439 |
- #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 <util/generic/typetraits.h>
- #include <util/generic/function.h>
- #include "cast.h"
- namespace NPyBind {
- template <typename TObjType>
- 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<TString>&) {
- return true;
- }
- };
- template <typename TObjType>
- class TIsACaller;
- template <typename TObjType>
- class TMethodCallers {
- private:
- typedef TSimpleSharedPtr<TBaseMethodCaller<TObjType>> TCallerPtr;
- typedef TVector<TCallerPtr> TCallerList;
- typedef TMap<TString, TCallerList> TCallerMap;
- const TSet<TString>& HiddenAttrNames;
- TCallerMap Callers;
- public:
- TMethodCallers(const TSet<TString>& 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<TString>& 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<TString>& resultNames) const {
- for (const auto& it : Callers) {
- resultNames.push_back(it.first);
- }
- }
- void GetPropertiesNames(PyObject*, TObjType* self, TVector<TString>& resultNames) const {
- const TCallerList* lst = Callers.FindPtr("IsA");
- if (!lst)
- return;
- for (const auto& caller : *lst) {
- TIsACaller<TObjType>* isACaller = dynamic_cast<TIsACaller<TObjType>*>(caller.Get());
- if (isACaller) {
- resultNames = isACaller->GetPropertiesNames(self);
- return;
- }
- }
- }
- };
- template <typename TObjType>
- class TIsACaller: public TBaseMethodCaller<TObjType> {
- private:
- class TIsAChecker {
- public:
- virtual ~TIsAChecker() {
- }
- virtual bool Check(const TObjType* obj) const = 0;
- };
- template <typename TConcrete>
- class TIsAConcreteChecker: public TIsAChecker {
- public:
- bool Check(const TObjType* obj) const override {
- return dynamic_cast<const TConcrete*>(obj) != nullptr;
- }
- };
- typedef TSimpleSharedPtr<TIsAChecker> TCheckerPtr;
- typedef TMap<TString, TCheckerPtr> 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 <typename TConcrete>
- void AddChecker(const TString& name) {
- Checkers[name] = new TIsAConcreteChecker<TConcrete>;
- }
- 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<TString> GetPropertiesNames(const TObjType* obj) const {
- TVector<TString> names;
- for (const auto& it : Checkers) {
- if (it.second->Check(obj)) {
- names.push_back(it.first);
- }
- }
- return names;
- }
- };
- template <typename TObjType>
- class TGenericMethodCaller: public TBaseMethodCaller<TObjType> {
- 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 <typename TObjType, typename TSubObject>
- class TSubObjectChecker: public TBaseMethodCaller<TObjType> {
- public:
- ~TSubObjectChecker() override {
- }
- bool HasMethod(PyObject*, TObjType* self, const TString&, const TSet<TString>&) override {
- return dynamic_cast<const TSubObject*>(self) != nullptr;
- }
- };
- template <typename TFunctor, typename Tuple, typename ResType, typename=std::enable_if_t<!std::is_same_v<ResType, void>>>
- void ApplyFunctor(TFunctor functor, Tuple resultArgs, PyObject*& res) {
- res = BuildPyObject(std::move(Apply(functor, resultArgs)));
- }
- template <typename TFunctor, typename Tuple, typename ResType, typename=std::enable_if_t<std::is_same_v<ResType, void>>, typename=void>
- void ApplyFunctor(TFunctor functor, Tuple resultArgs, PyObject*& res) {
- Py_INCREF(Py_None);
- res = Py_None;
- Apply(functor, resultArgs);
- }
- template <typename TObjType, typename TResType, typename... Args>
- class TFunctorCaller: public TBaseMethodCaller<TObjType> {
- using TFunctor = std::function<TResType(TObjType&,Args...)>;
- TFunctor Functor;
- public:
- explicit TFunctorCaller(TFunctor functor):
- Functor(functor){}
- bool CallMethod(PyObject*, TObjType* self, PyObject* args, PyObject*, PyObject*& res) const {
- auto methodArgsTuple = GetArguments<Args...>(args);
- auto resultArgs = std::tuple_cat(std::tie(*self), methodArgsTuple);
- ApplyFunctor<TFunctor, decltype(resultArgs), TResType>(Functor, resultArgs, res);
- return true;
- }
- };
- template <typename TObjType, typename TRealType>
- class TGetStateCaller: public TSubObjectChecker<TObjType, TRealType> {
- protected:
- TPyObjectPtr AddFromCaller(PyObject* obj, const TString& methodName) const {
- PyObject* res = PyObject_CallMethod(obj, const_cast<char*>(methodName.c_str()), const_cast<char*>(""));
- if (!res) {
- PyErr_Clear();
- return TPyObjectPtr(Py_None);
- }
- return TPyObjectPtr(res, true);
- }
- void GetStandartAttrsDictionary(PyObject* obj, TRealType*, TMap<TString, TPyObjectPtr>& dict) const {
- TPyObjectPtr attrsDict(PyObject_GetAttrString(obj, "__dict__"), true);
- TMap<TString, TPyObjectPtr> 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<TString, TPyObjectPtr>& 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<TRealType*>(self);
- if (!rself)
- return false;
- TMap<TString, TPyObjectPtr> dict;
- GetAttrsDictionary(obj, rself, dict);
- res = BuildPyObject(dict);
- return true;
- }
- };
- template <typename TObjType, typename TRealType>
- class TSetStateCaller: public TSubObjectChecker<TObjType, TRealType> {
- protected:
- void SetStandartAttrsDictionary(PyObject* obj, TRealType*, TMap<TString, TPyObjectPtr>& dict) const {
- TPyObjectPtr value(BuildPyObject(dict), true);
- PyObject_SetAttrString(obj, "__dict__", value.Get());
- }
- virtual void SetAttrsDictionary(PyObject* obj, TRealType* self, TMap<TString, TPyObjectPtr>& dict) const = 0;
- public:
- bool CallMethod(PyObject* obj, TObjType* self, PyObject* args, PyObject*, PyObject*& res) const override {
- TMap<TString, TPyObjectPtr> dict;
- if (!ExtractArgs(args, dict))
- ythrow yexception() << "Can't parse arguments: it should be one dictionary";
- TRealType* rself = dynamic_cast<TRealType*>(self);
- if (!rself)
- return false;
- SetAttrsDictionary(obj, rself, dict);
- Py_INCREF(Py_None);
- res = Py_None;
- return true;
- }
- };
- template <typename TObjType, typename TResult, typename TSubObject, typename TMethod, typename... Args>
- class TAnyParameterMethodCaller: public TSubObjectChecker<TObjType, TSubObject> {
- 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<TSubObject*>(self);
- if (sub == nullptr)
- return false;
- if (args && (!PyTuple_Check(args) || PyTuple_Size(args) != TFunctionArgs<TMethod>::Length)) {
- //ythrow yexception() << "Method takes " << (size_t)(TFunctionArgs<TMethod>::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(Apply(Applicant{sub, Method}, GetArguments<Args...>(args))));
- } catch (cast_exception) {
- return false;
- } 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 <typename TObjType, typename TSubObject, typename TMethod, typename... Args>
- class TAnyParameterMethodCaller<TObjType, void, TSubObject, TMethod, Args...>: public TSubObjectChecker<TObjType, TSubObject> {
- 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<TSubObject*>(self);
- if (sub == nullptr) {
- return false;
- }
- if (args && (!PyTuple_Check(args) || PyTuple_Size(args) != TFunctionArgs<TMethod>::Length)) {
- return false;
- }
- try {
- class Applicant {
- public:
- void operator()(Args... theArgs) {
- (Sub->*Method)(theArgs...);
- }
- TSubObject* Sub;
- TMethod Method;
- };
- Apply(Applicant{sub, Method}, GetArguments<Args...>(args));
- Py_INCREF(Py_None);
- res = Py_None;
- } catch (cast_exception) {
- return false;
- } catch (...) {
- PyErr_SetString(PyExc_RuntimeError, CurrentExceptionMessage().data());
- return true;
- }
- return true;
- }
- };
- template <typename TResult, typename TSubObject, typename... Args>
- struct TConstTraits {
- typedef TResult (TSubObject::*TMethod)(Args... args) const;
- };
- template <typename TResult, typename TSubObject, typename... Args>
- struct TNonConstTraits {
- typedef TResult (TSubObject::*TMethod)(Args... args);
- };
- template <typename TObjType, typename TResult, typename TSubObject, typename TMethod, typename... Args>
- class TConstMethodCaller: public TAnyParameterMethodCaller<TObjType, TResult, const TSubObject, typename TConstTraits<TResult, TSubObject, Args...>::TMethod, Args...> {
- public:
- TConstMethodCaller(typename TConstTraits<TResult, TSubObject, Args...>::TMethod method)
- : TAnyParameterMethodCaller<TObjType, TResult, const TSubObject, typename TConstTraits<TResult, TSubObject, Args...>::TMethod, Args...>(method)
- {
- }
- };
- template <typename TObjType, typename TResult, typename TSubObject, typename... Args>
- TSimpleSharedPtr<TBaseMethodCaller<TObjType>> CreateConstMethodCaller(TResult (TSubObject::*method)(Args...) const) {
- return new TConstMethodCaller<TObjType, TResult, TSubObject, TResult (TSubObject::*)(Args...) const, Args...>(method);
- }
- template <typename TObjType, typename TResType, typename... Args>
- TSimpleSharedPtr<TBaseMethodCaller<TObjType>> CreateFunctorCaller(std::function<TResType(TObjType&, Args...)> functor) {
- return new TFunctorCaller<TObjType, TResType, Args...>(functor);
- }
- template <typename TObjType, typename TResult, typename TSubObject, typename TMethod, typename... Args>
- class TMethodCaller: public TAnyParameterMethodCaller<TObjType, TResult, TSubObject, typename TNonConstTraits<TResult, TSubObject, Args...>::TMethod, Args...> {
- public:
- TMethodCaller(typename TNonConstTraits<TResult, TSubObject, Args...>::TMethod method)
- : TAnyParameterMethodCaller<TObjType, TResult, TSubObject, typename TNonConstTraits<TResult, TSubObject, Args...>::TMethod, Args...>(method)
- {
- }
- };
- template <typename TObjType, typename TResult, typename TSubObject, typename... Args>
- TSimpleSharedPtr<TBaseMethodCaller<TObjType>> CreateMethodCaller(TResult (TSubObject::*method)(Args...)) {
- return new TMethodCaller<TObjType, TResult, TSubObject, TResult (TSubObject::*)(Args...), Args...>(method);
- }
- }
|