added registered() python wrapper

null checks for the IOrganizer, replaced pluginDataPath() by the static version getPluginDataPath()
This commit is contained in:
isanae
2020-11-06 09:06:55 -05:00
parent 6445f7f47f
commit 6a73c23a4b
4 changed files with 74 additions and 41 deletions
+36 -27
View File
@@ -109,7 +109,7 @@ BOOST_PYTHON_MODULE(mobase)
utils::register_sequence_container<std::vector<Mapping>>();
utils::register_set_container<std::set<QString>>();
utils::register_associative_container<QMap<QString, QVariant>>(); // Required for QVariant since this is QVariantMap.
utils::register_associative_container<QMap<QString, QStringList>>();
utils::register_associative_container<std::map<QString, IModList::ModStates>>();
@@ -124,8 +124,8 @@ BOOST_PYTHON_MODULE(mobase)
// Variants:
bpy::register_variant<std::variant<
IPluginInstaller::EInstallResult,
std::shared_ptr<IFileTree>,
IPluginInstaller::EInstallResult,
std::shared_ptr<IFileTree>,
std::tuple<IPluginInstaller::EInstallResult, std::shared_ptr<IFileTree>, QString, int>>>();
bpy::register_variant<std::variant<IFileTree::OverwritesType, std::size_t>>();
bpy::register_variant<std::variant<QString, bool>>();
@@ -220,7 +220,7 @@ BOOST_PYTHON_MODULE(mobase)
.def_readwrite("description", &PluginSetting::description)
.def_readwrite("default_value", &PluginSetting::defaultValue);
bpy::class_<ExecutableInfo>("ExecutableInfo",
bpy::class_<ExecutableInfo>("ExecutableInfo",
bpy::init<const QString&, const QFileInfo&>((bpy::arg("title"), "binary")))
.def("withArgument", &ExecutableInfo::withArgument, bpy::return_self<>(), bpy::arg("argument"))
.def("withWorkingDirectory", &ExecutableInfo::withWorkingDirectory, bpy::return_self<>(), bpy::arg("directory"))
@@ -235,7 +235,7 @@ BOOST_PYTHON_MODULE(mobase)
.def("isCustom", &ExecutableInfo::isCustom)
;
bpy::class_<ExecutableForcedLoadSetting>("ExecutableForcedLoadSetting",
bpy::class_<ExecutableForcedLoadSetting>("ExecutableForcedLoadSetting",
bpy::init<const QString&, const QString&>((bpy::arg("process"), "library")))
.def("withForced", &ExecutableForcedLoadSetting::withForced, bpy::return_self<>(), bpy::arg("forced"))
.def("withEnabled", &ExecutableForcedLoadSetting::withEnabled, bpy::return_self<>(), bpy::arg("enabled"))
@@ -293,9 +293,9 @@ BOOST_PYTHON_MODULE(mobase)
(bpy::arg("path"), "filter"))
// In C++, it is possible to create a QStringList implicitly from a single QString. This is not possible with the current
// converters in python (and I do not think it is a good idea to have it everywhere), but here it is nice to be able to
// converters in python (and I do not think it is a good idea to have it everywhere), but here it is nice to be able to
// pass a single string, so we add an extra overload.
// Important: the order matters, because a Python string can be converted to a QStringList since it is a sequence of
// Important: the order matters, because a Python string can be converted to a QStringList since it is a sequence of
// single-character strings:
.def("findFiles", +[](const IOrganizer* o, QString const& p, const QStringList& gf) { return o->findFiles(p, gf); },
(bpy::arg("path"), "patterns"))
@@ -311,11 +311,11 @@ BOOST_PYTHON_MODULE(mobase)
// Custom implementation for startApplication and waitForApplication because 1) HANDLE (= void*) is not properly
// converted from/to python, and 2) we need to convert the by-ptr argument to a return-tuple for waitForApplication:
.def("startApplication",
+[](IOrganizer* o, const QString& executable, const QStringList& args, const QString& cwd, const QString& profile,
.def("startApplication",
+[](IOrganizer* o, const QString& executable, const QStringList& args, const QString& cwd, const QString& profile,
const QString& forcedCustomOverwrite, bool ignoreCustomOverwrite) {
return (std::uintptr_t) o->startApplication(executable, args, cwd, profile, forcedCustomOverwrite, ignoreCustomOverwrite);
}, (bpy::arg("executable"), (bpy::arg("args") = QStringList()), (bpy::arg("cwd") = ""), (bpy::arg("profile") = ""),
}, (bpy::arg("executable"), (bpy::arg("args") = QStringList()), (bpy::arg("cwd") = ""), (bpy::arg("profile") = ""),
(bpy::arg("forcedCustomOverwrite") = ""), (bpy::arg("ignoreCustomOverwrite") = false)), bpy::return_value_policy<bpy::return_by_value>())
.def("waitForApplication", +[](IOrganizer *o, std::uintptr_t handle) {
DWORD returnCode;
@@ -367,9 +367,9 @@ BOOST_PYTHON_MODULE(mobase)
// FileTreeEntry Scope:
auto fileTreeEntryClass = bpy::class_<FileTreeEntry, boost::noncopyable>("FileTreeEntry", bpy::no_init);
{
bpy::scope scope = fileTreeEntryClass;
bpy::enum_<FileTreeEntry::FileTypes>("FileTypes")
.value("FILE_OR_DIRECTORY", FileTreeEntry::FILE_OR_DIRECTORY)
.value("FILE", FileTreeEntry::FILE)
@@ -413,7 +413,7 @@ BOOST_PYTHON_MODULE(mobase)
// IFileTree scope:
auto iFileTreeClass = bpy::class_<IFileTree, bpy::bases<FileTreeEntry>, boost::noncopyable>("IFileTree", bpy::no_init);
{
bpy::scope scope = iFileTreeClass;
bpy::enum_<IFileTree::InsertPolicy>("InsertPolicy")
@@ -463,7 +463,7 @@ BOOST_PYTHON_MODULE(mobase)
return result;
}, bpy::arg("path"))
// Merge needs custom return types depending if the user wants overrides or not. A failure is translated
// Merge needs custom return types depending if the user wants overrides or not. A failure is translated
// into an exception for easier tracing and handling.
.def("merge", +[](IFileTree* p, std::shared_ptr<IFileTree> other, bool returnOverwrites) -> std::variant<IFileTree::OverwritesType, std::size_t> {
IFileTree::OverwritesType overwrites;
@@ -510,7 +510,7 @@ BOOST_PYTHON_MODULE(mobase)
.def("__repr__", +[](const IFileTree* entry) { return "IFileTree(\"" + entry->name() + "\")"; })
;
}
bpy::class_<IProfile, boost::noncopyable>("IProfile", bpy::no_init)
.def("name", &IProfile::name)
@@ -649,11 +649,11 @@ BOOST_PYTHON_MODULE(mobase)
bpy::return_self<>(), (bpy::arg("value"), "quality"))
// Methods to simulate the assignment operator:
.def("reset", +[](GuessedValue<QString>* gv) {
.def("reset", +[](GuessedValue<QString>* gv) {
*gv = GuessedValue<QString>(); }, bpy::return_self<>())
.def("reset", +[](GuessedValue<QString>* gv, const QString& value, EGuessQuality eq) {
.def("reset", +[](GuessedValue<QString>* gv, const QString& value, EGuessQuality eq) {
*gv = GuessedValue<QString>(value, eq); }, bpy::return_self<>(), (bpy::arg("value"), "quality"))
.def("reset", +[](GuessedValue<QString>* gv, const GuessedValue<QString>& other) {
.def("reset", +[](GuessedValue<QString>* gv, const GuessedValue<QString>& other) {
*gv = other; }, bpy::return_self<>(), bpy::arg("other"))
// Use an intermediate lambda to avoid having to register the std::function conversion:
@@ -767,10 +767,11 @@ 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,
// Note: registered(), 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_<IPluginWrapper, boost::noncopyable>("IPlugin")
.def("registered", &MOBase::IPlugin::registered, &IPluginWrapper::registered_Default)
.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)
@@ -783,6 +784,7 @@ BOOST_PYTHON_MODULE(mobase)
;
bpy::class_<IPluginDiagnoseWrapper, bpy::bases<IPlugin>, boost::noncopyable>("IPluginDiagnose")
.def("registered", &MOBase::IPlugin::registered, &IPluginDiagnoseWrapper::registered_Default)
.def("localizedName", &MOBase::IPlugin::localizedName, &IPluginDiagnoseWrapper::localizedName_Default)
.def("master", &MOBase::IPlugin::master, &IPluginDiagnoseWrapper::master_Default, bpy::return_value_policy<bpy::reference_existing_object>())
@@ -809,6 +811,7 @@ BOOST_PYTHON_MODULE(mobase)
;
bpy::class_<IPluginFileMapperWrapper, bpy::bases<IPlugin>, boost::noncopyable>("IPluginFileMapper")
.def("registered", &MOBase::IPlugin::registered, &IPluginFileMapperWrapper::registered_Default)
.def("localizedName", &MOBase::IPlugin::localizedName, &IPluginFileMapperWrapper::localizedName_Default)
.def("master", &MOBase::IPlugin::master, &IPluginFileMapperWrapper::master_Default, bpy::return_value_policy<bpy::reference_existing_object>())
.def("mappings", bpy::pure_virtual(&MOBase::IPluginFileMapper::mappings))
@@ -843,6 +846,7 @@ BOOST_PYTHON_MODULE(mobase)
;
bpy::class_<IPluginGameWrapper, bpy::bases<IPlugin>, boost::noncopyable>("IPluginGame")
.def("registered", &MOBase::IPlugin::registered, &IPluginGameWrapper::registered_Default)
.def("localizedName", &MOBase::IPlugin::localizedName, &IPluginGameWrapper::localizedName_Default)
.def("master", &MOBase::IPlugin::master, &IPluginGameWrapper::master_Default, bpy::return_value_policy<bpy::reference_existing_object>())
@@ -940,6 +944,7 @@ BOOST_PYTHON_MODULE(mobase)
bpy::class_<IPluginInstallerSimpleWrapper, bpy::bases<IPluginInstaller>, 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("registered", &MOBase::IPlugin::registered, &IPluginInstallerSimpleWrapper::registered_Default)
.def("localizedName", &MOBase::IPlugin::localizedName, &IPluginInstallerSimpleWrapper::localizedName_Default)
.def("master", &MOBase::IPlugin::master, &IPluginInstallerSimpleWrapper::master_Default, bpy::return_value_policy<bpy::reference_existing_object>())
@@ -957,10 +962,11 @@ BOOST_PYTHON_MODULE(mobase)
bpy::class_<IPluginInstallerCustomWrapper, bpy::bases<IPluginInstaller>, 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("registered", &MOBase::IPlugin::registered, &IPluginInstallerCustomWrapper::registered_Default)
.def("localizedName", &MOBase::IPlugin::localizedName, &IPluginInstallerCustomWrapper::localizedName_Default)
.def("master", &MOBase::IPlugin::master, &IPluginInstallerCustomWrapper::master_Default, bpy::return_value_policy<bpy::reference_existing_object>())
// Needs to add both otherwize boost does not understand:
// Needs to add both otherwize boost does not understand:
.def("isArchiveSupported", &IPluginInstaller::isArchiveSupported, bpy::arg("tree"))
.def("isArchiveSupported", &IPluginInstallerCustom::isArchiveSupported, bpy::arg("archive_name"))
.def("supportedExtensions", &IPluginInstallerCustom::supportedExtensions)
@@ -970,6 +976,7 @@ BOOST_PYTHON_MODULE(mobase)
;
bpy::class_<IPluginModPageWrapper, bpy::bases<IPlugin>, boost::noncopyable>("IPluginModPage")
.def("registered", &MOBase::IPlugin::registered, &IPluginModPageWrapper::registered_Default)
.def("localizedName", &MOBase::IPlugin::localizedName, &IPluginModPageWrapper::localizedName_Default)
.def("master", &MOBase::IPlugin::master, &IPluginModPageWrapper::master_Default, bpy::return_value_policy<bpy::reference_existing_object>())
@@ -983,15 +990,17 @@ BOOST_PYTHON_MODULE(mobase)
;
bpy::class_<IPluginPreviewWrapper, bpy::bases<IPlugin>, boost::noncopyable>("IPluginPreview")
.def("registered", &MOBase::IPlugin::registered, &IPluginPreviewWrapper::registered_Default)
.def("localizedName", &MOBase::IPlugin::localizedName, &IPluginPreviewWrapper::localizedName_Default)
.def("master", &MOBase::IPlugin::master, &IPluginPreviewWrapper::master_Default, bpy::return_value_policy<bpy::reference_existing_object>())
.def("supportedExtensions", bpy::pure_virtual(&IPluginPreview::supportedExtensions))
.def("genFilePreview", bpy::pure_virtual(&IPluginPreview::genFilePreview), bpy::return_value_policy<bpy::return_by_value>(),
.def("genFilePreview", bpy::pure_virtual(&IPluginPreview::genFilePreview), bpy::return_value_policy<bpy::return_by_value>(),
(bpy::arg("filename"), "max_size"))
;
bpy::class_<IPluginToolWrapper, bpy::bases<IPlugin>, boost::noncopyable>("IPluginTool")
.def("registered", &MOBase::IPlugin::registered, &IPluginToolWrapper::registered_Default)
.def("localizedName", &MOBase::IPlugin::localizedName, &IPluginToolWrapper::localizedName_Default)
.def("master", &MOBase::IPlugin::master, &IPluginToolWrapper::master_Default, bpy::return_value_policy<bpy::reference_existing_object>())
@@ -1009,7 +1018,7 @@ BOOST_PYTHON_MODULE(mobase)
bpy::def("getProductVersion", &MOBase::getProductVersion, bpy::arg("executable"));
bpy::def("getIconForExecutable", &MOBase::iconForExecutable, bpy::arg("executable"));
// Expose MoVariant: MoVariant is a fake object whose only purpose is to be used as a type-hint
// Expose MoVariant: MoVariant is a fake object whose only purpose is to be used as a type-hint
// on the python side (e.g., def foo(x: mobase.MoVariant)). The real MoVariant is defined in the
// generated stubs, since it's only relevant when doing type-checking, but this needs to be defined,
// otherwise MoVariant is not found when actually running plugins through MO2, making them crash.
@@ -1116,7 +1125,7 @@ BOOST_PYTHON_MODULE(moprivate)
using callback_t = std::function<bool(QString, bool)>;
FileTree(std::shared_ptr<const IFileTree> parent, QString name, callback_t callback) :
FileTree(std::shared_ptr<const IFileTree> parent, QString name, callback_t callback) :
FileTreeEntry(parent, name), IFileTree(), m_Callback(callback){ }
std::shared_ptr<FileTreeEntry> addFile(QString name, bool) override {
@@ -1221,7 +1230,7 @@ void PythonRunner::initPath()
static QStringList paths = {
QCoreApplication::applicationDirPath() + "/pythoncore.zip",
QCoreApplication::applicationDirPath() + "/pythoncore",
m_MOInfo->pluginDataPath()
IOrganizer::getPluginDataPath()
};
Py_SetPath(paths.join(';').toStdWString().c_str());
@@ -1255,8 +1264,8 @@ QList<QObject*> PythonRunner::instantiate(const QString &pluginName)
// `pluginName` can either be a python file (single-file plugin or a folder (whole module).
//
// For whole module, we simply add the parent folder to path, then we load the module with a simple
// bpy::import, and we retrieve the associated __dict__ from which we extract either createPlugin or
// For whole module, we simply add the parent folder to path, then we load the module with a simple
// bpy::import, and we retrieve the associated __dict__ from which we extract either createPlugin or
// createPlugins.
//
// For single file, we need to use bpy::exec_file, and we will use the context (global variables)
@@ -1356,7 +1365,7 @@ QList<QObject*> PythonRunner::instantiate(const QString &pluginName)
}
return allInterfaceList;
}
}
catch (const bpy::error_already_set&) {
MOBase::log::error("Failed to import plugin from {}.", pluginName);
throw pyexcept::PythonError();