aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp/pybind/module.h
diff options
context:
space:
mode:
authorvitalyisaev <vitalyisaev@ydb.tech>2023-11-30 13:26:22 +0300
committervitalyisaev <vitalyisaev@ydb.tech>2023-11-30 15:44:45 +0300
commit0a98fece5a9b54f16afeb3a94b3eb3105e9c3962 (patch)
tree291d72dbd7e9865399f668c84d11ed86fb190bbf /library/cpp/pybind/module.h
parentcb2c8d75065e5b3c47094067cb4aa407d4813298 (diff)
downloadydb-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.h176
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);
+ }
+ };
+
+}