Add python interface for IPluginInstallerSimple.

This commit is contained in:
Mikaël Capelle
2020-05-02 00:30:17 +02:00
parent 6cececc7a0
commit c9115e7ad0
4 changed files with 372 additions and 93 deletions
+50 -24
View File
@@ -3,9 +3,12 @@
#include "gilock.h"
#include <QUrl>
#include <QWidget>
#include "pythonwrapperutilities.h"
#include "sipApiAccess.h"
#include <boost/variant.hpp>
namespace boost
{
// See bug https://connect.microsoft.com/VisualStudio/Feedback/Details/2852624
@@ -286,27 +289,55 @@ std::map<std::type_index, boost::any> IPluginGameWrapper::featureList() const
}
/// end IPluginGame Wrapper
/////////////////////////////////////
/// IPluginInstaller macro
#define COMMON_I_PLUGIN_INSTALLER_WRAPPER_DEFINITIONS(class_name) \
unsigned int class_name::priority() const { return basicWrapperFunctionImplementation<class_name, unsigned int>(this, "priority"); } \
bool class_name::isManualInstaller() const { return basicWrapperFunctionImplementation<class_name, bool>(this, "isManualInstaller"); } \
bool class_name::isArchiveSupported(std::shared_ptr<const IFileTree> tree) const { return basicWrapperFunctionImplementation<class_name, bool>(this, "isArchiveSupported", tree); }
/// end IPluginInstaller macro
/////////////////////////////////////
/// IPluginInstaller Wrapper
COMMON_I_PLUGIN_WRAPPER_DEFINITIONS(IPluginInstallerSimpleWrapper)
COMMON_I_PLUGIN_INSTALLER_WRAPPER_DEFINITIONS(IPluginInstallerSimpleWrapper)
IPluginInstaller::EInstallResult IPluginInstallerSimpleWrapper::install(
GuessedValue<QString>& modName, std::shared_ptr<IFileTree>& tree,
QString& version, int& nexusID)
{
namespace bpy = boost::python;
using return_type = boost::variant<IPluginInstaller::EInstallResult, std::shared_ptr<IFileTree>, boost::tuple<std::shared_ptr<IFileTree>, QString, int>> ;
auto ret = basicWrapperFunctionImplementation<IPluginInstallerSimpleWrapper, return_type>(this, "install", boost::ref(modName), tree, version, nexusID);
return boost::apply_visitor([&](auto const& t) {
using type = std::decay_t<decltype(t)>;
if constexpr (std::is_same_v<type, IPluginInstaller::EInstallResult>) {
return t;
}
else if constexpr (std::is_same_v<type, std::shared_ptr<IFileTree>>) {
tree = t;
return IPluginInstaller::RESULT_SUCCESS;
}
else if constexpr (std::is_same_v<type, boost::tuple<std::shared_ptr<IFileTree>, QString, int>>) {
tree = boost::get<0>(t);
version = boost::get<1>(t);
nexusID = boost::get<2>(t);
return IPluginInstaller::RESULT_SUCCESS;
}
else {
static_assert("Type not handled in boost::apply_visitor.");
}
}, ret);
}
/// end IPluginInstallerSimple Wrapper
/////////////////////////////////////
/// IPluginInstallerCustom Wrapper
COMMON_I_PLUGIN_WRAPPER_DEFINITIONS(IPluginInstallerCustomWrapper)
unsigned int IPluginInstallerCustomWrapper::priority() const
{
return basicWrapperFunctionImplementation<IPluginInstallerCustomWrapper, unsigned int>(this, "priority");
}
bool IPluginInstallerCustomWrapper::isManualInstaller() const
{
return basicWrapperFunctionImplementation<IPluginInstallerCustomWrapper, bool>(this, "isManualInstaller");
}
bool IPluginInstallerCustomWrapper::isArchiveSupported(const DirectoryTree &archiveTree) const
{
//return basicWrapperFunctionImplementation<IPluginInstallerCustomWrapper, bool>(this, "isArchiveSupported", archiveTree);
// This was a stub implementation when I got here, and a real one won't compile.
return false;
}
COMMON_I_PLUGIN_INSTALLER_WRAPPER_DEFINITIONS(IPluginInstallerCustomWrapper)
bool IPluginInstallerCustomWrapper::isArchiveSupported(const QString &archiveName) const
{
@@ -324,11 +355,6 @@ IPluginInstaller::EInstallResult IPluginInstallerCustomWrapper::install(GuessedV
return basicWrapperFunctionImplementation<IPluginInstallerCustomWrapper, IPluginInstaller::EInstallResult>(this, "install", modName, gameName, archiveName, version, modID);
}
void IPluginInstallerCustomWrapper::setParentWidget(QWidget *parent)
{
basicWrapperFunctionImplementation<IPluginInstallerCustomWrapper, void>(this, "setParentWidget", parent);
}
/// end IPluginInstallerCustom Wrapper
/////////////////////////////
/// IPluginModPage Wrapper
+30 -8
View File
@@ -132,25 +132,47 @@ protected:
};
#define COMMON_I_PLUGIN_INSTALLER_WRAPPER_DECLARATIONS public: \
using IPluginInstaller::parentWidget; \
using IPluginInstaller::manager; \
virtual unsigned int priority() const override; \
virtual bool isManualInstaller() const override; \
virtual bool isArchiveSupported(std::shared_ptr<const MOBase::IFileTree> tree) const override;
class IPluginInstallerSimpleWrapper : public MOBase::IPluginInstallerSimple, public boost::python::wrapper<MOBase::IPluginInstallerSimple>
{
Q_OBJECT
Q_INTERFACES(MOBase::IPlugin MOBase::IPluginInstaller MOBase::IPluginInstallerSimple)
COMMON_I_PLUGIN_WRAPPER_DECLARATIONS
COMMON_I_PLUGIN_INSTALLER_WRAPPER_DECLARATIONS
public:
static constexpr const char* className = "IPluginInstallerSimpleWrapper";
using boost::python::wrapper<MOBase::IPluginInstallerSimple>::get_override;
virtual EInstallResult install(MOBase::GuessedValue<QString>& modName, std::shared_ptr<MOBase::IFileTree>& tree,
QString& version, int& nexusID) override;
};
class IPluginInstallerCustomWrapper : public MOBase::IPluginInstallerCustom, public boost::python::wrapper<MOBase::IPluginInstallerCustom>
{
Q_OBJECT
Q_INTERFACES(MOBase::IPlugin MOBase::IPluginInstaller MOBase::IPluginInstallerCustom)
COMMON_I_PLUGIN_WRAPPER_DECLARATIONS
COMMON_I_PLUGIN_INSTALLER_WRAPPER_DECLARATIONS
public:
static constexpr const char* className = "IPluginInstallerCustomWrapper";
using boost::python::wrapper<MOBase::IPluginInstallerCustom>::get_override;
virtual unsigned int priority() const;
virtual bool isManualInstaller() const;
virtual bool isArchiveSupported(const MOBase::DirectoryTree &tree) const;
virtual bool isArchiveSupported(const QString &archiveName) const;
virtual std::set<QString> supportedExtensions() const;
virtual bool isArchiveSupported(const QString &archiveName) const override;
virtual std::set<QString> supportedExtensions() const override;
virtual EInstallResult install(MOBase::GuessedValue<QString> &modName, QString gameName, const QString &archiveName,
const QString &version, int modID);
virtual void setParentWidget(QWidget *parent);
const QString &version, int modID) override;
};
+39 -61
View File
@@ -25,9 +25,10 @@
#ifndef Q_MOC_RUN
#include <boost/python.hpp>
#include <boost/mp11.hpp>
#include "tuple_helper.h"
#endif
MOBase::IOrganizer *s_Organizer = nullptr;
@@ -69,6 +70,7 @@ IPythonRunner *CreatePythonRunner(MOBase::IOrganizer *moInfo, const QString &pyt
using namespace MOBase;
namespace bpy = boost::python;
namespace mp11 = boost::mp11;
struct QString_to_python_str
{
@@ -161,58 +163,6 @@ struct HANDLE_converters
};
template <typename T>
struct GuessedValue_converters
{
struct GuessedValue_to_python
{
static PyObject *convert(const GuessedValue<T> &var) {
bpy::list result;
const std::set<T> &values = var.variants();
for (auto iter = values.begin(); iter != values.end(); ++iter) {
result.append(bpy::make_tuple(*iter, GUESS_GOOD));
}
return bpy::incref(result.ptr());
}
};
struct GuessedValue_from_python
{
GuessedValue_from_python() {
bpy::converter::registry::push_back(&convertible, &construct, bpy::type_id<GuessedValue<T> >());
}
static void *convertible(PyObject *objPtr) {
if PyList_Check(objPtr) {
return objPtr;
} else {
return nullptr;
}
}
static void construct(PyObject *objPtr, bpy::converter::rvalue_from_python_stage1_data* data) {
void *storage = ((bpy::converter::rvalue_from_python_storage<GuessedValue<T> >*)data)->storage.bytes;
GuessedValue<T> *result = new (storage) GuessedValue<T>();
bpy::list source(bpy::handle<>(bpy::borrowed(objPtr)));
int length = bpy::len(source);
for (int i = 0; i < length; ++i) {
bpy::tuple cell = bpy::extract<bpy::tuple>(source[i]);
result->update(bpy::extract<T>(cell[0]), bpy::extract<EGuessQuality>(cell[1]));
}
data->convertible = storage;
}
};
GuessedValue_converters()
{
GuessedValue_from_python();
bpy::to_python_converter<GuessedValue<T>, GuessedValue_to_python>();
}
};
template<typename key_type, typename value_type>
struct QMap_converters
{
@@ -819,8 +769,19 @@ struct DowncastReturn {
};
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(updateWithQuality, MOBase::GuessedValue<QString>::update, 2, 2)
/**
* @brief Register implicit conversion from any of the decayed type from the variant
* to the variant type.
*/
template <class... Args>
void register_implicit_variant() {
mp11::mp_for_each<
mp11::mp_list<Args... >> (
[](auto t) {
// take advantage of built-in conversions
bpy::implicitly_convertible<std::decay_t<decltype(t)>, boost::variant<Args... >>();
});
}
BOOST_PYTHON_MODULE(mobase)
@@ -962,7 +923,9 @@ BOOST_PYTHON_MODULE(mobase)
// FileTreeEntry and IFileTree are only managed by shared ptr.
bpy::register_ptr_to_python<std::shared_ptr<FileTreeEntry>>();
bpy::register_ptr_to_python<std::shared_ptr<const FileTreeEntry>>();
bpy::register_ptr_to_python<std::shared_ptr<IFileTree>>();
bpy::register_ptr_to_python<std::shared_ptr<const IFileTree>>();
// FileTreeEntry Scope:
auto fileTreeEntryClass = bpy::class_<FileTreeEntry, std::shared_ptr<FileTreeEntry>, boost::noncopyable>("FileTreeEntry", bpy::no_init);
@@ -1162,11 +1125,15 @@ BOOST_PYTHON_MODULE(mobase)
.value("user", MOBase::GUESS_USER)
;
bpy::class_<MOBase::GuessedValue<QString>, boost::noncopyable>("GuessedString")
bpy::class_<MOBase::GuessedValue<QString>, boost::noncopyable>("GuessedString", bpy::no_init)
.def("update",
static_cast<GuessedValue<QString> &(GuessedValue<QString>::*)(const QString&, EGuessQuality)>(&GuessedValue<QString>::update),
bpy::return_value_policy<bpy::reference_existing_object>(), updateWithQuality())
static_cast<GuessedValue<QString>& (GuessedValue<QString>::*)(const QString&)>(&GuessedValue<QString>::update),
bpy::return_self<>())
.def("update",
static_cast<GuessedValue<QString>& (GuessedValue<QString>::*)(const QString&, EGuessQuality)>(&GuessedValue<QString>::update),
bpy::return_self<>())
.def("variants", &MOBase::GuessedValue<QString>::variants, bpy::return_value_policy<bpy::copy_const_reference>())
.def("__str__", &MOBase::GuessedValue<QString>::operator const QString&, bpy::return_value_policy<bpy::copy_const_reference>())
;
bpy::to_python_converter<IPluginList::PluginStates, QFlags_to_int<IPluginList::PluginState>>();
@@ -1315,6 +1282,8 @@ BOOST_PYTHON_MODULE(mobase)
.def("featureUnmanagedMods", &MOBase::IPluginGame::feature<UnmanagedMods>, bpy::return_value_policy<bpy::reference_existing_object>())
;
bpy::register_tuple<boost::tuple<std::shared_ptr<IFileTree>, QString, int>>();
register_implicit_variant<IPluginInstaller::EInstallResult, std::shared_ptr<IFileTree>, boost::tuple<std::shared_ptr<IFileTree>, QString, int>>();
bpy::enum_<MOBase::IPluginInstaller::EInstallResult>("InstallResult")
.value("success", MOBase::IPluginInstaller::RESULT_SUCCESS)
.value("failed", MOBase::IPluginInstaller::RESULT_FAILED)
@@ -1323,8 +1292,18 @@ BOOST_PYTHON_MODULE(mobase)
.value("notAttempted", MOBase::IPluginInstaller::RESULT_NOTATTEMPTED)
;
bpy::class_<IPluginInstallerSimpleWrapper, boost::noncopyable>("IPluginInstallerSimple")
.def("install", &IPluginInstallerSimple::install)
.def("getParentWidget", &IPluginInstallerSimpleWrapper::parentWidget, bpy::return_value_policy<bpy::reference_existing_object>())
.def("getManager", &IPluginInstallerSimpleWrapper::manager, bpy::return_value_policy<bpy::reference_existing_object>())
;
bpy::class_<IPluginInstallerCustomWrapper, boost::noncopyable>("IPluginInstallerCustom")
.def("setParentWidget", bpy::pure_virtual(&MOBase::IPluginInstallerCustom::setParentWidget))
.def("isArchiveSupported", &IPluginInstallerCustom::isArchiveSupported)
.def("supportedExtensions", &IPluginInstallerCustom::supportedExtensions)
.def("install", &IPluginInstallerCustom::install)
.def("getParentWidget", &IPluginInstallerCustomWrapper::parentWidget, bpy::return_value_policy<bpy::reference_existing_object>())
.def("getManager", &IPluginInstallerCustomWrapper::manager, bpy::return_value_policy<bpy::reference_existing_object>())
;
bpy::class_<IPluginModPageWrapper, boost::noncopyable>("IPluginModPage")
@@ -1338,8 +1317,6 @@ BOOST_PYTHON_MODULE(mobase)
.def("setParentWidget", bpy::pure_virtual(&MOBase::IPluginTool::setParentWidget))
;
GuessedValue_converters<QString>();
HANDLE_converters();
//bpy::to_python_converter<ModRepositoryFileInfo, ModRepositoryFileInfo_to_python_dict>();
@@ -1521,6 +1498,7 @@ QList<QObject*> PythonRunner::instantiate(const QString &pluginName)
// Must try the wrapper because it's only a plugin extension interface in C++, so doesn't extend QObject
TRY_PLUGIN_TYPE(IPluginFileMapperWrapper, pluginObj);
TRY_PLUGIN_TYPE(IPluginInstallerCustom, pluginObj);
TRY_PLUGIN_TYPE(IPluginInstallerSimple, pluginObj);
TRY_PLUGIN_TYPE(IPluginModPage, pluginObj);
TRY_PLUGIN_TYPE(IPluginPreview, pluginObj);
TRY_PLUGIN_TYPE(IPluginTool, pluginObj);
+253
View File
@@ -0,0 +1,253 @@
// From: https://pyplusplus.readthedocs.io/en/latest/troubleshooting_guide/automatic_conversion/tuples.hpp.html
// Copyright 2004-2007 Roman Yakovenko.
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef TUPLES_HPP_16_JAN_2007
#define TUPLES_HPP_16_JAN_2007
#include <boost/python.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/python/object.hpp> //len function
#include <boost/mpl/int.hpp>
#include <boost/mpl/next.hpp>
/**
* Converts boost::tuples::tuple<...> to\from Python tuple
*
* The conversion is done "on-the-fly", you should only register the conversion
* with your tuple classes.
* For example:
*
* typedef boost::tuples::tuple< int, double, std::string > triplet;
* boost::python::register_tuple< triplet >();
*
* That's all. After this point conversion to\from next types will be handled
* by Boost.Python library:
*
* triplet
* triplet& ( return type only )
* const triplet
* const triplet&
*
* Implementation description.
* The conversion uses Boost.Python custom r-value converters. r-value converters
* is very powerful and undocumented feature of the library. The only documentation
* we have is http://boost.org/libs/python/doc/v2/faq.html#custom_string .
*
* The conversion consists from two parts: "to" and "from".
*
* "To" conversion
* The "to" part is pretty easy and well documented ( http://docs.python.org/api/api.html ).
* You should use Python C API to create an instance of a class and than you
* initialize the relevant members of the instance.
*
* "From" conversion
* Lets start from analyzing one of the use case Boost.Python library have to
* deal with:
*
* void do_smth( const triplet& arg ){...}
*
* In order to allow calling this function from Python, the library should keep
* parameter "arg" alive until the function returns. In other words, the library
* should provide instances life-time management. The provided interface is not
* ideal and could be improved. You have to implement two functions:
*
* void* convertible( PyObject* obj )
* Checks whether the "obj" could be converted to an instance of the desired
* class. If true, the function should return "obj", otherwise NULL
*
* void construct( PyObject* obj, converter::rvalue_from_python_stage1_data* data)
* Constructs the instance of the desired class. This function will be called
* if and only if "convertible" function returned true. The first argument
* is Python object, which was passed as parameter to "convertible" function.
* The second object is some kind of memory allocator for one object. Basically
* it keeps a memory chunk. You will use the memory for object allocation.
*
* For some unclear for me reason, the library implements "C style Inheritance"
* ( http://www.embedded.com/97/fe29712.htm ). So, in order to create new
* object in the storage you have to cast to the "right" class:
*
* typedef converter::rvalue_from_python_storage<your_type_t> storage_t;
* storage_t* the_storage = reinterpret_cast<storage_t*>( data );
* void* memory_chunk = the_storage->storage.bytes;
*
* "memory_chunk" points to the memory, where the instance will be allocated.
*
* In order to create object at specific location, you should use placement new
* operator:
*
* your_type_t* instance = new (memory_chunk) your_type_t();
*
* Now, you can continue to initialize the instance.
*
* instance->set_xyz = read xyz from obj
*
* If "your_type_t" constructor requires some arguments, "read" the Python
* object before you call the constructor:
*
* xyz_type xyz = read xyz from obj
* your_type_t* instance = new (memory_chunk) your_type_t(xyz);
*
* Hint:
* In most case you don't really need\have to work with C Python API. Let
* Boost.Python library to do some work for you!
*
**/
namespace boost {
namespace python {
namespace details {
//Small helper function, introduced to allow short syntax for index incrementing
template< int index>
typename mpl::next< mpl::int_< index > >::type increment_index() {
typedef typename mpl::next< mpl::int_< index > >::type next_index_type;
return next_index_type();
}
}
template< class TTuple >
struct to_py_tuple {
typedef mpl::int_< tuples::length< TTuple >::value > length_type;
static PyObject* convert(const TTuple& c_tuple) {
list values;
//add all c_tuple items to "values" list
convert_impl(c_tuple, values, mpl::int_< 0 >(), length_type());
//create Python tuple from the list
return incref(python::tuple(values).ptr());
}
private:
template< int index, int length >
static void
convert_impl(const TTuple& c_tuple, list& values, mpl::int_< index >, mpl::int_< length >) {
values.append(c_tuple.template get< index >());
convert_impl(c_tuple, values, details::increment_index<index>(), length_type());
}
template< int length >
static void
convert_impl(const TTuple&, list& values, mpl::int_< length >, mpl::int_< length >)
{}
};
template< class TTuple>
struct from_py_sequence {
typedef TTuple tuple_type;
typedef mpl::int_< tuples::length< TTuple >::value > length_type;
static void*
convertible(PyObject* py_obj) {
if (!PySequence_Check(py_obj)) {
return 0;
}
if (!PyObject_HasAttrString(py_obj, "__len__")) {
return 0;
}
python::object py_sequence(handle<>(borrowed(py_obj)));
if (tuples::length< TTuple >::value != len(py_sequence)) {
return 0;
}
if (convertible_impl(py_sequence, mpl::int_< 0 >(), length_type())) {
return py_obj;
}
else {
return 0;
}
}
static void
construct(PyObject* py_obj, converter::rvalue_from_python_stage1_data* data) {
typedef converter::rvalue_from_python_storage<TTuple> storage_t;
storage_t* the_storage = reinterpret_cast<storage_t*>(data);
void* memory_chunk = the_storage->storage.bytes;
TTuple* c_tuple = new (memory_chunk) TTuple();
data->convertible = memory_chunk;
python::object py_sequence(handle<>(borrowed(py_obj)));
construct_impl(py_sequence, *c_tuple, mpl::int_< 0 >(), length_type());
}
static TTuple to_c_tuple(PyObject* py_obj) {
if (!convertible(py_obj)) {
throw std::runtime_error("Unable to construct boost::tuples::tuple from Python object!");
}
TTuple c_tuple;
python::object py_sequence(handle<>(borrowed(py_obj)));
construct_impl(py_sequence, c_tuple, mpl::int_< 0 >(), length_type());
return c_tuple;
}
private:
template< int index, int length >
static bool
convertible_impl(const python::object& py_sequence, mpl::int_< index >, mpl::int_< length >) {
typedef typename tuples::element< index, TTuple>::type element_type;
object element = py_sequence[index];
extract<element_type> type_checker(element);
if (!type_checker.check()) {
return false;
}
else {
return convertible_impl(py_sequence, details::increment_index<index>(), length_type());
}
}
template< int length >
static bool
convertible_impl(const python::object& py_sequence, mpl::int_< length >, mpl::int_< length >) {
return true;
}
template< int index, int length >
static void
construct_impl(const python::object& py_sequence, TTuple& c_tuple, mpl::int_< index >, mpl::int_< length >) {
typedef typename tuples::element< index, TTuple>::type element_type;
object element = py_sequence[index];
c_tuple.template get< index >() = extract<element_type>(element);
construct_impl(py_sequence, c_tuple, details::increment_index<index>(), length_type());
}
template< int length >
static void
construct_impl(const python::object& py_sequence, TTuple& c_tuple, mpl::int_< length >, mpl::int_< length >)
{}
};
template< class TTuple>
void register_tuple() {
to_python_converter< TTuple, to_py_tuple<TTuple> >();
converter::registry::push_back(&from_py_sequence<TTuple>::convertible
, &from_py_sequence<TTuple>::construct
, type_id<TTuple>());
};
}
} //boost::python
#endif//TUPLES_HPP_16_JAN_2007