aboutsummaryrefslogtreecommitdiffstats
path: root/library/python/runtime_py3/test/subinterpreter/py3_subinterpreters.cpp
diff options
context:
space:
mode:
authorsnermolaev <snermolaev@yandex-team.com>2025-04-29 06:44:21 +0300
committersnermolaev <snermolaev@yandex-team.com>2025-04-29 06:57:18 +0300
commit713adc6a88be8af8a342a9a72a055b7ef6514563 (patch)
tree19dada616a0fd41ead73ad79d463a11c6cd8279d /library/python/runtime_py3/test/subinterpreter/py3_subinterpreters.cpp
parent407f7c0bc156862b8263bccf3eaaf0687ba75f8d (diff)
downloadydb-713adc6a88be8af8a342a9a72a055b7ef6514563.tar.gz
Subinterpretor compatible __res module (2nd attempt)
Cython is not yet subinterpreter compatible. There are no ETA when cython is going to support subinterpreters. This PR removes cython from hermetic python imoprt hooks in order to make them subinterpretr-compatible. commit_hash:427b6f9db6afa6695659ee147621e1ccb391d3cb
Diffstat (limited to 'library/python/runtime_py3/test/subinterpreter/py3_subinterpreters.cpp')
-rw-r--r--library/python/runtime_py3/test/subinterpreter/py3_subinterpreters.cpp82
1 files changed, 82 insertions, 0 deletions
diff --git a/library/python/runtime_py3/test/subinterpreter/py3_subinterpreters.cpp b/library/python/runtime_py3/test/subinterpreter/py3_subinterpreters.cpp
new file mode 100644
index 00000000000..0a934d4db50
--- /dev/null
+++ b/library/python/runtime_py3/test/subinterpreter/py3_subinterpreters.cpp
@@ -0,0 +1,82 @@
+#include "stdout_interceptor.h"
+
+#include <util/stream/str.h>
+
+#include <library/cpp/testing/gtest/gtest.h>
+
+#include <Python.h>
+
+#include <thread>
+#include <algorithm>
+
+struct TSubinterpreters: ::testing::Test {
+ static void SetUpTestSuite() {
+ Py_InitializeEx(0);
+ EXPECT_TRUE(TPyStdoutInterceptor::SetupInterceptionSupport());
+ }
+ static void TearDownTestSuite() {
+ Py_Finalize();
+ }
+
+ static void ThreadPyRun(PyInterpreterState* interp, IOutputStream& pyout, const char* pycode) {
+ PyThreadState* state = PyThreadState_New(interp);
+ PyEval_RestoreThread(state);
+
+ {
+ TPyStdoutInterceptor interceptor{pyout};
+ PyRun_SimpleString(pycode);
+ }
+
+ PyThreadState_Clear(state);
+ PyThreadState_DeleteCurrent();
+ }
+};
+
+TEST_F(TSubinterpreters, NonSubinterpreterFlowStillWorks) {
+ TStringStream pyout;
+ TPyStdoutInterceptor interceptor{pyout};
+
+ PyRun_SimpleString("print('Hello World')");
+ EXPECT_EQ(pyout.Str(), "Hello World\n");
+}
+
+TEST_F(TSubinterpreters, ThreadedSubinterpretersFlowWorks) {
+ TStringStream pyout[2];
+
+ PyInterpreterConfig cfg = {
+ .use_main_obmalloc = 0,
+ .allow_fork = 0,
+ .allow_exec = 0,
+ .allow_threads = 1,
+ .allow_daemon_threads = 0,
+ .check_multi_interp_extensions = 1,
+ .gil = PyInterpreterConfig_OWN_GIL,
+ };
+
+ PyThreadState* mainState = PyThreadState_Get();
+ PyThreadState *sub[2] = {nullptr, nullptr};
+ Py_NewInterpreterFromConfig(&sub[0], &cfg);
+ ASSERT_NE(sub[0], nullptr);
+ Py_NewInterpreterFromConfig(&sub[1], &cfg);
+ ASSERT_NE(sub[1], nullptr);
+ PyThreadState_Swap(mainState);
+
+ PyThreadState* savedState = PyEval_SaveThread();
+ std::array<std::thread, 2> threads{
+ std::thread{ThreadPyRun, sub[0]->interp, std::ref(pyout[0]), "print('Hello Thread 0')"},
+ std::thread{ThreadPyRun, sub[1]->interp, std::ref(pyout[1]), "print('Hello Thread 1')"}
+ };
+ std::ranges::for_each(threads, &std::thread::join);
+ PyEval_RestoreThread(savedState);
+
+ PyThreadState_Swap(sub[0]);
+ Py_EndInterpreter(sub[0]);
+
+ PyThreadState_Swap(sub[1]);
+ Py_EndInterpreter(sub[1]);
+
+ PyThreadState_Swap(mainState);
+
+ EXPECT_EQ(pyout[0].Str(), "Hello Thread 0\n");
+ EXPECT_EQ(pyout[1].Str(), "Hello Thread 1\n");
+}