aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp/pybind/method.h
diff options
context:
space:
mode:
authorsay <say@yandex-team.com>2023-02-14 17:24:43 +0300
committersay <say@yandex-team.com>2023-02-14 17:24:43 +0300
commite0094c4ad6964e11564777bc0d859c68d8aa9de2 (patch)
tree5d2ad1a4df88da1f74385888891a2a5f9fbbc3ef /library/cpp/pybind/method.h
parent65a08c9fdece8dba50da8beb4d7c81447211dd45 (diff)
downloadydb-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.h439
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);
+ }
+
+}