diff --git a/src/runner/gilock.cpp b/src/runner/gilock.cpp index ed5985c..fb63387 100644 --- a/src/runner/gilock.cpp +++ b/src/runner/gilock.cpp @@ -7,5 +7,6 @@ GILock::GILock() GILock::~GILock() { + PyErr_Clear(); PyGILState_Release(m_State); } diff --git a/src/runner/proxypluginwrappers.cpp b/src/runner/proxypluginwrappers.cpp index cabb127..579663c 100644 --- a/src/runner/proxypluginwrappers.cpp +++ b/src/runner/proxypluginwrappers.cpp @@ -38,6 +38,16 @@ QString class_name::name() const \ return basicWrapperFunctionImplementation(this, "name"); \ } \ \ +QString class_name::localizedName() const \ +{ \ + return basicWrapperFunctionImplementationWithDefault(this, &class_name::localizedName_Default, "localizedName"); \ +} \ + \ +IPlugin* class_name::master() const \ +{ \ + return basicWrapperFunctionImplementationWithDefault(this, &class_name::master_Default, "master"); \ +} \ + \ QString class_name::author() const \ { \ return basicWrapperFunctionImplementation(this, "author"); \ @@ -61,7 +71,9 @@ bool class_name::isActive() const \ QList class_name::settings() const \ { \ return basicWrapperFunctionImplementation>(this, "settings"); \ -} +} \ +QString class_name::localizedName_Default() const { return IPlugin::localizedName(); } \ +IPlugin* class_name::master_Default() const { return IPlugin::master(); } /// end COMMON_I_PLUGIN_WRAPPER_DEFINITIONS ///////////////////////////// diff --git a/src/runner/proxypluginwrappers.h b/src/runner/proxypluginwrappers.h index 6638bbf..1d51664 100644 --- a/src/runner/proxypluginwrappers.h +++ b/src/runner/proxypluginwrappers.h @@ -19,12 +19,15 @@ #define COMMON_I_PLUGIN_WRAPPER_DECLARATIONS public: \ virtual bool init(MOBase::IOrganizer *moInfo) override; \ virtual QString name() const override; \ +virtual QString localizedName() const override; \ +virtual IPlugin* master() const override; \ virtual QString author() const override; \ virtual QString description() const override; \ virtual MOBase::VersionInfo version() const override; \ virtual bool isActive() const override; \ -virtual QList settings() const override; - +virtual QList settings() const override; \ +QString localizedName_Default() const; \ +IPlugin* master_Default() const; // Even though the base interface is not a QObject, this has to be because we have no way to pass Mod Organizer a plugin that implements multiple interfaces. // QObject must be the first base class because moc assumes the first base class is a QObject diff --git a/src/runner/pythonrunner.cpp b/src/runner/pythonrunner.cpp index 0972bdc..c319179 100644 --- a/src/runner/pythonrunner.cpp +++ b/src/runner/pythonrunner.cpp @@ -677,9 +677,14 @@ BOOST_PYTHON_MODULE(mobase) .def("onModMoved", &MOBase::IModList::onModMoved, bpy::arg("callback")) ; + // Note: localizedName() and master() have to go in all the plugin wrappers declaration, + // since the default functions are specific to each wrapper, otherwise in turns into an + // infinite recursion mess. bpy::class_("IPlugin") .def("init", bpy::pure_virtual(&MOBase::IPlugin::init), bpy::arg("organizer")) .def("name", bpy::pure_virtual(&MOBase::IPlugin::name)) + .def("localizedName", &MOBase::IPlugin::localizedName, &IPluginWrapper::localizedName_Default) + .def("master", &MOBase::IPlugin::master, &IPluginWrapper::master_Default, bpy::return_value_policy()) .def("author", bpy::pure_virtual(&MOBase::IPlugin::author)) .def("description", bpy::pure_virtual(&MOBase::IPlugin::description)) .def("version", bpy::pure_virtual(&MOBase::IPlugin::version)) @@ -688,6 +693,9 @@ BOOST_PYTHON_MODULE(mobase) ; bpy::class_, boost::noncopyable>("IPluginDiagnose") + .def("localizedName", &MOBase::IPlugin::localizedName, &IPluginDiagnoseWrapper::localizedName_Default) + .def("master", &MOBase::IPlugin::master, &IPluginDiagnoseWrapper::master_Default, bpy::return_value_policy()) + .def("activeProblems", bpy::pure_virtual(&MOBase::IPluginDiagnose::activeProblems)) .def("shortDescription", bpy::pure_virtual(&MOBase::IPluginDiagnose::shortDescription), bpy::arg("key")) .def("fullDescription", bpy::pure_virtual(&MOBase::IPluginDiagnose::fullDescription), bpy::arg("key")) @@ -704,6 +712,8 @@ BOOST_PYTHON_MODULE(mobase) ; bpy::class_, boost::noncopyable>("IPluginFileMapper") + .def("localizedName", &MOBase::IPlugin::localizedName, &IPluginFileMapperWrapper::localizedName_Default) + .def("master", &MOBase::IPlugin::master, &IPluginFileMapperWrapper::master_Default, bpy::return_value_policy()) .def("mappings", bpy::pure_virtual(&MOBase::IPluginFileMapper::mappings)) ; @@ -736,6 +746,9 @@ BOOST_PYTHON_MODULE(mobase) ; bpy::class_, boost::noncopyable>("IPluginGame") + .def("localizedName", &MOBase::IPlugin::localizedName, &IPluginGameWrapper::localizedName_Default) + .def("master", &MOBase::IPlugin::master, &IPluginGameWrapper::master_Default, bpy::return_value_policy()) + .def("gameName", bpy::pure_virtual(&MOBase::IPluginGame::gameName)) .def("initializeProfile", bpy::pure_virtual(&MOBase::IPluginGame::initializeProfile), (bpy::arg("directory"), "settings")) .def("savegameExtension", bpy::pure_virtual(&MOBase::IPluginGame::savegameExtension)) @@ -830,6 +843,9 @@ BOOST_PYTHON_MODULE(mobase) bpy::class_, boost::noncopyable>("IPluginInstallerSimple") .def("onInstallationStart", &IPluginInstaller::onInstallationStart, (bpy::arg("archive"), bpy::arg("reinstallation"), bpy::arg("current_mod"))) .def("onInstallationEnd", &IPluginInstaller::onInstallationEnd, (bpy::arg("result"), bpy::arg("new_mod"))) + .def("localizedName", &MOBase::IPlugin::localizedName, &IPluginInstallerSimpleWrapper::localizedName_Default) + .def("master", &MOBase::IPlugin::master, &IPluginInstallerSimpleWrapper::master_Default, bpy::return_value_policy()) + // Note: Keeping the variant here even if we always return a tuple to be consistent with the wrapper and // have proper stubs generation. .def("install", +[](IPluginInstallerSimple* p, GuessedValue& modName, std::shared_ptr& tree, QString& version, int& nexusID) @@ -844,6 +860,9 @@ BOOST_PYTHON_MODULE(mobase) bpy::class_, boost::noncopyable>("IPluginInstallerCustom") .def("onInstallationStart", &IPluginInstaller::onInstallationStart, (bpy::arg("archive"), bpy::arg("reinstallation"), bpy::arg("current_mod"))) .def("onInstallationEnd", &IPluginInstaller::onInstallationEnd, (bpy::arg("result"), bpy::arg("new_mod"))) + .def("localizedName", &MOBase::IPlugin::localizedName, &IPluginInstallerCustomWrapper::localizedName_Default) + .def("master", &MOBase::IPlugin::master, &IPluginInstallerCustomWrapper::master_Default, bpy::return_value_policy()) + // Needs to add both otherwize boost does not understand: .def("isArchiveSupported", &IPluginInstaller::isArchiveSupported, bpy::arg("tree")) .def("isArchiveSupported", &IPluginInstallerCustom::isArchiveSupported, bpy::arg("archive_name")) @@ -854,6 +873,9 @@ BOOST_PYTHON_MODULE(mobase) ; bpy::class_, boost::noncopyable>("IPluginModPage") + .def("localizedName", &MOBase::IPlugin::localizedName, &IPluginModPageWrapper::localizedName_Default) + .def("master", &MOBase::IPlugin::master, &IPluginModPageWrapper::master_Default, bpy::return_value_policy()) + .def("displayName", bpy::pure_virtual(&IPluginModPage::displayName)) .def("icon", bpy::pure_virtual(&IPluginModPage::icon)) .def("pageURL", bpy::pure_virtual(&IPluginModPage::pageURL)) @@ -864,12 +886,18 @@ BOOST_PYTHON_MODULE(mobase) ; bpy::class_, boost::noncopyable>("IPluginPreview") + .def("localizedName", &MOBase::IPlugin::localizedName, &IPluginPreviewWrapper::localizedName_Default) + .def("master", &MOBase::IPlugin::master, &IPluginPreviewWrapper::master_Default, bpy::return_value_policy()) + .def("supportedExtensions", bpy::pure_virtual(&IPluginPreview::supportedExtensions)) .def("genFilePreview", bpy::pure_virtual(&IPluginPreview::genFilePreview), bpy::return_value_policy(), (bpy::arg("filename"), "max_size")) ; bpy::class_, boost::noncopyable>("IPluginTool") + .def("localizedName", &MOBase::IPlugin::localizedName, &IPluginToolWrapper::localizedName_Default) + .def("master", &MOBase::IPlugin::master, &IPluginToolWrapper::master_Default, bpy::return_value_policy()) + .def("displayName", bpy::pure_virtual(&IPluginTool::displayName)) .def("tooltip", bpy::pure_virtual(&IPluginTool::tooltip)) .def("icon", bpy::pure_virtual(&IPluginTool::icon)) diff --git a/src/runner/pythonwrapperutilities.h b/src/runner/pythonwrapperutilities.h index 9786474..b5e0066 100644 --- a/src/runner/pythonwrapperutilities.h +++ b/src/runner/pythonwrapperutilities.h @@ -19,8 +19,10 @@ namespace details { */ template ReturnType wrapperFunctionImplementation(WrapperTypePtr wrapper, bool apiTransfer, Fn fn, boost::python::object* objPtr, const char *methodName, Args... args) { - GILock lock; - boost::python::override implementation = wrapper->get_override(methodName); + boost::python::override implementation = [&]() { + GILock lock; + return wrapper->get_override(methodName); + }(); if (!implementation) { if constexpr (std::is_same_v) { throw pyexcept::MissingImplementation(wrapper->className, methodName); @@ -29,6 +31,8 @@ namespace details { return std::invoke(fn, wrapper, args...); } } + + GILock lock; try { boost::python::object result = implementation(args...); if (objPtr) {