aboutsummaryrefslogtreecommitdiffstats
path: root/yql/essentials/udfs/common/python/bindings/py_yql_module.cpp
diff options
context:
space:
mode:
authorimunkin <imunkin@yandex-team.com>2024-11-08 10:00:23 +0300
committerimunkin <imunkin@yandex-team.com>2024-11-08 10:12:13 +0300
commita784a2f943d6e15caa6241e2e96d80aac6dbf375 (patch)
tree05f1e5366c916b988a8afb75bdab8ddeee0f6e6d /yql/essentials/udfs/common/python/bindings/py_yql_module.cpp
parentd70137a7b530ccaa52834274913bbb5a3d1ca06e (diff)
downloadydb-a784a2f943d6e15caa6241e2e96d80aac6dbf375.tar.gz
Move yql/udfs/common/ to /yql/essentials YQL-19206
Except the following directories: * clickhouse/client * datetime * knn * roaring commit_hash:c7da95636144d28db109d6b17ddc762e9bacb59f
Diffstat (limited to 'yql/essentials/udfs/common/python/bindings/py_yql_module.cpp')
-rw-r--r--yql/essentials/udfs/common/python/bindings/py_yql_module.cpp251
1 files changed, 251 insertions, 0 deletions
diff --git a/yql/essentials/udfs/common/python/bindings/py_yql_module.cpp b/yql/essentials/udfs/common/python/bindings/py_yql_module.cpp
new file mode 100644
index 0000000000..5d1497f7c7
--- /dev/null
+++ b/yql/essentials/udfs/common/python/bindings/py_yql_module.cpp
@@ -0,0 +1,251 @@
+#include "py_yql_module.h"
+
+#include "py_void.h"
+#include "py_iterator.h"
+#include "py_list.h"
+#include "py_dict.h"
+#include "py_stream.h"
+#include "py_utils.h"
+#include "py_callable.h"
+
+#include <library/cpp/resource/resource.h>
+#include <yql/essentials/udfs/common/python/python_udf/python_udf.h>
+
+namespace NPython {
+
+static PyMethodDef ModuleMethods[] = {
+ { nullptr, nullptr, 0, nullptr } /* sentinel */
+};
+
+#define MODULE_NAME "yql"
+
+#if PY_MAJOR_VERSION >= 3
+#define MODULE_NAME_TYPING "yql.typing"
+#endif
+
+#define MODULE_INITIALIZED_ATTRIBUTE "_initialized"
+
+PyDoc_STRVAR(ModuleDoc,
+ "This module provides YQL specific types for Python.");
+
+#if PY_MAJOR_VERSION >= 3
+PyDoc_STRVAR(ModuleDocTyping,
+ "This module provides annotations for YQL types for Python.");
+#endif
+
+PyDoc_STRVAR(StopIterationException_doc,
+ "Can be throwed to yield stream iteration.");
+
+#define PREPARE_TYPE(Name, Type) \
+ do { \
+ if (PyType_Ready(Type) < 0) { \
+ throw yexception() << "Can't prepare type: " << (Name); \
+ } \
+ } while (0)
+
+#define REGISTER_TYPE(Name, Type) \
+ do { \
+ PREPARE_TYPE(Name, Type); \
+ Py_INCREF(Type); \
+ if (PyModule_AddObject(module, (Name), (PyObject*) Type) < 0) { \
+ throw yexception() << "Can't add type: " << (Name); \
+ } \
+ } while (0)
+
+#define REGISTER_OBJECT(Name, Object) \
+ do { \
+ if (PyDict_SetItemString(dict, (Name), (PyObject *) (Object)) < 0) \
+ throw yexception() << "Can't register object: " << (Name); \
+ } while (0)
+
+#define REGISTER_EXCEPTION(Name, Object, Doc) \
+ do { \
+ if (!Object) { \
+ Object = PyErr_NewExceptionWithDoc((char*) MODULE_NAME "." Name, Doc, nullptr, nullptr); \
+ if (!Object) { \
+ throw yexception() << "Can't register exception: " << (Name); \
+ } \
+ REGISTER_OBJECT(Name, Object); \
+ } \
+ } while (0)
+
+#if PY_MAJOR_VERSION >= 3
+static PyModuleDef ModuleDefinition = {
+ PyModuleDef_HEAD_INIT,
+ INIT_MEMBER(m_name, MODULE_NAME),
+ INIT_MEMBER(m_doc, ModuleDoc),
+ INIT_MEMBER(m_size, -1),
+ INIT_MEMBER(m_methods, ModuleMethods),
+ INIT_MEMBER(m_slots, nullptr),
+ INIT_MEMBER(m_traverse, nullptr),
+ INIT_MEMBER(m_clear, nullptr),
+ INIT_MEMBER(m_free, nullptr),
+};
+
+static PyModuleDef ModuleDefinitionTyping = {
+ PyModuleDef_HEAD_INIT,
+ INIT_MEMBER(m_name, MODULE_NAME_TYPING),
+ INIT_MEMBER(m_doc, ModuleDocTyping),
+ INIT_MEMBER(m_size, -1),
+ INIT_MEMBER(m_methods, nullptr),
+ INIT_MEMBER(m_slots, nullptr),
+ INIT_MEMBER(m_traverse, nullptr),
+ INIT_MEMBER(m_clear, nullptr),
+ INIT_MEMBER(m_free, nullptr),
+};
+
+PyMODINIT_FUNC PyInit_YQL(void)
+{
+ auto mod = PyModule_Create(&ModuleDefinition);
+ PyModule_AddObject(mod, "__path__", Py_BuildValue("()"));
+ return mod;
+}
+
+void go_throw();
+
+PyMODINIT_FUNC PyInit_YQLTyping(void)
+{
+ return PyModule_Create(&ModuleDefinitionTyping);
+}
+#else
+PyMODINIT_FUNC PyInit_YQL(void)
+{
+ Py_InitModule3(MODULE_NAME, ModuleMethods, ModuleDoc);
+}
+#endif
+
+void PrepareYqlModule() {
+ PyImport_AppendInittab(MODULE_NAME, &PyInit_YQL);
+#if PY_MAJOR_VERSION >= 3
+ PyImport_AppendInittab(MODULE_NAME_TYPING, &PyInit_YQLTyping);
+#endif
+}
+
+#if PY_MAJOR_VERSION >= 3
+void RegisterRuntimeModule(const char* name, PyObject* module) {
+ if (!module || !PyModule_Check(module)) {
+ throw yexception() << "Invalid object for module " << name;
+ }
+
+ // borrowed reference
+ PyObject* modules = PyImport_GetModuleDict();
+ if (!modules || !PyDict_CheckExact(modules)) {
+ throw yexception() << "Can't get sys.modules dictionary";
+ }
+
+ if (PyDict_SetItemString(modules, name, module) < 0) {
+ throw yexception() << "Can't register module " << name;
+ }
+}
+#endif
+
+void InitYqlModule(NYql::NUdf::EPythonFlavor pythonFlavor, bool standalone) {
+ TPyObjectPtr m = PyImport_ImportModule(MODULE_NAME);
+ if (!standalone && !m) {
+ PyErr_Clear();
+#if PY_MAJOR_VERSION >= 3
+ m = PyInit_YQL();
+ RegisterRuntimeModule(MODULE_NAME, m.Get());
+#else
+ PyInit_YQL();
+#endif
+ m = PyImport_ImportModule(MODULE_NAME);
+ }
+
+ PyObject* module = m.Get();
+
+ if (!module) {
+ throw yexception() << "Can't get YQL module.";
+ }
+
+ TPyObjectPtr initialized = PyObject_GetAttrString(module, MODULE_INITIALIZED_ATTRIBUTE);
+ if (!initialized) {
+ PyErr_Clear();
+ } else if (initialized.Get() == Py_True) {
+ return;
+ }
+
+ PyObject* dict = PyModule_GetDict(module);
+
+ REGISTER_TYPE("TVoid", &PyVoidType);
+ REGISTER_OBJECT("Void", &PyVoidObject);
+
+ PREPARE_TYPE("TIterator", &PyIteratorType);
+ PREPARE_TYPE("TPairIterator", &PyPairIteratorType);
+
+ PREPARE_TYPE("TDict", &PyLazyDictType);
+ PREPARE_TYPE("TSet", &PyLazySetType);
+
+ PREPARE_TYPE("TLazyListIterator", &PyLazyListIteratorType);
+ PREPARE_TYPE("TLazyList", &PyLazyListType);
+ PREPARE_TYPE("TThinListIterator", &PyThinListIteratorType);
+ PREPARE_TYPE("TThinList", &PyThinListType);
+
+ PREPARE_TYPE("TStream", &PyStreamType);
+ PREPARE_TYPE("TCallable", &PyCallableType);
+
+ REGISTER_EXCEPTION("TYieldIteration", PyYieldIterationException, StopIterationException_doc);
+
+#if PY_MAJOR_VERSION >= 3
+ if (pythonFlavor == NYql::NUdf::EPythonFlavor::Arcadia) {
+ if (!standalone) {
+ TPyObjectPtr typingModule = PyImport_ImportModule(MODULE_NAME_TYPING);
+ if (!typingModule) {
+ PyErr_Clear();
+ typingModule = PyInit_YQLTyping();
+ RegisterRuntimeModule(MODULE_NAME_TYPING, typingModule.Get());
+ }
+ }
+
+ const auto typing = NResource::Find(TStringBuf("typing.py"));
+ const auto rc = PyRun_SimpleStringFlags(typing.c_str(), nullptr);
+
+ if (rc < 0) {
+ // Not sure if PyErr_Print() works after PyRun_SimpleStringFlags,
+ // but just in case...
+ PyErr_Print();
+ ythrow yexception() << "Can't parse YQL type annotations module";
+ }
+
+ auto processError = [&] (PyObject* obj, TStringBuf message) {
+ if (obj) {
+ return;
+ }
+ PyObject *ptype, *pvalue, *ptraceback;
+ PyErr_Fetch(&ptype, &pvalue, &ptraceback);
+ if (pvalue) {
+ auto pstr = PyObject_Str(pvalue);
+ if (pstr) {
+ if (auto err_msg = PyUnicode_AsUTF8(pstr)) {
+ Cerr << err_msg << Endl;
+ }
+ }
+ PyErr_Restore(ptype, pvalue, ptraceback);
+ }
+ ythrow yexception() << "Can't setup YQL type annotations module: " << message;
+ };
+
+ auto main = PyImport_ImportModule("__main__");
+ processError(main, "PyImport_ImportModule");
+ auto function = PyObject_GetAttrString(main, "main");
+ processError(function, "PyObject_GetAttrString");
+ auto args = PyTuple_New(0);
+ processError(args, "PyTuple_New");
+ auto result = PyObject_CallObject(function, args);
+ processError(result, "PyObject_CallObject");
+
+ Py_DECREF(result);
+ Py_DECREF(args);
+ Py_DECREF(function);
+ Py_DECREF(main);
+ }
+#endif
+
+ REGISTER_OBJECT(MODULE_INITIALIZED_ATTRIBUTE, Py_True);
+}
+
+void TermYqlModule() {
+ PyYieldIterationException = nullptr;
+}
+
+} // namspace NPython