This is an automated email from the ASF dual-hosted git repository.

szaszm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi-minifi-cpp.git

commit e62b5bb1511882f93f3caf1f38fb6ed8c647b4d5
Author: Marton Szasz <[email protected]>
AuthorDate: Mon Oct 28 16:44:03 2024 +0100

    MINIFICPP-2480 workaround python version incompatibilities around 3.6 vs 
3.11
    
    Closes #1883
    
    Signed-off-by: Marton Szasz <[email protected]>
---
 extensions/python/PythonInterpreter.cpp | 54 +++++++++++++++++++++++++++------
 1 file changed, 45 insertions(+), 9 deletions(-)

diff --git a/extensions/python/PythonInterpreter.cpp 
b/extensions/python/PythonInterpreter.cpp
index 267db16fd..212aea705 100644
--- a/extensions/python/PythonInterpreter.cpp
+++ b/extensions/python/PythonInterpreter.cpp
@@ -18,6 +18,16 @@
 
 #include "PythonBindings.h"
 
+#ifndef WIN32
+#if !defined(__APPLE__) && !defined(_GNU_SOURCE)
+#define _GNU_SOURCE  // NOLINT: for RTLD_DEFAULT (source: `man dlsym` on linux)
+#endif  // !__APPLE__ && !_GNU_SOURCE
+// on Apple, RTLD_DEFAULT is defined without needing any macros (source: `man 
dlsym` on macOS)
+#include <dlfcn.h>
+#endif  // !WIN32
+
+#include <regex>
+
 namespace org::apache::nifi::minifi::extensions::python {
 
 Interpreter* Interpreter::getInterpreter() {
@@ -34,30 +44,56 @@ GlobalInterpreterLock::~GlobalInterpreterLock() {
 }
 
 namespace {
+struct version {
+  int major;
+  int minor;
+};
+
+std::optional<version> getPythonVersion() {
+  // example version: "3.0a5+ (py3k:63103M, May 12 2008, 00:53:55) \n[GCC 
4.2.3]"
+  //                  "3.12.6 (main, Sep  8 2024, 13:18:56) [GCC 14.2.1 
20240805]"
+  std::string ver_str = Py_GetVersion();
+  std::smatch match;
+  if (std::regex_search(ver_str, match, std::regex{R"(^(\d+)\.(\d+))"})) {
+    return version{std::stoi(match[1]), std::stoi(match[2])};
+  } else {
+    return std::nullopt;
+  }
+}
+
 // PyEval_InitThreads might be marked deprecated (depending on the version of 
Python.h)
-// Python <= 3.6: This needs to be called manually after Py_Initialize to 
initialize threads
+// Python <= 3.6: This needs to be called manually after Py_Initialize to 
initialize threads (python < 3.6 is unsupported by us)
 // Python >= 3.7: Noop function since its functionality is included in 
Py_Initialize
 // Python >= 3.9: Marked as deprecated (still noop)
+// Python >= 3.11: removed
 // This can be removed if we drop the support for Python 3.6
 void initThreads() {
+  using namespace std::literals;
+  // early return (skip workaround) above Python 3.6
+  if (const auto version = getPythonVersion(); !version || (version->major == 
3 && version->minor > 6) || version->major > 3) {
+    return;
+  }
 #if defined(__clang__)
-  #pragma clang diagnostic push
+#pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
 #elif defined(__GNUC__)
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-#elif defined(WIN32)
-  #pragma warning(push)
-#pragma warning(disable: 4996)
 #endif
-  if (!PyEval_ThreadsInitialized())
-    PyEval_InitThreads();
+#ifndef WIN32  // dlsym hack, doesn't work on windows
+  // dlsym hack: allows us to build with python 3.11+, where these were 
removed (so no header declarations), and run with python 3.6 (e.g. RHEL8)
+  // the dlsym hack doesn't work on Windows, we'll drop python 3.6 support 
there
+  // lowercase, to avoid name conflicts with the header declaration, in case 
we're using an old enough python to build
+  const auto pyeval_threads_initialized = (int (*)())dlsym(RTLD_DEFAULT, 
"PyEval_ThreadsInitialized");  // NOLINT: C-style cast for POSIX-guaranteed 
dataptr -> funcptr conversion in dlsym
+  const auto pyeval_initthreads = (void (*)())dlsym(RTLD_DEFAULT, 
"PyEval_InitThreads");  // NOLINT: C-style cast for POSIX-guaranteed dataptr -> 
funcptr conversion in dlsym
+  gsl_Assert(pyeval_threads_initialized && pyeval_initthreads && "We're on 
python 3.6, yet we couldn't load PyEval_ThreadsInitialized and/or 
PyEval_InitThreads");
+  if (!pyeval_threads_initialized())
+    pyeval_initthreads();
+#endif  // !WIN32
 #if defined(__clang__)
 #pragma clang diagnostic pop
 #elif defined(__GNUC__)
 #pragma GCC diagnostic pop
-#elif defined(WIN32)
-#pragma warning(pop)
 #endif
 }
 

Reply via email to