aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSulfurNitride <SulfurNitride@users.noreply.github.com>2026-05-16 13:45:57 -0500
committerSulfurNitride <SulfurNitride@users.noreply.github.com>2026-05-16 13:45:57 -0500
commite403a7a9e1a71dde52fe21e1dd2215c0981a3fbc (patch)
treea823560fdfa0da676ca3a23119fdb30fcb877393
parentf39c7e06d2465258371412c4adac48ba72fddbb9 (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.h47
-rw-r--r--src/src/plugincontainer.cpp9
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;
}