diff options
author | vitalyisaev <vitalyisaev@ydb.tech> | 2023-11-30 13:26:22 +0300 |
---|---|---|
committer | vitalyisaev <vitalyisaev@ydb.tech> | 2023-11-30 15:44:45 +0300 |
commit | 0a98fece5a9b54f16afeb3a94b3eb3105e9c3962 (patch) | |
tree | 291d72dbd7e9865399f668c84d11ed86fb190bbf /library/cpp/pybind/module.h | |
parent | cb2c8d75065e5b3c47094067cb4aa407d4813298 (diff) | |
download | ydb-0a98fece5a9b54f16afeb3a94b3eb3105e9c3962.tar.gz |
YQ Connector:Use docker-compose in integrational tests
Diffstat (limited to 'library/cpp/pybind/module.h')
-rw-r--r-- | library/cpp/pybind/module.h | 176 |
1 files changed, 176 insertions, 0 deletions
diff --git a/library/cpp/pybind/module.h b/library/cpp/pybind/module.h new file mode 100644 index 0000000000..41dcb4dfec --- /dev/null +++ b/library/cpp/pybind/module.h @@ -0,0 +1,176 @@ +#pragma once + +#define PY_SSIZE_T_CLEAN +#include <Python.h> +#include "ptr.h" +#include "cast.h" +#include "exceptions.h" + +#include <util/generic/function.h> + +namespace NPyBind { +#if PY_MAJOR_VERSION >= 3 + namespace NPrivate { + using TFinalizationCallBack = std::function<void()>; + void AddFinalizationCallBack(TFinalizationCallBack); + class TAtExitRegistrar: private TNonCopyable { + TAtExitRegistrar(TPyObjectPtr module); + public: + static void Instantiate(TPyObjectPtr module) { + static TAtExitRegistrar registrar(module); + Y_UNUSED(registrar); + } + }; + + class TPyBindModuleRegistrar: private TNonCopyable { + TPyBindModuleRegistrar(); + TPyObjectPtr Module; + public: + static void Instantiate() { + static TPyBindModuleRegistrar registrar; + Y_UNUSED(registrar); + } + }; + } //NPrivate +#endif + + class TModuleHolder { + private: + TModuleHolder(const TModuleHolder&); + TModuleHolder& operator=(const TModuleHolder&); + + TModuleHolder(); + private: + typedef PyCFunction TModuleMethod; +#if PY_MAJOR_VERSION >= 3 + typedef PyObject* (*TModuleInitFunc)(); +#else + typedef void (*TModuleInitFunc)(); +#endif + + struct TMethodDef { + TString Name; + TModuleMethod Method; + TString Description; + int Flags; + + TMethodDef(const TString& name, TModuleMethod method, const TString& descr, int flags) + : Name(name) + , Method(method) + , Description(descr) + , Flags(flags) + { + } + + operator PyMethodDef() const { + PyMethodDef cur = {Name.c_str(), Method, Flags, Description.c_str()}; + return cur; + } + }; + + typedef TSimpleSharedPtr<TVector<TMethodDef>> TMethodDefVecPtr; + typedef TSimpleSharedPtr<TVector<PyMethodDef>> TPyMethodDefVecPtr; + + TVector<TMethodDefVecPtr> Methods; + TVector<TPyMethodDefVecPtr> Defs; +#if PY_MAJOR_VERSION >= 3 + //because the md_name will leak otherwise + class TPyModuleDefWithName { + PyModuleDef Def; + TString Name; + public: + explicit TPyModuleDefWithName(TString name, TPyMethodDefVecPtr moduleDefs) + : Name(std::move(name)) + { + Def = PyModuleDef{ + PyModuleDef_HEAD_INIT, + Name.c_str(), + nullptr, + -1, + moduleDefs->data(), + nullptr, nullptr, nullptr, nullptr + }; + } + PyModuleDef* GetDefPtr() { + return &Def; + } + + }; + TVector<TSimpleSharedPtr<TPyModuleDefWithName>> ModuleDefs; +#endif + + template <TModuleMethod method> + static PyObject* MethodWrapper(PyObject* obj, PyObject* args) { + try { + PyObject* res = method(obj, args); + if (!res && !PyErr_Occurred()) + ythrow yexception() << "\nModule method exited with NULL, but didn't set Error.\n Options:\n -- Return correct value or None;\n -- Set python exception;\n -- Throw c++ exception."; + return res; + } catch (const std::exception& ex) { + PyErr_SetString(TExceptionsHolder::Instance().ToPyException(ex).Get(), ex.what()); + } catch (...) { + PyErr_SetString(PyExc_RuntimeError, "Unknown error occurred while trying to call module method"); + } + return nullptr; + } + + public: + static TModuleHolder& Instance() { + static TModuleHolder Holder; + return Holder; + } + + void ImportModule(TPyObjectPtr module, const char* const name, TModuleInitFunc initFunc) { + PyImport_AppendInittab(const_cast<char*>(name), initFunc); + TPyObjectPtr importedModule(PyImport_ImportModule(name), true); + PyModule_AddObject(module.Get(), name, importedModule.Get()); + } + + template <TModuleMethod method> + void AddModuleMethod(const TString& name, const TString& descr = "") { + Methods.back()->push_back(TMethodDef(name, MethodWrapper<method>, descr, METH_VARARGS)); + } + + TPyObjectPtr InitModule(const TString& name) { + Defs.push_back(new TVector<PyMethodDef>(Methods.back()->begin(), Methods.back()->end())); + PyMethodDef blank = {nullptr, nullptr, 0, nullptr}; + Defs.back()->push_back(blank); +#if PY_MAJOR_VERSION >= 3 + ModuleDefs.push_back(MakeSimpleShared<TPyModuleDefWithName>(name, Defs.back())); + TPyObjectPtr res(PyModule_Create(ModuleDefs.back()->GetDefPtr())); + NPrivate::TAtExitRegistrar::Instantiate(res); + NPrivate::TPyBindModuleRegistrar::Instantiate(); +#else + TPyObjectPtr res(Py_InitModule(name.c_str(), &(Defs.back()->at(0)))); +#endif + Methods.push_back(new TVector<TMethodDef>); + return res; + } + }; + + template <typename TMethodSignature, TMethodSignature method> + class TModuleMethodCaller { + private: + template <typename TResult, typename... Args> + struct TCaller { + static PyObject* Call(PyObject* args) { + return BuildPyObject(Apply(method, GetArguments<Args...>(args))); + } + }; + + template <typename TResult, typename... Args> + static PyObject* InternalCall(TResult (*)(Args...), PyObject* args) { + return BuildPyObject(Apply(method, GetArguments<Args...>(args))); + } + + public: + static PyObject* Call(PyObject*, PyObject* args) { + if (args && (!PyTuple_Check(args) || PyTuple_Size(args) != TFunctionArgs<TMethodSignature>::Length)) { + ythrow yexception() << "Method takes " << (size_t)(TFunctionArgs<TMethodSignature>::Length) << " arguments, " << PyTuple_Size(args) << " provided"; + } + + return InternalCall(method, args); + } + }; + +} |