From e7d12e70ff73c9105d4f24da3791c80c6d02e520 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20N=C3=B6then?= Date: Thu, 7 May 2026 16:04:53 +0200 Subject: [PATCH] Fix Python ownership detection and stabilize GIL handling for binding mode --- src/module/utilities/gtpy_contextmanager.cpp | 48 ++++++++++++++++---- src/module/utilities/gtpy_globals.h | 8 ++++ 2 files changed, 48 insertions(+), 8 deletions(-) diff --git a/src/module/utilities/gtpy_contextmanager.cpp b/src/module/utilities/gtpy_contextmanager.cpp index 0627068..136aa9d 100644 --- a/src/module/utilities/gtpy_contextmanager.cpp +++ b/src/module/utilities/gtpy_contextmanager.cpp @@ -63,6 +63,38 @@ namespace { +void ensurePythonOwnerEnvVar() +{ + if (qEnvironmentVariableIsSet(gtpy::constants::env::PYTHON_OWNER_ENV_VAR)) + { + return; + } + + auto exeBaseName = QFileInfo(QCoreApplication::applicationFilePath()) + .completeBaseName() + .toLower(); + + bool gtlabOwnsPython = (exeBaseName == QStringLiteral("gtlab") || + exeBaseName == QStringLiteral("gtlabconsole")); + + qputenv(gtpy::constants::env::PYTHON_OWNER_ENV_VAR, + gtlabOwnsPython + ? gtpy::constants::env::PYTHON_OWNER_GTLAB + : gtpy::constants::env::PYTHON_OWNER_EXTERNAL); +} + +bool isGtlabPythonOwner() +{ + ensurePythonOwnerEnvVar(); + + auto owner = qEnvironmentVariable( + gtpy::constants::env::PYTHON_OWNER_ENV_VAR) + .trimmed() + .toLower(); + + return owner == QLatin1String(gtpy::constants::env::PYTHON_OWNER_GTLAB); +} + GtpyContext::ContextType contextTypeEnumConvert( GtpyContextManager::Context type) { @@ -140,15 +172,15 @@ GtpyContextManager::GtpyContextManager(QObject* parent) : dlopen(PYTHON_LIBRARY, RTLD_LAZY | RTLD_GLOBAL); #endif - // In the embedded case, we are owning the python interpreter, so we - // need to create it first. - // - // When the python module is running from inside a normal python interpreter - // we are not allowed to reinit. Also, we don't want to redirect std out in this case - const bool pythonAlreadyInitialized = (Py_IsInitialized() != 0); - m_ownsPythonInterpreter = !pythonAlreadyInitialized; + // Determine interpreter ownership via GTLAB_PYTHON_INTERPRETER_OWNER. + // Ownership controls whether we initialize Python in embedded mode + // or attach to an externally hosted Python interpreter. + m_ownsPythonInterpreter = isGtlabPythonOwner(); - int pythonQtFlags = m_ownsPythonInterpreter ? PythonQt::RedirectStdOut : PythonQt::PythonAlreadyInitialized; + const bool pythonAlreadyInitialized = (Py_IsInitialized() != 0); + int pythonQtFlags = pythonAlreadyInitialized + ? PythonQt::PythonAlreadyInitialized + : PythonQt::RedirectStdOut; PythonQt::init(pythonQtFlags); diff --git a/src/module/utilities/gtpy_globals.h b/src/module/utilities/gtpy_globals.h index 7705f5e..ea6d2a4 100644 --- a/src/module/utilities/gtpy_globals.h +++ b/src/module/utilities/gtpy_globals.h @@ -27,6 +27,14 @@ constexpr const char* COLLECTION_CAT = "category"; constexpr const char* COLLECTION_SUBCAT = "subcategory"; constexpr const char* PROJ_PY_SCRIPTS_DIR = "scripts/python"; +/// Environment +namespace env +{ +constexpr const char* PYTHON_OWNER_ENV_VAR = "GTLAB_PYTHON_INTERPRETER_OWNER"; +constexpr const char* PYTHON_OWNER_GTLAB = "gtlab"; +constexpr const char* PYTHON_OWNER_EXTERNAL = "external"; +} + } // namespace constants } // namespace gtpy