diff options
| author | SulfurNitride <SulfurNitride@users.noreply.github.com> | 2026-05-16 13:45:57 -0500 |
|---|---|---|
| committer | SulfurNitride <SulfurNitride@users.noreply.github.com> | 2026-05-16 13:45:57 -0500 |
| commit | e403a7a9e1a71dde52fe21e1dd2215c0981a3fbc (patch) | |
| tree | a823560fdfa0da676ca3a23119fdb30fcb877393 | |
| parent | f39c7e06d2465258371412c4adac48ba72fddbb9 (diff) | |
plugins: surface plugin name and Python exception on init failure
"plugin failed to initialize" with no name and no exception text is
the canonical first-line indicator for a Python plugin that didn't
register — but it tells the user nothing about *which* plugin or
*why*. Encountered while diagnosing a flaky first-launch failure
where a Python plugin's init() silently returned False on cold
boot and succeeded on the next run, plus a Ready or Not instance
that wouldn't open.
Wrap the IPlugin::init trampoline in pyplugins.h manually instead
of relying on PYBIND11_OVERRIDE_PURE so we can:
- log the plugin's name and the surrounding hint when init()
returns False (typically a plugin swallowing an exception in
its own try/except);
- catch pybind11::error_already_set and log the Python exception
text + traceback before returning False to the C++ caller.
plugincontainer.cpp's "plugin failed to initialize" line now also
prints the plugin name so the proxied-candidate context is clear.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| -rw-r--r-- | libs/plugin_python/src/mobase/wrappers/pyplugins.h | 47 | ||||
| -rw-r--r-- | src/src/plugincontainer.cpp | 9 |
2 files changed, 54 insertions, 2 deletions
diff --git a/libs/plugin_python/src/mobase/wrappers/pyplugins.h b/libs/plugin_python/src/mobase/wrappers/pyplugins.h index ce9a911..1480690 100644 --- a/libs/plugin_python/src/mobase/wrappers/pyplugins.h +++ b/libs/plugin_python/src/mobase/wrappers/pyplugins.h @@ -16,6 +16,8 @@ #include <uibase/ipluginpreview.h> #include <uibase/iplugintool.h> +#include <log.h> + // these needs to be defined in a header file for automoc - this file is included only // in pyplugins.cpp namespace mo2::python { @@ -35,7 +37,50 @@ namespace mo2::python { bool init(IOrganizer* organizer) override { - PYBIND11_OVERRIDE_PURE(bool, PluginBase, init, organizer); + // Wrap the override manually so we can surface diagnostics when a + // Python plugin's init() silently returns False (the common case + // when a plugin's own try/except eats the exception) or raises + // an exception that would otherwise bubble up as a cryptic + // "plugin failed to initialize" in plugincontainer. + try { + pybind11::gil_scoped_acquire gil; + pybind11::function override_fn = pybind11::get_override( + static_cast<const PluginBase*>(this), "init"); + if (!override_fn) { + pybind11::pybind11_fail( + "Tried to call pure virtual function \"PluginBase::init\""); + } + pybind11::object result = override_fn(organizer); + const bool ok = pybind11::cast<bool>(result); + if (!ok) { + QString pluginName; + try { + pluginName = this->name(); + } catch (...) { + } + MOBase::log::warn( + "Python plugin init() returned False (plugin: '{}'). " + "The plugin is probably swallowing an exception in " + "its own try/except — check the plugin source for " + "details.", + pluginName.isEmpty() ? std::string("<unknown>") + : pluginName.toStdString()); + } + return ok; + } catch (const pybind11::error_already_set& e) { + QString pluginName; + try { + pluginName = this->name(); + } catch (...) { + } + MOBase::log::error( + "Python plugin init() raised an exception (plugin: '{}'): " + "{}", + pluginName.isEmpty() ? std::string("<unknown>") + : pluginName.toStdString(), + e.what()); + return false; + } } QString name() const override { diff --git a/src/src/plugincontainer.cpp b/src/src/plugincontainer.cpp index 99e8ddb..9aa15a2 100644 --- a/src/src/plugincontainer.cpp +++ b/src/src/plugincontainer.cpp @@ -485,7 +485,14 @@ bool PluginContainer::initPlugin(IPlugin* plugin, IPluginProxy* pluginProxy, } if (!plugin->init(proxy)) { - log::warn("plugin failed to initialize"); + QString pluginName; + try { + pluginName = plugin->name(); + } catch (...) { + } + log::warn("plugin failed to initialize: {}", + pluginName.isEmpty() ? std::string("<unnamed>") + : pluginName.toStdString()); return false; } |
