diff options
| author | SulfurNitride <SulfurNitride@users.noreply.github.com> | 2026-02-19 17:25:48 -0600 |
|---|---|---|
| committer | SulfurNitride <SulfurNitride@users.noreply.github.com> | 2026-02-19 17:25:48 -0600 |
| commit | 5467e210668eb24de9a4429ef0ddcbc6442e8932 (patch) | |
| tree | 6c672183779c800963d73db5bee6742bf08c65fd /libs | |
| parent | 6d70b7fa6a9cdf8ea59c7aa98f92249c5a4e0060 (diff) | |
Add Python init diagnostics, AppImage build system, misc Linux fixes
- python: switch Py_InitializeEx → Py_InitializeFromConfig for explicit
error reporting; add dladdr + fprintf diagnostics to trace which DSO
Python symbols resolve to and whether Py_IsInitialized succeeds
- python: log RTLD_NOLOAD vs fresh-load outcome when promoting libpython
to RTLD_GLOBAL
- build: add Docker-based AppImage build script (build.sh), update
Dockerfile and build-inner.sh for AppImage-only workflow
- build: remove Flatpak support entirely; move AppImage metadata
(desktop/metainfo/icon) from flatpak/ to data/
- Add Monster Hunter Wilds basic_games plugin
- .gitignore: exclude squashfs-root, build-source, flatpak-repo,
__pycache__, fomod-plus-settings.ini
- Various Linux port fixes across wineprefix, processrunner, proton
launcher, FUSE connector, instance manager, download manager,
sanitychecks, and NaK Rust paths/runtime_wrap
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'libs')
| -rw-r--r-- | libs/basic_games/games/baldursgate3/pak_parser.py | 21 | ||||
| -rw-r--r-- | libs/basic_games/games/game_monsterhunterwilds.py | 15 | ||||
| -rw-r--r-- | libs/game_bethesda/src/*.cpp | 0 | ||||
| -rw-r--r-- | libs/game_bethesda/src/gamebryo/gamebryolocalsavegames.cpp | 4 | ||||
| -rw-r--r-- | libs/game_bethesda/src/gamebryo/gamegamebryo.cpp | 6 | ||||
| -rw-r--r-- | libs/game_gamebryo/src/gamebryo/gamebryolocalsavegames.cpp | 4 | ||||
| -rw-r--r-- | libs/installer_fomod_plus/installer/FomodPlusInstaller.cpp | 2 | ||||
| -rw-r--r-- | libs/installer_fomod_plus/installer/lib/Logger.h | 2 | ||||
| -rw-r--r-- | libs/installer_fomod_plus/installer/ui/UIHelper.cpp | 1 | ||||
| -rw-r--r-- | libs/installer_fomod_plus/patchwizard/lib/PatchFinder.cpp | 1 | ||||
| -rw-r--r-- | libs/installer_fomod_plus/scanner/archiveparser.h | 1 | ||||
| -rw-r--r-- | libs/installer_fomod_plus/share/FOMODData/FomodDB.h | 2 | ||||
| -rw-r--r-- | libs/nak/src/paths.rs | 6 | ||||
| -rw-r--r-- | libs/nak/src/runtime_wrap.rs | 36 | ||||
| -rw-r--r-- | libs/plugin_python/src/proxy/proxypython.cpp | 7 | ||||
| -rw-r--r-- | libs/plugin_python/src/runner/pythonrunner.cpp | 178 |
16 files changed, 164 insertions, 122 deletions
diff --git a/libs/basic_games/games/baldursgate3/pak_parser.py b/libs/basic_games/games/baldursgate3/pak_parser.py index 4ebc83c..533c72e 100644 --- a/libs/basic_games/games/baldursgate3/pak_parser.py +++ b/libs/basic_games/games/baldursgate3/pak_parser.py @@ -281,21 +281,12 @@ class BG3PakParser: except Exception: pass - if os.path.exists("/.flatpak-info"): - # In Flatpak, host wine binaries can't execute directly - # (missing host libraries). Use flatpak-spawn to run on host. - spawn_cmd = ["flatpak-spawn", "--host"] - if wine_prefix: - spawn_cmd.append(f"--env=WINEPREFIX={wine_prefix}") - spawn_cmd.extend(["bash", "-c", command]) - result = subprocess.run(spawn_cmd, **kwargs) - else: - env = os.environ.copy() - if wine_prefix: - env["WINEPREFIX"] = wine_prefix - kwargs["env"] = env - kwargs["shell"] = True - result = subprocess.run(command, **kwargs) + env = os.environ.copy() + if wine_prefix: + env["WINEPREFIX"] = wine_prefix + kwargs["env"] = env + kwargs["shell"] = True + result = subprocess.run(command, **kwargs) if result.returncode: qWarning( diff --git a/libs/basic_games/games/game_monsterhunterwilds.py b/libs/basic_games/games/game_monsterhunterwilds.py new file mode 100644 index 0000000..419377c --- /dev/null +++ b/libs/basic_games/games/game_monsterhunterwilds.py @@ -0,0 +1,15 @@ +from ..basic_game import BasicGame + + +class MonsterHunterWildsGame(BasicGame): + Name = "Monster Hunter: Wilds Support Plugin" + Author = "AbyssDragnonModding" + Version = "1.0.0" + + GameName = "Monster Hunter: Wilds" + GameShortName = "monsterhunterwilds" + GameBinary = "MonsterHunterWilds.exe" + GameDataPath = "%GAME_PATH%" + GameSaveExtension = "bin" + GameNexusId = 6993 + GameSteamId = 2246340 diff --git a/libs/game_bethesda/src/*.cpp b/libs/game_bethesda/src/*.cpp new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/libs/game_bethesda/src/*.cpp diff --git a/libs/game_bethesda/src/gamebryo/gamebryolocalsavegames.cpp b/libs/game_bethesda/src/gamebryo/gamebryolocalsavegames.cpp index e4df0ea..4c1199c 100644 --- a/libs/game_bethesda/src/gamebryo/gamebryolocalsavegames.cpp +++ b/libs/game_bethesda/src/gamebryo/gamebryolocalsavegames.cpp @@ -56,7 +56,9 @@ QString GamebryoLocalSavegames::localSavesDummy() const QDir GamebryoLocalSavegames::localSavesDirectory() const { - return QDir(m_Game->myGamesPath()).absoluteFilePath(localSavesDummy()); + QString dummy = localSavesDummy(); + dummy.replace("\\", "/"); + return QDir(m_Game->myGamesPath()).absoluteFilePath(dummy); } QDir GamebryoLocalSavegames::localGameDirectory() const diff --git a/libs/game_bethesda/src/gamebryo/gamegamebryo.cpp b/libs/game_bethesda/src/gamebryo/gamegamebryo.cpp index b53d823..4a92440 100644 --- a/libs/game_bethesda/src/gamebryo/gamegamebryo.cpp +++ b/libs/game_bethesda/src/gamebryo/gamegamebryo.cpp @@ -494,11 +494,7 @@ QString GameGamebryo::myGamesPath() const return findInRegistry(HKEY_LOCAL_MACHINE, L"Software\\LOOT", L"Installed Path") + "/Loot.exe"; #else - // On Linux, look for loot in common locations - QString flatpakPath = QDir::homePath() + "/.var/app/io.github.loot.loot/data/LOOT/Loot"; - if (QFileInfo(flatpakPath).exists()) - return flatpakPath; - // Try system PATH + // On Linux, look for loot in PATH QString systemLoot = QStandardPaths::findExecutable("loot"); if (!systemLoot.isEmpty()) return systemLoot; diff --git a/libs/game_gamebryo/src/gamebryo/gamebryolocalsavegames.cpp b/libs/game_gamebryo/src/gamebryo/gamebryolocalsavegames.cpp index cf78eba..d0cc2ef 100644 --- a/libs/game_gamebryo/src/gamebryo/gamebryolocalsavegames.cpp +++ b/libs/game_gamebryo/src/gamebryo/gamebryolocalsavegames.cpp @@ -44,7 +44,9 @@ QString GamebryoLocalSavegames::localSavesDummy() const QDir GamebryoLocalSavegames::localSavesDirectory() const { - return QDir(m_Game->myGamesPath()).absoluteFilePath(localSavesDummy()); + QString dummy = localSavesDummy(); + dummy.replace("\\", "/"); + return QDir(m_Game->myGamesPath()).absoluteFilePath(dummy); } QDir GamebryoLocalSavegames::localGameDirectory() const diff --git a/libs/installer_fomod_plus/installer/FomodPlusInstaller.cpp b/libs/installer_fomod_plus/installer/FomodPlusInstaller.cpp index 690bdbb..c748495 100644 --- a/libs/installer_fomod_plus/installer/FomodPlusInstaller.cpp +++ b/libs/installer_fomod_plus/installer/FomodPlusInstaller.cpp @@ -34,8 +34,6 @@ bool FomodPlusInstaller::init(IOrganizer* organizer) mOrganizer = organizer; mFomodContent = make_shared<FomodDataContent>(organizer); log.setLogFilePath(QDir::currentPath().toStdString() + "/logs/fomodplus.log"); - std::cout << "QDir::currentPath(): " << QDir::currentPath().toStdString() << std::endl; - std::cout << "mOrganizer->basePath() : " << mOrganizer->basePath().toStdString() << std::endl; // REMEMBER: This mFomodDB persists beyond the scope of an individual install. Do not do anything nasty to it. mFomodDb = std::make_unique<FomodDB>(mOrganizer->basePath().toStdString()); diff --git a/libs/installer_fomod_plus/installer/lib/Logger.h b/libs/installer_fomod_plus/installer/lib/Logger.h index fdeb1f0..53fb7d9 100644 --- a/libs/installer_fomod_plus/installer/lib/Logger.h +++ b/libs/installer_fomod_plus/installer/lib/Logger.h @@ -90,8 +90,6 @@ public: if (mLogFile.is_open()) { writeLog(mLogFile); } - - writeLog(std::cout); } Logger& operator=(const Logger&) = delete; diff --git a/libs/installer_fomod_plus/installer/ui/UIHelper.cpp b/libs/installer_fomod_plus/installer/ui/UIHelper.cpp index 56aff85..5f34e5d 100644 --- a/libs/installer_fomod_plus/installer/ui/UIHelper.cpp +++ b/libs/installer_fomod_plus/installer/ui/UIHelper.cpp @@ -27,7 +27,6 @@ bool CtrlClickEventFilter::eventFilter(QObject* obj, QEvent* event) if (mouseEvent->button() == Qt::LeftButton && mouseEvent->modifiers() & Qt::ControlModifier) { // TODO: Add Ctrl+click handler logic here - std::cout << "Ctrl+click detected on plugin: " << mPlugin->getName() << " in group: " << mGroup->getName() << std::endl; // For now, just fall through to default behavior } } diff --git a/libs/installer_fomod_plus/patchwizard/lib/PatchFinder.cpp b/libs/installer_fomod_plus/patchwizard/lib/PatchFinder.cpp index cd06b3a..855fe2b 100644 --- a/libs/installer_fomod_plus/patchwizard/lib/PatchFinder.cpp +++ b/libs/installer_fomod_plus/patchwizard/lib/PatchFinder.cpp @@ -77,7 +77,6 @@ void PatchFinder::populateInstalledPlugins() const auto mod_tree = mod->fileTree(); for (auto it = mod_tree->begin(); it != mod_tree->end(); ++it) { if ((*it)->isFile() && isPluginFile((*it)->name())) { - std::cout << "Plugin: " << (*it)->name().toStdString() << std::endl; m_installedPlugins[mod].emplace_back((*it)->name().toStdString()); m_installedPluginsCacheSet.insert((*it)->name().toStdString()); } diff --git a/libs/installer_fomod_plus/scanner/archiveparser.h b/libs/installer_fomod_plus/scanner/archiveparser.h index 4058d43..8819a84 100644 --- a/libs/installer_fomod_plus/scanner/archiveparser.h +++ b/libs/installer_fomod_plus/scanner/archiveparser.h @@ -83,7 +83,6 @@ public: } if (hasFomodFiles(archive->getFileList())) { - std::cout << "Found FOMOD files in " << qualifiedInstallerPath.toStdString() << std::endl; return ScanResult::HAS_FOMOD; } return ScanResult::NO_FOMOD; diff --git a/libs/installer_fomod_plus/share/FOMODData/FomodDB.h b/libs/installer_fomod_plus/share/FOMODData/FomodDB.h index d1a9aa2..f5ea6f5 100644 --- a/libs/installer_fomod_plus/share/FOMODData/FomodDB.h +++ b/libs/installer_fomod_plus/share/FOMODData/FomodDB.h @@ -33,8 +33,6 @@ public: for (const auto &group: installStep.optionalFileGroups.groups) { for (const auto &plugin: group.plugins.plugins) { // Create a DB entry for the given plugin if it has an ESP - std::cout << "\nPlugin: " << plugin.name << std::endl; - for (auto file: plugin.files.files) { if (file.isFolder || !isPluginFile(file.source)) { continue; diff --git a/libs/nak/src/paths.rs b/libs/nak/src/paths.rs index e86c284..f6dde6c 100644 --- a/libs/nak/src/paths.rs +++ b/libs/nak/src/paths.rs @@ -1,11 +1,11 @@ //! Shared data directory for Fluorine Manager. //! -//! All data lives under `~/.var/app/com.fluorine.manager/`. +//! All data lives under `~/.local/share/fluorine/`. use std::path::PathBuf; -/// Returns the Fluorine data directory (`~/.var/app/com.fluorine.manager`). +/// Returns the Fluorine data directory (`~/.local/share/fluorine`). pub fn data_dir() -> PathBuf { let home = std::env::var("HOME").unwrap_or_else(|_| "/tmp".to_string()); - PathBuf::from(home).join(".var/app/com.fluorine.manager") + PathBuf::from(home).join(".local/share/fluorine") } diff --git a/libs/nak/src/runtime_wrap.rs b/libs/nak/src/runtime_wrap.rs index d33bd59..9b5ff89 100644 --- a/libs/nak/src/runtime_wrap.rs +++ b/libs/nak/src/runtime_wrap.rs @@ -34,23 +34,6 @@ fn find_in_path(binary: &str) -> Option<PathBuf> { } pub fn resolve_umu_run() -> Option<PathBuf> { - if is_flatpak() { - // In Flatpak, umu-run must run on the host via flatpak-spawn --host. - let host_copy = env::var("XDG_DATA_HOME") - .ok() - .map(PathBuf::from) - .unwrap_or_else(|| { - let home = env::var("HOME").unwrap_or_else(|_| "/tmp".to_string()); - PathBuf::from(home).join(".local/share") - }) - .join("fluorine/umu-run"); - if host_copy.exists() { - return Some(host_copy); - } - // Fall back to bare name for host PATH resolution. - return Some(PathBuf::from("umu-run")); - } - let bundled = env::var("NAK_BUNDLED_UMU_RUN") .ok() .map(PathBuf::from) @@ -64,14 +47,8 @@ pub fn resolve_umu_run() -> Option<PathBuf> { } } -pub fn is_flatpak() -> bool { - Path::new("/.flatpak-info").exists() -} - /// Build a command to run `exe` with the given environment variables. /// -/// In Flatpak mode, `flatpak-spawn --host` is used and env vars are passed as -/// `--env=KEY=VALUE` flags (the host process does NOT inherit the sandbox env). /// In steam-run mode, the command is wrapped with `steam-run`. /// Otherwise the command runs directly. pub fn build_command<S: AsRef<OsStr>>( @@ -86,19 +63,6 @@ pub fn build_command<S: AsRef<OsStr>>( } return cmd; } - if is_flatpak() { - let mut cmd = Command::new("flatpak-spawn"); - cmd.arg("--host"); - for (key, value) in envs { - cmd.arg(format!( - "--env={}={}", - key, - value.as_ref().to_string_lossy() - )); - } - cmd.arg(exe.as_ref()); - return cmd; - } let mut cmd = Command::new(exe); for (key, value) in envs { cmd.env(key, value.as_ref()); diff --git a/libs/plugin_python/src/proxy/proxypython.cpp b/libs/plugin_python/src/proxy/proxypython.cpp index 196bf23..c9c7ba2 100644 --- a/libs/plugin_python/src/proxy/proxypython.cpp +++ b/libs/plugin_python/src/proxy/proxypython.cpp @@ -224,6 +224,9 @@ bool ProxyPython::init(IOrganizer* moInfo) if (!m_Runner || !m_Runner->isInitialized()) { m_LoadFailure = FailureType::INITIALIZATION; + MOBase::log::error("Python proxy: interpreter failed to initialize, " + "Python plugins will be unavailable"); + return true; // return true so the plugin stays loaded (shows diagnostic) } else { m_Runner->addDllSearchPath(pluginDataRoot / "dlls"); @@ -296,7 +299,7 @@ QStringList ProxyPython::pluginList(const QDir& pluginPath) const QList<QObject*> ProxyPython::load(const QString& identifier) { - if (!m_Runner) { + if (!m_Runner || !m_Runner->isInitialized()) { return {}; } return m_Runner->load(identifier); @@ -304,7 +307,7 @@ QList<QObject*> ProxyPython::load(const QString& identifier) void ProxyPython::unload(const QString& identifier) { - if (m_Runner) { + if (m_Runner && m_Runner->isInitialized()) { return m_Runner->unload(identifier); } } diff --git a/libs/plugin_python/src/runner/pythonrunner.cpp b/libs/plugin_python/src/runner/pythonrunner.cpp index 05a0f30..b0bf48c 100644 --- a/libs/plugin_python/src/runner/pythonrunner.cpp +++ b/libs/plugin_python/src/runner/pythonrunner.cpp @@ -85,21 +85,6 @@ namespace mo2::python { return true; } - std::optional<QByteArray> oldPythonHome; - std::optional<QByteArray> oldPythonPath; - auto restorePythonEnv = [&]() { - if (oldPythonHome.has_value()) { - setenv("PYTHONHOME", oldPythonHome->constData(), 1); - } else { - unsetenv("PYTHONHOME"); - } - if (oldPythonPath.has_value()) { - setenv("PYTHONPATH", oldPythonPath->constData(), 1); - } else { - unsetenv("PYTHONPATH"); - } - }; - try { static const char* argv0 = "ModOrganizer.exe"; @@ -107,14 +92,49 @@ namespace mo2::python { #ifdef MO2_PYTHON_SHARED_LIBRARY // Ensure libpython symbols are globally visible for extension modules // loaded later (_struct, PyQt6, etc.). + bool usedNoload = true; void* pyHandle = dlopen(MO2_PYTHON_SHARED_LIBRARY, RTLD_NOW | RTLD_GLOBAL | RTLD_NOLOAD); if (pyHandle == nullptr) { + usedNoload = false; pyHandle = dlopen(MO2_PYTHON_SHARED_LIBRARY, RTLD_NOW | RTLD_GLOBAL); } if (pyHandle == nullptr) { - MOBase::log::warn("failed to dlopen python shared library '{}': {}", + MOBase::log::warn("python: failed to dlopen '{}': {}", MO2_PYTHON_SHARED_LIBRARY, dlerror()); + } else { + MOBase::log::debug("python: '{}' promoted to RTLD_GLOBAL ({})", + MO2_PYTHON_SHARED_LIBRARY, + usedNoload ? "RTLD_NOLOAD" : "fresh load"); + } + + // Diagnose which DSO the Python init symbols resolve to globally. + { + Dl_info di; + void* sym = dlsym(RTLD_DEFAULT, "Py_InitializeEx"); + if (sym && dladdr(sym, &di)) { + fprintf(stderr, "[py-diag] Py_InitializeEx global from '%s'\n", + di.dli_fname); + MOBase::log::debug("python: Py_InitializeEx (global) from '{}'", + di.dli_fname); + } else { + fprintf(stderr, "[py-diag] Py_InitializeEx NOT in global scope " + "(sym=%p, err=%s)\n", + sym, dlerror()); + MOBase::log::warn("python: Py_InitializeEx NOT in global scope " + "(sym={}, err={})", + sym, dlerror()); + } + sym = dlsym(RTLD_DEFAULT, "Py_IsInitialized"); + if (sym && dladdr(sym, &di)) { + fprintf(stderr, "[py-diag] Py_IsInitialized global from '%s'\n", + di.dli_fname); + MOBase::log::debug("python: Py_IsInitialized (global) from '{}'", + di.dli_fname); + } else { + fprintf(stderr, "[py-diag] Py_IsInitialized NOT in global scope\n"); + MOBase::log::warn("python: Py_IsInitialized NOT in global scope"); + } } #endif #endif @@ -130,6 +150,25 @@ namespace mo2::python { } else { pythonHome = QCoreApplication::applicationDirPath() + "/python"; } + if (!QDir(pythonHome).exists()) { + MOBase::log::warn("python: PYTHONHOME dir '{}' does not exist", + pythonHome); + } + + std::optional<QByteArray> oldPythonHome; + std::optional<QByteArray> oldPythonPath; + auto restorePythonEnv = [&]() { + if (oldPythonHome.has_value()) { + setenv("PYTHONHOME", oldPythonHome->constData(), 1); + } else { + unsetenv("PYTHONHOME"); + } + if (oldPythonPath.has_value()) { + setenv("PYTHONPATH", oldPythonPath->constData(), 1); + } else { + unsetenv("PYTHONPATH"); + } + }; if (const char* v = std::getenv("PYTHONHOME"); v != nullptr) { oldPythonHome = QByteArray(v); } @@ -137,48 +176,86 @@ namespace mo2::python { oldPythonPath = QByteArray(v); } - if (QDir(pythonHome).exists()) { - setenv("PYTHONHOME", pythonHome.toUtf8().constData(), 1); - - const QDir libDir(pythonHome + "/lib"); - const auto pyDirs = - libDir.entryList({"python3.*"}, QDir::Dirs | QDir::NoDotAndDotDot); - if (!pyDirs.isEmpty()) { - const QString pyver = pyDirs.first(); - const QString pyPath = QString("%1/lib/%2:%1/lib/%2/site-packages:%1") - .arg(pythonHome, pyver); - setenv("PYTHONPATH", pyPath.toUtf8().constData(), 1); - } - } - // Paths we want to prepend/append for MO2 plugin loading. auto paths = pythonPaths; - PyConfig config; - PyConfig_InitPythonConfig(&config); + // Configure bundled Python like running python/bin/python3 directly. + const QDir libDir(pythonHome + "/lib"); + const auto pyDirs = + libDir.entryList({"python3.*"}, QDir::Dirs | QDir::NoDotAndDotDot); + const QString pyverDir = pyDirs.isEmpty() ? QStringLiteral("python3.13") + : pyDirs.first(); + const QString stdlibDir = pythonHome + "/lib/" + pyverDir; + const QString stdlibZip = pythonHome + "/lib/python313.zip"; + const QString rootDynloadDir = pythonHome + "/lib-dynload"; + const QString rootPlatDir = pythonHome + "/plat-linux2"; + const QString dynloadDir = stdlibDir + "/lib-dynload"; + const QString siteDir = stdlibDir + "/site-packages"; - // from PyBind11 - config.parse_argv = 0; - config.install_signal_handlers = 0; + QStringList corePaths = {stdlibDir, siteDir, dynloadDir}; + if (QFile::exists(stdlibZip)) { + corePaths.prepend(stdlibZip); + } + if (QDir(rootDynloadDir).exists()) { + corePaths.append(rootDynloadDir); + } + if (QDir(rootPlatDir).exists()) { + corePaths.append(rootPlatDir); + } - // from MO2 - config.site_import = 1; - config.optimization_level = 2; + corePaths.append(pythonHome); + const QByteArray pyPath = corePaths.join(":").toUtf8(); + setenv("PYTHONHOME", pythonHome.toUtf8().constData(), 1); + setenv("PYTHONPATH", pyPath.constData(), 1); - py::initialize_interpreter(&config, 1, &argv0, true); + fprintf(stderr, + "[py-diag] calling Py_InitializeFromConfig, PYTHONHOME='%s', " + "Py_IsInitialized before=%d\n", + pythonHome.toUtf8().constData(), Py_IsInitialized()); + MOBase::log::debug( + "python: calling Py_InitializeFromConfig, PYTHONHOME='{}', " + "Py_IsInitialized before={}", + pythonHome, Py_IsInitialized()); - // Restore process environment after interpreter startup so - // subprocesses (umu/NaK/tools) are not forced onto MO2's Python. - restorePythonEnv(); + // Use Py_InitializeFromConfig (Python 3.8+) for explicit error reporting. + { + PyConfig config; + PyConfig_InitPythonConfig(&config); + // PYTHONHOME is already set via setenv; PyConfig reads it from env. + PyStatus status = Py_InitializeFromConfig(&config); + PyConfig_Clear(&config); + if (PyStatus_Exception(status)) { + fprintf(stderr, + "[py-diag] Py_InitializeFromConfig FAILED: '%s' [in '%s']\n", + status.err_msg ? status.err_msg : "(no message)", + status.func ? status.func : "(no func)"); + MOBase::log::error( + "python: Py_InitializeFromConfig failed: '{}' [in '{}']", + status.err_msg ? status.err_msg : "(no message)", + status.func ? status.func : "(no func)"); + restorePythonEnv(); + return false; + } + } + + fprintf(stderr, "[py-diag] Py_IsInitialized after=%d\n", + Py_IsInitialized()); + MOBase::log::debug("python: Py_IsInitialized after={}", + Py_IsInitialized()); if (!Py_IsInitialized()) { + const char* ph = std::getenv("PYTHONHOME"); + const char* pp = std::getenv("PYTHONPATH"); MOBase::log::error( - "failed to init python: failed to initialize interpreter."); - - if (PyGILState_Check()) { - PyEval_SaveThread(); - } - + "failed to init python: Py_IsInitialized() returned false."); + MOBase::log::error(" PYTHONHOME='{}', PYTHONPATH='{}'", + ph ? ph : "(null)", pp ? pp : "(null)"); + MOBase::log::error(" pythonHome='{}', exists={}", pythonHome, + QDir(pythonHome).exists() ? "yes" : "no"); + const QString encPath = pythonHome + "/lib/python3.13/encodings"; + MOBase::log::error(" encodings dir='{}', exists={}", encPath, + QDir(encPath).exists() ? "yes" : "no"); + restorePythonEnv(); return false; } @@ -201,11 +278,11 @@ namespace mo2::python { // when Python is initialized, the GIl is acquired, and if it is not // release, trying to acquire it on a different thread will deadlock PyEval_SaveThread(); + restorePythonEnv(); return true; } catch (const py::error_already_set& ex) { - restorePythonEnv(); MOBase::log::error("failed to init python: {}", ex.what()); return false; } @@ -353,7 +430,8 @@ namespace mo2::python { return allInterfaceList; } catch (const py::error_already_set& ex) { - MOBase::log::error("Failed to import plugin from {}.", identifier); + MOBase::log::error("Failed to import plugin from {}: {}", identifier, + ex.what()); throw pyexcept::PythonError(ex); } } |
