diff options
author | say <say@yandex-team.com> | 2023-02-14 17:24:43 +0300 |
---|---|---|
committer | say <say@yandex-team.com> | 2023-02-14 17:24:43 +0300 |
commit | e0094c4ad6964e11564777bc0d859c68d8aa9de2 (patch) | |
tree | 5d2ad1a4df88da1f74385888891a2a5f9fbbc3ef /library/cpp/pybind/method.h | |
parent | 65a08c9fdece8dba50da8beb4d7c81447211dd45 (diff) | |
download | ydb-e0094c4ad6964e11564777bc0d859c68d8aa9de2.tar.gz |
Migrate black linter on custom_lint pipeline
Diffstat (limited to 'library/cpp/pybind/method.h')
-rw-r--r-- | library/cpp/pybind/method.h | 439 |
1 files changed, 439 insertions, 0 deletions
diff --git a/library/cpp/pybind/method.h b/library/cpp/pybind/method.h new file mode 100644 index 0000000000..7c1f6e90e1 --- /dev/null +++ b/library/cpp/pybind/method.h @@ -0,0 +1,439 @@ +#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); + } + +} |