Global re-organization to split mobase from runner.

This commit is contained in:
Mikaël Capelle
2022-04-28 22:44:15 +02:00
parent b2767887a9
commit 68060b0e6b
44 changed files with 724 additions and 664 deletions
-3
View File
@@ -1,3 +0,0 @@
[submodule "pybind11"]
path = pybind11
url = https://github.com/pybind/pybind11
+7 -2
View File
@@ -14,14 +14,19 @@ set(PYTHON_EXECUTABLE ${PYTHON_BUILD_PATH}/python.exe)
set(PYTHON_INCLUDE_DIRS ${PYTHON_ROOT}/Include)
set(PYTHON_LIBRARIES ${MO2_INSTALL_LIBS_PATH}/python${PYVERSION}.lib)
add_subdirectory(pybind11)
add_subdirectory(${MO2_BUILD_PATH}/pybind11 ${CMAKE_CURRENT_BINARY_DIR}/pybind11)
project(plugin_python)
# order matters!
add_subdirectory(src/runner-pybind11)
add_subdirectory(src/pybind11-qt)
add_subdirectory(src/mobase)
add_subdirectory(src/runner)
add_subdirectory(src/proxy)
# force plugin_python to build mobase
add_dependencies(plugin_python mobase)
# add_subdirectory(tests)
set_property(DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT plugin_python)
Submodule pybind11 deleted from 9bc2704430
+14
View File
@@ -0,0 +1,14 @@
cmake_minimum_required(VERSION 3.16)
pybind11_add_module(mobase MODULE)
mo2_configure_library(mobase
SOURCE_TREE
WARNINGS OFF
AUTOMOC ON
TRANSLATIONS OFF
PRIVATE_DEPENDS uibase Qt::Core
)
target_link_libraries(mobase PRIVATE pybind11::qt)
target_include_directories(mobase PRIVATE ${PYTHON_ROOT}/Include)
install(TARGETS mobase DESTINATION bin/plugins/plugin_python/libs)
+54
View File
@@ -0,0 +1,54 @@
#include "deprecation.h"
#include <filesystem>
#include <set>
#include <pybind11/pybind11.h>
#include <QCoreApplication>
#include "log.h"
namespace py = pybind11;
namespace mo2::python {
void show_deprecation_warning(std::string_view name, std::string_view message,
bool show_once)
{
// Contains the list of filename / line number for which a deprecation
// warning has already been shown.
static std::set<std::pair<std::string, int>> DeprecatedLines;
// Find the caller:
auto inspect = py::module_::import("inspect");
auto current_frame = inspect.attr("currentframe")();
py::sequence callable_frame = inspect.attr("getouterframes")(current_frame, 2);
auto filename = callable_frame[-1].attr("filename").cast<std::string>();
auto function = callable_frame[-1].attr("function").cast<std::string>();
auto lineno = callable_frame[-1].attr("lineno").cast<int>();
// Only show once if requested:
if (show_once && DeprecatedLines.contains({filename, lineno})) {
return;
}
// Register the deprecation:
DeprecatedLines.emplace(filename, lineno);
auto path = relative(std::filesystem::path(filename),
QCoreApplication::applicationDirPath().toStdWString());
// Show the message:
if (message.empty()) {
MOBase::log::warn("[deprecated] {} in {} [{}:{}].", name, function,
path.native(), lineno);
}
else {
MOBase::log::warn("[deprecated] {} in {} [{}:{}]: {}", name, function,
path.native(), lineno, message);
}
}
} // namespace mo2::python
@@ -7,19 +7,6 @@
namespace mo2::python {
/**
* @brief Configure Python stdout and stderr to log to MO2.
*
*/
void configure_python_stream();
/**
* @brief Configure logging for MO2 python plugin.
*
* @param mobase The mobase module.
*/
void configure_python_logging(pybind11::module_ mobase);
/**
* @brief Show a deprecation warning.
*
+269
View File
@@ -0,0 +1,269 @@
#pragma warning(disable : 4100)
#pragma warning(disable : 4996)
#include <tuple>
#include <variant>
#include "pybind11_all.h"
#include "wrappers/pyfiletree.h"
#include "wrappers/wrappers.h"
// TODO: remove these include (only for testing)
#include <QDir>
#include <QFile>
#include <QWidget>
#include <fmt/format.h>
#include <iplugin.h>
#include <iplugindiagnose.h>
#include <ipluginfilemapper.h>
#include <iplugingame.h>
#include <iplugininstaller.h>
#include <iplugininstallersimple.h>
#include <ipluginlist.h>
#include <ipluginmodpage.h>
#include <ipluginpreview.h>
#include <iplugintool.h>
#include <isavegame.h>
#include <isavegameinfowidget.h>
using namespace MOBase;
namespace py = pybind11;
PYBIND11_MODULE(mobase, m)
{
using namespace mo2::python;
py::module_::import("PyQt6.QtCore");
py::module_::import("PyQt6.QtWidgets");
// bindings
//
mo2::python::add_basic_bindings(m);
mo2::python::add_wrapper_bindings(m);
// game features must be added before plugins
mo2::python::add_game_feature_bindings(m);
mo2::python::add_plugins_bindings(m);
// widgets
//
py::module_ widgets(
py::reinterpret_borrow<py::module_>(PyImport_AddModule("mobase.widgets")));
m.attr("widgets") = widgets;
mo2::python::add_widget_bindings(widgets);
// functions
//
m.def("getFileVersion", &MOBase::getFileVersion, py::arg("filepath"));
m.def("getProductVersion", &MOBase::getProductVersion, py::arg("executable"));
m.def("getIconForExecutable", &MOBase::iconForExecutable, py::arg("executable"));
// 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
m.attr("MoVariant") = py::none();
// private stuff for debugging/test
py::module_ moprivate(
py::reinterpret_borrow<py::module_>(PyImport_AddModule("mobase.private")));
m.attr("private") = moprivate;
// expose a function to create a particular tree, only for debugging
// purpose, not in mobase.
mo2::python::add_make_tree_function(moprivate);
moprivate.def("extract_plugins", &mo2::python::extract_plugins);
// == BEGIN TESTS ==
m.def("testPlugin", [](py::object pyobj) {
py::scoped_ostream_redirect s{std::cout};
std::cout << "type: " << pyobj.get_type().attr("__name__").cast<std::string>()
<< "\n";
auto qobjects = mo2::python::extract_plugins(pyobj);
std::cout << " found " << qobjects.size() << " plugins\n";
// cast as IPlugin
for (int i = 0; i < qobjects.size(); ++i) {
IPlugin* plugin = qobject_cast<IPlugin*>(qobjects[i]);
std::cout << fmt::format(" plugin {}: {} -> {}\n", i, (void*)qobjects[i],
(void*)plugin);
std::cout << fmt::format(" name: {}\n", plugin->name().toStdString());
// std::cout << fmt::format(
// " installer?: {}\n",
// (void*)qobject_cast<IPluginInstaller*>(qobjects[i]));
// std::cout << fmt::format(
// " installer simple?: {}\n",
// (void*)qobject_cast<IPluginInstallerSimple*>(qobjects[i]));
if (IPluginGame* game = dynamic_cast<IPluginGame*>(plugin)) {
auto saves = game->listSaves(QDir());
std::cout << " saves: " << saves.size() << "\n";
for (auto& save : saves) {
std::cout << " save: " << (void*)save.get() << ", "
<< py::reinterpret_borrow<py::object>(py::cast(save))
<< ", " << save->getFilepath().toStdString() << "\n";
}
}
}
});
py::detail::type_caster<QString> t1;
py::detail::type_caster<QVariant> t2;
m.def("testQStringList", [](QStringList const& list) {
QStringList res = list;
for (QString& value : res) {
value = value + "_CPP";
}
return res;
});
m.def("testGuessedString", [](GuessedValue<QString> const& value) {
return std::make_tuple(value.operator const QString&(), value.variants());
});
m.def("testQStringList", [](QStringList const& list) {
QStringList res = list;
for (QString& value : res) {
value = value + "_CPP";
}
return res;
});
m.def("testQMap1", [](QMap<QString, QString> const& map) {
QMap<QString, int> res;
for (auto it = map.begin(); it != map.end(); ++it) {
res[it.key()] = it.value().size();
}
return res;
});
m.def("testQMap2", [](QMap<QString, int> const& map) {
QMap<QString, QString> res;
for (auto it = map.begin(); it != map.end(); ++it) {
res[it.key()] = QString::number(it.value());
}
return res;
});
m.def("testDateTime1", []() {
return QDateTime::fromString("2022-02-15T12:33:45", Qt::ISODate);
});
m.def("testDateTime2", [](QDateTime const& datetime) {
return datetime.toString();
});
m.def("testEnum0", []() {
return Qt::GlobalColor::darkRed;
});
m.def("testEnum", [](Qt::GlobalColor color) {
return py::make_tuple(color, QMessageBox::Icon::Information);
});
m.def("testPixmap", [](QPixmap const& pixmap) {
return pixmap.size();
});
m.def("createSaveGame", []() -> ISaveGame* {
class SaveGame : public ISaveGame {
QString getFilepath() const override { return "filepath"; }
QDateTime getCreationTime() const override
{
return QDateTime::fromString("2022-02-15T12:33:45", Qt::ISODate);
}
QString getName() const override { return "name"; }
QString getSaveGroupIdentifier() const override { return "group"; }
QStringList allFiles() const override { return {"file1", "file2"}; }
};
return new SaveGame();
});
m.def("testSaveGameWidget", [](ISaveGameInfoWidget* widget) {
class SaveGame : public ISaveGame {
QString getFilepath() const override { return "filepath-c++"; }
QDateTime getCreationTime() const override
{
return QDateTime::fromString("2022-02-15T12:33:45", Qt::ISODate);
}
QString getName() const override { return "name"; }
QString getSaveGroupIdentifier() const override { return "group"; }
QStringList allFiles() const override { return {"file1", "file2"}; }
};
static SaveGame s;
widget->setSave(s);
});
m.def("testSaveGameRef", [](const ISaveGame& game) {
std::cout << "getFilepath(): " << game.getFilepath().toStdString() << "\n";
std::cout << "getCreationTime(): "
<< game.getCreationTime().toString().toStdString() << "\n";
std::cout << "getName(): " << game.getName().toStdString() << "\n";
std::cout << "getSaveGroupIdentifier(): "
<< game.getSaveGroupIdentifier().toStdString() << "\n";
std::cout << "allFiles(): [ " << game.allFiles().join(" ").toStdString()
<< " ]\n";
});
m.def("testSaveGamePtr", [](const ISaveGame* game) {
std::cout << "getFilepath(): " << game->getFilepath().toStdString() << "\n";
std::cout << "getCreationTime(): "
<< game->getCreationTime().toString().toStdString() << "\n";
std::cout << "getName(): " << game->getName().toStdString() << "\n";
std::cout << "getSaveGroupIdentifier(): "
<< game->getSaveGroupIdentifier().toStdString() << "\n";
std::cout << "allFiles(): [ " << game->allFiles().join(" ").toStdString()
<< " ]\n";
});
m.def("testWidget1", [](QWidget* w) {
if (w) {
std::cout << "background role: " << w->backgroundRole() << "\n";
}
else {
std::cout << "null widget\n";
}
});
m.def("testWidget2", []() {
QWidget* w = new QWidget();
w->setBackgroundRole(QPalette::ColorRole::HighlightedText);
return w;
});
m.def("testFlags1", [](IPluginList::PluginStates states) {
std::vector<std::string> res;
if (states.testFlag(IPluginList::STATE_MISSING)) {
res.push_back("missing");
}
if (states.testFlag(IPluginList::STATE_INACTIVE)) {
res.push_back("inactive");
}
if (states.testFlag(IPluginList::STATE_ACTIVE)) {
res.push_back("active");
}
return res;
});
m.def("testFlags2", [](QStringList const& states) {
IPluginList::PluginStates res;
if (states.contains("missing")) {
res |= IPluginList::STATE_MISSING;
}
if (states.contains("inactive")) {
res |= IPluginList::STATE_INACTIVE;
}
if (states.contains("active")) {
res |= IPluginList::STATE_ACTIVE;
}
return res;
});
}
@@ -20,7 +20,7 @@
#include <pluginsetting.h>
#include <versioninfo.h>
#include "../pythonutils.h"
#include "../deprecation.h"
#include "pyfiletree.h"
using namespace MOBase;
@@ -3,12 +3,7 @@
#include <tuple>
#include <variant>
#include <pybind11/functional.h>
#include <pybind11/operators.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include "../pybind11_qt/pybind11_qt.h"
#include "../pybind11_all.h"
#include <ifiletree.h>
#include <log.h>
+13 -18
View File
@@ -4,58 +4,58 @@
<context>
<name>ProxyPython</name>
<message>
<location filename="proxypython.cpp" line="162"/>
<location filename="proxypython.cpp" line="166"/>
<source>Python Proxy</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxypython.cpp" line="172"/>
<location filename="proxypython.cpp" line="176"/>
<source>Proxy Plugin to allow plugins written in python to be loaded</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxypython.cpp" line="244"/>
<location filename="proxypython.cpp" line="248"/>
<source>ModOrganizer path contains a semicolon</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxypython.cpp" line="246"/>
<location filename="proxypython.cpp" line="250"/>
<source>Python DLL not found</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxypython.cpp" line="248"/>
<location filename="proxypython.cpp" line="252"/>
<source>Invalid Python DLL</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxypython.cpp" line="250"/>
<location filename="proxypython.cpp" line="254"/>
<source>Initializing Python failed</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxypython.cpp" line="252"/>
<location filename="proxypython.cpp" line="282"/>
<location filename="proxypython.cpp" line="256"/>
<location filename="proxypython.cpp" line="286"/>
<source>invalid problem key %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxypython.cpp" line="260"/>
<location filename="proxypython.cpp" line="264"/>
<source>The path to Mod Organizer (%1) contains a semicolon. &lt;br&gt;While this is legal on NTFS drives, many softwares do not handle it correctly.&lt;br&gt;Unfortunately MO depends on libraries that seem to fall into that group.&lt;br&gt;As a result the python plugin cannot be loaded, and the only solution we canoffer is to remove the semicolon or move MO to a path without a semicolon.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxypython.cpp" line="271"/>
<location filename="proxypython.cpp" line="275"/>
<source>The Python plugin DLL was not found, maybe your antivirus deleted it. Re-installing MO2 might fix the problem.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxypython.cpp" line="274"/>
<location filename="proxypython.cpp" line="278"/>
<source>The Python plugin DLL is invalid, maybe your antivirus is blocking it. Re-installing MO2 and adding exclusions for it to your AV might fix the problem.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxypython.cpp" line="279"/>
<location filename="proxypython.cpp" line="283"/>
<source>The initialization of the Python plugin DLL failed, unfortunately without any details.</source>
<translation type="unfinished"></translation>
</message>
@@ -63,12 +63,7 @@
<context>
<name>QObject</name>
<message>
<location filename="../runner/error.h" line="68"/>
<source>An unexpected C++ exception was thrown in python code.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../runner/error.h" line="117"/>
<location filename="../runner/error.h" line="75"/>
<source>An unknown exception was thrown in python code.</source>
<translation type="unfinished"></translation>
</message>

Some files were not shown because too many files have changed in this diff Show More