mirror of
https://github.com/ModOrganizer2/modorganizer-plugin_python.git
synced 2026-07-27 14:03:33 -07:00
Add argument wrapper for directory and files.
This commit is contained in:
@@ -56,9 +56,12 @@ PYBIND11_MODULE(mobase, m)
|
||||
|
||||
// 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"));
|
||||
m.def("getFileVersion", wrap_for_filepath(&MOBase::getFileVersion),
|
||||
py::arg("filepath"));
|
||||
m.def("getProductVersion", wrap_for_filepath(&MOBase::getProductVersion),
|
||||
py::arg("executable"));
|
||||
m.def("getIconForExecutable", wrap_for_filepath(&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:
|
||||
|
||||
@@ -1,21 +1,91 @@
|
||||
#ifndef PYTHON_PYBIND11_ALL_H
|
||||
#define PYTHON_PYBIND11_ALL_H
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
#include <QDir>
|
||||
|
||||
#include <pybind11/operators.h>
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/stl.h>
|
||||
#include <pybind11/stl/filesystem.h>
|
||||
|
||||
#include "pybind11_utils/functional.h"
|
||||
|
||||
#include "pybind11_qt/pybind11_qt.h"
|
||||
|
||||
#include "pybind11_utils/arg_wrapper.h"
|
||||
#include "pybind11_utils/functional.h"
|
||||
#include "pybind11_utils/shared_cpp_owner.h"
|
||||
|
||||
#include <isavegame.h>
|
||||
#include <pluginrequirements.h>
|
||||
|
||||
namespace mo2::python {
|
||||
|
||||
// struct to wrap "path" object between C++ and Python, allowing to mix
|
||||
// pathlib.Path, QString, QFileInfo and QDir when possible
|
||||
//
|
||||
class BasePathWrapper {
|
||||
protected:
|
||||
// we store a std::filesystem::path because it can be converted to most thing,
|
||||
// even though we lose basic functionality on QDir (name filter, etc.)
|
||||
std::filesystem::path path_;
|
||||
|
||||
public:
|
||||
BasePathWrapper() = default;
|
||||
BasePathWrapper(BasePathWrapper const&) = default;
|
||||
BasePathWrapper& operator=(BasePathWrapper const&) = default;
|
||||
BasePathWrapper(BasePathWrapper&&) = default;
|
||||
BasePathWrapper& operator=(BasePathWrapper&&) = default;
|
||||
|
||||
BasePathWrapper(std::filesystem::path const& path) : path_{path} {}
|
||||
BasePathWrapper(QString const& path) : path_{path.toStdWString()} {}
|
||||
|
||||
operator QString() const { return QString::fromStdWString(path_.native()); }
|
||||
operator std::filesystem::path() const { return path_; }
|
||||
};
|
||||
|
||||
class FileWrapper : public BasePathWrapper {
|
||||
public:
|
||||
using BasePathWrapper::BasePathWrapper;
|
||||
|
||||
FileWrapper(QFileInfo const& fileInfo)
|
||||
: BasePathWrapper(fileInfo.filesystemFilePath())
|
||||
{
|
||||
}
|
||||
|
||||
operator QFileInfo() const { return QFileInfo(path_); }
|
||||
};
|
||||
|
||||
class DirectoryWrapper : public BasePathWrapper {
|
||||
public:
|
||||
using BasePathWrapper::BasePathWrapper;
|
||||
|
||||
DirectoryWrapper(QDir const& dir) : BasePathWrapper(dir.filesystemPath()) {}
|
||||
|
||||
operator QDir() const { return QDir(path_); }
|
||||
};
|
||||
|
||||
template <std::size_t... Is, class Fn>
|
||||
auto wrap_for_filepath(Fn&& fn)
|
||||
{
|
||||
return mo2::python::wrap_arguments<FileWrapper, Is...>(std::forward<Fn>(fn));
|
||||
}
|
||||
|
||||
template <std::size_t... Is, class Fn>
|
||||
auto wrap_for_directory(Fn&& fn)
|
||||
{
|
||||
return mo2::python::wrap_arguments<DirectoryWrapper, Is...>(
|
||||
std::forward<Fn>(fn));
|
||||
}
|
||||
|
||||
} // namespace mo2::python
|
||||
|
||||
MO2_PYBIND11_SHARED_CPP_HOLDER(MOBase::IPluginRequirement)
|
||||
MO2_PYBIND11_SHARED_CPP_HOLDER(MOBase::ISaveGame)
|
||||
|
||||
MO2_PYBIND11_WRAP_ARGUMENT_CASTER(mo2::python::FileWrapper, QFileInfo,
|
||||
std::filesystem::path, QString);
|
||||
MO2_PYBIND11_WRAP_ARGUMENT_CASTER(mo2::python::DirectoryWrapper, QDir,
|
||||
std::filesystem::path, QString);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -30,6 +30,7 @@ namespace mo2::python {
|
||||
namespace py = pybind11;
|
||||
|
||||
using namespace pybind11::literals;
|
||||
using namespace mo2::python;
|
||||
|
||||
void add_versioninfo_classes(py::module_ m)
|
||||
{
|
||||
@@ -90,9 +91,10 @@ namespace mo2::python {
|
||||
void add_executable_classes(py::module_ m)
|
||||
{
|
||||
py::class_<ExecutableInfo>(m, "ExecutableInfo")
|
||||
.def(py::init<const QString&, const QFileInfo&>(), "title"_a, "binary"_a)
|
||||
.def(py::init<const QString&, const FileWrapper&>(), "title"_a, "binary"_a)
|
||||
.def("withArgument", &ExecutableInfo::withArgument, "argument"_a)
|
||||
.def("withWorkingDirectory", &ExecutableInfo::withWorkingDirectory,
|
||||
.def("withWorkingDirectory",
|
||||
wrap_for_directory(&ExecutableInfo::withWorkingDirectory),
|
||||
"directory"_a)
|
||||
.def("withSteamAppId", &ExecutableInfo::withSteamAppId, "app_id"_a)
|
||||
.def("asCustom", &ExecutableInfo::asCustom)
|
||||
@@ -448,16 +450,17 @@ namespace mo2::python {
|
||||
.def("setPersistent", &IOrganizer::setPersistent, "plugin_name"_a, "key"_a,
|
||||
"value"_a, "sync"_a = true)
|
||||
.def("pluginDataPath", &IOrganizer::pluginDataPath)
|
||||
.def("installMod", &IOrganizer::installMod,
|
||||
.def("installMod", wrap_for_filepath<1>(&IOrganizer::installMod),
|
||||
py::return_value_policy::reference, "filename"_a,
|
||||
"name_suggestion"_a = "")
|
||||
.def("resolvePath", &IOrganizer::resolvePath, "filename"_a)
|
||||
.def("resolvePath", wrap_for_filepath(&IOrganizer::resolvePath),
|
||||
"filename"_a)
|
||||
.def("listDirectories", &IOrganizer::listDirectories, "directory"_a)
|
||||
|
||||
// "provide multiple overloads of findFiles
|
||||
.def(
|
||||
"findFiles",
|
||||
[](const IOrganizer* o, QString const& p,
|
||||
[](const IOrganizer* o, DirectoryWrapper const& p,
|
||||
std::function<bool(QString const&)> const& f) {
|
||||
return o->findFiles(p, f);
|
||||
},
|
||||
@@ -474,19 +477,21 @@ namespace mo2::python {
|
||||
// single-character strings:
|
||||
.def(
|
||||
"findFiles",
|
||||
[](const IOrganizer* o, QString const& p, const QStringList& gf) {
|
||||
[](const IOrganizer* o, DirectoryWrapper const& p,
|
||||
const QStringList& gf) {
|
||||
return o->findFiles(p, gf);
|
||||
},
|
||||
"path"_a, "patterns"_a)
|
||||
.def(
|
||||
"findFiles",
|
||||
[](const IOrganizer* o, QString const& p, const QString& f) {
|
||||
[](const IOrganizer* o, DirectoryWrapper const& p, const QString& f) {
|
||||
return o->findFiles(p, QStringList{f});
|
||||
},
|
||||
"path"_a, "pattern"_a)
|
||||
|
||||
.def("getFileOrigins", &IOrganizer::getFileOrigins, "filename"_a)
|
||||
.def("findFileInfos", &IOrganizer::findFileInfos, "path"_a, "filter"_a)
|
||||
.def("findFileInfos", wrap_for_directory(&IOrganizer::findFileInfos),
|
||||
"path"_a, "filter"_a)
|
||||
|
||||
.def("virtualFileTree", &IOrganizer::virtualFileTree)
|
||||
|
||||
@@ -503,9 +508,9 @@ namespace mo2::python {
|
||||
// argument to a return-tuple for waitForApplication
|
||||
.def(
|
||||
"startApplication",
|
||||
[](IOrganizer* o, const QString& executable, const QStringList& args,
|
||||
const QString& cwd, const QString& profile,
|
||||
const QString& forcedCustomOverwrite,
|
||||
[](IOrganizer* o, const FileWrapper& executable,
|
||||
const QStringList& args, const DirectoryWrapper& cwd,
|
||||
const QString& profile, const QString& forcedCustomOverwrite,
|
||||
bool ignoreCustomOverwrite) -> std::uintptr_t {
|
||||
return (std::uintptr_t)o->startApplication(
|
||||
executable, args, cwd, profile, forcedCustomOverwrite,
|
||||
@@ -659,7 +664,7 @@ namespace mo2::python {
|
||||
.def(
|
||||
"installArchive",
|
||||
[](IInstallationManager* m, GuessedValue<QString> modName,
|
||||
QString archive, int modId) {
|
||||
FileWrapper archive, int modId) {
|
||||
auto result = m->installArchive(modName, archive, modId);
|
||||
return std::make_tuple(result, static_cast<QString>(modName),
|
||||
modId);
|
||||
|
||||
@@ -155,12 +155,12 @@ namespace mo2::python {
|
||||
public:
|
||||
QString BinaryName() const override
|
||||
{
|
||||
PYBIND11_OVERRIDE_PURE(QString, ScriptExtender, BinaryName, );
|
||||
PYBIND11_OVERRIDE_PURE(FileWrapper, ScriptExtender, BinaryName, );
|
||||
}
|
||||
|
||||
QString PluginPath() const override
|
||||
{
|
||||
PYBIND11_OVERRIDE_PURE(QString, ScriptExtender, PluginPath, );
|
||||
PYBIND11_OVERRIDE_PURE(DirectoryWrapper, ScriptExtender, PluginPath, );
|
||||
}
|
||||
|
||||
QString loaderName() const override
|
||||
@@ -170,7 +170,7 @@ namespace mo2::python {
|
||||
|
||||
QString loaderPath() const override
|
||||
{
|
||||
PYBIND11_OVERRIDE_PURE(QString, ScriptExtender, loaderPath, );
|
||||
PYBIND11_OVERRIDE_PURE(FileWrapper, ScriptExtender, loaderPath, );
|
||||
}
|
||||
|
||||
QString savegameExtension() const override
|
||||
@@ -206,11 +206,15 @@ namespace mo2::python {
|
||||
}
|
||||
QFileInfo referenceFile(const QString& modName) const override
|
||||
{
|
||||
PYBIND11_OVERRIDE_PURE(QFileInfo, UnmanagedMods, referenceFile, modName);
|
||||
PYBIND11_OVERRIDE_PURE(FileWrapper, UnmanagedMods, referenceFile, modName);
|
||||
}
|
||||
QStringList secondaryFiles(const QString& modName) const override
|
||||
{
|
||||
PYBIND11_OVERRIDE_PURE(QStringList, UnmanagedMods, secondaryFiles, modName);
|
||||
auto result = [&] {
|
||||
PYBIND11_OVERRIDE_PURE(QList<FileWrapper>, UnmanagedMods,
|
||||
secondaryFiles, modName);
|
||||
}();
|
||||
return QList<QString>(result.begin(), result.end());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -370,11 +370,11 @@ namespace mo2::python {
|
||||
}
|
||||
QDir gameDirectory() const override
|
||||
{
|
||||
PYBIND11_OVERRIDE_PURE(QDir, IPluginGame, gameDirectory, );
|
||||
PYBIND11_OVERRIDE_PURE(DirectoryWrapper, IPluginGame, gameDirectory, );
|
||||
}
|
||||
QDir dataDirectory() const override
|
||||
{
|
||||
PYBIND11_OVERRIDE_PURE(QDir, IPluginGame, dataDirectory, );
|
||||
PYBIND11_OVERRIDE_PURE(DirectoryWrapper, IPluginGame, dataDirectory, );
|
||||
}
|
||||
void setGamePath(const QString& path) override
|
||||
{
|
||||
@@ -382,11 +382,11 @@ namespace mo2::python {
|
||||
}
|
||||
QDir documentsDirectory() const override
|
||||
{
|
||||
PYBIND11_OVERRIDE_PURE(QDir, IPluginGame, documentsDirectory, );
|
||||
PYBIND11_OVERRIDE_PURE(DirectoryWrapper, IPluginGame, documentsDirectory, );
|
||||
}
|
||||
QDir savesDirectory() const override
|
||||
{
|
||||
PYBIND11_OVERRIDE_PURE(QDir, IPluginGame, savesDirectory, );
|
||||
PYBIND11_OVERRIDE_PURE(DirectoryWrapper, IPluginGame, savesDirectory, );
|
||||
}
|
||||
QList<ExecutableInfo> executables() const override
|
||||
{
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace mo2::python {
|
||||
public:
|
||||
QString getFilepath() const override
|
||||
{
|
||||
PYBIND11_OVERRIDE_PURE(QString, ISaveGame, getFilepath, );
|
||||
PYBIND11_OVERRIDE_PURE(FileWrapper, ISaveGame, getFilepath, );
|
||||
}
|
||||
|
||||
QDateTime getCreationTime() const override
|
||||
|
||||
@@ -80,6 +80,15 @@ namespace pybind11::detail::qt {
|
||||
}
|
||||
}
|
||||
|
||||
const sipTypeDef* type =
|
||||
qt::sipAPI()->api_find_type(MetaData<QClass>::class_name);
|
||||
if (type == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (!qt::sipAPI()->api_can_convert_to_type(src.ptr(), type, 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// this would transfer responsibility for deconstructing the
|
||||
// object to C++, but pybind11 assumes l-value converters (such
|
||||
// as this) don't do that instead, this should be called within
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
#ifndef PYTHON_PYBIND11_UTILS_FUNCTION_WRAPPER_H
|
||||
#define PYTHON_PYBIND11_UTILS_FUNCTION_WRAPPER_H
|
||||
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
|
||||
#include <pybind11/pybind11.h>
|
||||
|
||||
namespace mo2::python {
|
||||
|
||||
namespace detail {
|
||||
|
||||
// simple helper class that expose a ::type attribute which is U is I is in Is,
|
||||
// V otherwise
|
||||
template <class U, class V, std::size_t I, class Is>
|
||||
struct wrap_arg;
|
||||
|
||||
template <class U, class V, std::size_t I, std::size_t... Is>
|
||||
struct wrap_arg<U, V, I, std::index_sequence<Is...>> {
|
||||
using type =
|
||||
std::conditional_t<std::disjunction_v<std::bool_constant<I == Is>...>,
|
||||
U, V>;
|
||||
};
|
||||
|
||||
// helper type for wrap_arg
|
||||
template <class U, class A, std::size_t I, class Is>
|
||||
using wrap_arg_t = typename wrap_arg<U, A, I, Is>::type;
|
||||
|
||||
template <class T, std::size_t... Is, std::size_t... AIs, class Fn, class R,
|
||||
class... Args>
|
||||
auto wrap_fn_impl(std::index_sequence<Is...>, Fn&& fn, R (*sg)(Args...),
|
||||
std::index_sequence<AIs...>)
|
||||
{
|
||||
return [fn = std::forward<Fn>(fn)](
|
||||
wrap_arg_t<T, Args, AIs, std::index_sequence<Is...>>... args) {
|
||||
return std::invoke(fn, std::forward<decltype(args)>(args)...);
|
||||
};
|
||||
}
|
||||
template <class T, class... Args, std::size_t... Is>
|
||||
auto make_convertible_index_sequence(std::index_sequence<Is...>)
|
||||
{
|
||||
return std::index_sequence<(std::is_convertible_v<Args, T> ? Is : -1)...>{};
|
||||
}
|
||||
|
||||
template <class T, std::size_t... Is, class Fn, class R, class... Args>
|
||||
auto wrap_fn_impl(Fn&& fn, R (*sg)(Args...))
|
||||
{
|
||||
if constexpr (sizeof...(Is) == 0) {
|
||||
return wrap_fn_impl<T>(make_convertible_index_sequence<T, Args...>(
|
||||
std::make_index_sequence<sizeof...(Args)>{}),
|
||||
std::forward<Fn>(fn), sg,
|
||||
std::make_index_sequence<sizeof...(Args)>{});
|
||||
}
|
||||
else {
|
||||
return wrap_fn_impl<T>(std::index_sequence<Is...>{},
|
||||
std::forward<Fn>(fn), sg,
|
||||
std::make_index_sequence<sizeof...(Args)>{});
|
||||
}
|
||||
}
|
||||
|
||||
template <class Type, class... WrappedTypes>
|
||||
struct load_wrapped_argument_helper;
|
||||
|
||||
template <class Type>
|
||||
struct load_wrapped_argument_helper<Type> {
|
||||
static bool load(Type& value, pybind11::handle src, bool convert)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template <class Type, class WrappedType, class... WrappedTypes>
|
||||
struct load_wrapped_argument_helper<Type, WrappedType, WrappedTypes...> {
|
||||
static bool load(Type& value, pybind11::handle src, bool convert)
|
||||
{
|
||||
pybind11::detail::make_caster<WrappedType> caster;
|
||||
|
||||
if (caster.load(src, convert)) {
|
||||
value = Type{static_cast<WrappedType>(caster)};
|
||||
return true;
|
||||
}
|
||||
|
||||
return load_wrapped_argument_helper<Type, WrappedTypes...>::load(
|
||||
value, src, convert);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <class T, std::size_t... Is, class Fn>
|
||||
auto wrap_arguments(Fn&& fn)
|
||||
{
|
||||
return detail::wrap_fn_impl<T, Is...>(
|
||||
std::forward<Fn>(fn), (pybind11::detail::function_signature_t<Fn>*)nullptr);
|
||||
}
|
||||
|
||||
template <class T, std::size_t... Is, class R, class... Args>
|
||||
auto wrap_arguments(R (*fn)(Args...))
|
||||
{
|
||||
return detail::wrap_fn_impl<T, Is...>(fn, fn);
|
||||
}
|
||||
|
||||
template <class T, std::size_t... Is, class R, class C, class... Args>
|
||||
auto wrap_arguments(R (C::*fn)(Args...))
|
||||
{
|
||||
return detail::wrap_fn_impl<T, Is...>(fn, (R(*)(C*, Args...)) nullptr);
|
||||
}
|
||||
|
||||
template <class T, std::size_t... Is, class R, class C, class... Args>
|
||||
auto wrap_arguments(R (C::*fn)(Args...) &)
|
||||
{
|
||||
return detail::wrap_fn_impl<T, Is...>(fn, (R(*)(C*, Args...)) nullptr);
|
||||
}
|
||||
|
||||
template <class T, std::size_t... Is, class R, class C, class... Args>
|
||||
auto wrap_arguments(R (C::*fn)(Args...) const)
|
||||
{
|
||||
return detail::wrap_fn_impl<T, Is...>(fn, (R(*)(const C*, Args...)) nullptr);
|
||||
}
|
||||
|
||||
template <class T, std::size_t... Is, class R, class C, class... Args>
|
||||
auto wrap_arguments(R (C::*fn)(Args...) const&)
|
||||
{
|
||||
return detail::wrap_fn_impl<T, Is...>(fn, (R(*)(const C*, Args...)) nullptr);
|
||||
}
|
||||
|
||||
} // namespace mo2::python
|
||||
|
||||
#define MO2_PYBIND11_WRAP_ARGUMENT_CASTER(Type, ...) \
|
||||
namespace pybind11::detail { \
|
||||
template <> \
|
||||
struct type_caster<Type> { \
|
||||
PYBIND11_TYPE_CASTER(Type, const_name(#Type)); \
|
||||
bool load(handle src, bool convert) \
|
||||
{ \
|
||||
return mo2::python::detail::load_wrapped_argument_helper< \
|
||||
Type, __VA_ARGS__>::load(value, src, convert); \
|
||||
} \
|
||||
}; \
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,69 @@
|
||||
#include "pybind11_utils/arg_wrapper.h"
|
||||
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/stl.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
// wrapper that can be constructed from
|
||||
class Wrapper {
|
||||
std::string value;
|
||||
|
||||
public:
|
||||
Wrapper() = default;
|
||||
|
||||
Wrapper(Wrapper const&) = default;
|
||||
Wrapper(Wrapper&&) = default;
|
||||
Wrapper& operator=(Wrapper const&) = default;
|
||||
Wrapper& operator=(Wrapper&&) = default;
|
||||
|
||||
template <class U, std::enable_if_t<std::is_convertible_v<U, std::string>, int> = 0>
|
||||
Wrapper(U&& u) : value{std::forward<U>(u)}
|
||||
{
|
||||
}
|
||||
|
||||
Wrapper(int u) : value{std::to_string(u)} {}
|
||||
|
||||
operator int() const { return std::stoi(value); }
|
||||
operator std::string() const { return value; }
|
||||
};
|
||||
|
||||
MO2_PYBIND11_WRAP_ARGUMENT_CASTER(Wrapper, int, std::string);
|
||||
|
||||
template <std::size_t... Is, class Fn>
|
||||
auto wrap(Fn&& fn)
|
||||
{
|
||||
return mo2::python::wrap_arguments<Wrapper, Is...>(std::forward<Fn>(fn));
|
||||
}
|
||||
|
||||
std::string fn1(std::string const& value)
|
||||
{
|
||||
return value + "-" + value;
|
||||
}
|
||||
|
||||
int fn2(int value)
|
||||
{
|
||||
return value * 2;
|
||||
}
|
||||
|
||||
std::string fn3(int value, std::vector<int> values, std::string const& name)
|
||||
{
|
||||
return name + "-" + std::to_string(value + values.size());
|
||||
}
|
||||
|
||||
PYBIND11_MODULE(argument_wrapper, m)
|
||||
{
|
||||
m.def("fn1_raw", &fn1);
|
||||
m.def("fn1_wrap", wrap(&fn1));
|
||||
m.def("fn1_wrap_0", wrap<0>(&fn1));
|
||||
|
||||
m.def("fn2_raw", &fn2);
|
||||
m.def("fn2_wrap", wrap(&fn2));
|
||||
m.def("fn2_wrap_0", wrap<0>(&fn2));
|
||||
|
||||
m.def("fn3_raw", &fn3);
|
||||
m.def("fn3_wrap", wrap(&fn3));
|
||||
m.def("fn3_wrap_0", wrap<0>(&fn3));
|
||||
m.def("fn3_wrap_2", wrap<2>(&fn3));
|
||||
m.def("fn3_wrap_0_2", wrap<0, 2>(&fn3));
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
import mobase
|
||||
import pytest
|
||||
|
||||
m = pytest.importorskip("mobase_tests.argument_wrapper")
|
||||
|
||||
|
||||
def test_argument_wrapper_fn1():
|
||||
assert m.fn1_raw("hello") == "hello-hello"
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
m.fn1_raw(1)
|
||||
|
||||
assert m.fn1_wrap("hello") == "hello-hello"
|
||||
assert m.fn1_wrap(32) == "32-32"
|
||||
|
||||
assert m.fn1_wrap_0("world") == "world-world"
|
||||
assert m.fn1_wrap_0(45) == "45-45"
|
||||
|
||||
|
||||
def test_argument_wrapper_fn2():
|
||||
assert m.fn2_raw(33) == 66
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
m.fn2_raw("12")
|
||||
|
||||
assert m.fn2_wrap("15") == 30
|
||||
assert m.fn2_wrap(32) == 64
|
||||
|
||||
assert m.fn2_wrap_0("-15") == -30
|
||||
assert m.fn2_wrap_0(45) == 90
|
||||
|
||||
|
||||
def test_argument_wrapper_fn3():
|
||||
assert m.fn3_raw(33, [], "hello") == "hello-33"
|
||||
assert m.fn3_raw(33, [1, 2], "hello") == "hello-35"
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
m.fn3_raw("12", [], "hello")
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
m.fn3_raw(36, [], 136)
|
||||
|
||||
assert m.fn3_wrap(14, [1, 2], "world") == "world-16"
|
||||
assert m.fn3_wrap("15", [0], "woot") == "woot-16"
|
||||
assert m.fn3_wrap(17, [], 33) == "33-17"
|
||||
assert m.fn3_wrap("15", [], 44) == "44-15"
|
||||
|
||||
assert m.fn3_wrap_0_2(14, [1, 2], "world") == "world-16"
|
||||
assert m.fn3_wrap_0_2("15", [0], "woot") == "woot-16"
|
||||
assert m.fn3_wrap_0_2(17, [], 33) == "33-17"
|
||||
assert m.fn3_wrap_0_2("15", [], 44) == "44-15"
|
||||
|
||||
assert m.fn3_wrap_0(14, [1, 2], "world") == "world-16"
|
||||
assert m.fn3_wrap_0("15", [], "w00t") == "w00t-15"
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
m.fn3_wrap_0(14, [], 12)
|
||||
|
||||
assert m.fn3_wrap_2(14, [1, 2], "world") == "world-16"
|
||||
assert m.fn3_wrap_2(15, [], 18) == "18-15"
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
m.fn3_wrap_2("14", [], 12)
|
||||
@@ -0,0 +1,45 @@
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import mobase
|
||||
import pytest
|
||||
from PyQt6.QtCore import QDir, QFileInfo
|
||||
|
||||
|
||||
def test_filepath_wrappers():
|
||||
|
||||
# TBC that this works everywhere
|
||||
version = ".".join(map(str, sys.version_info[:3]))
|
||||
|
||||
# from string, ok
|
||||
assert mobase.getProductVersion(sys.executable) == version
|
||||
|
||||
# from path, ok
|
||||
assert mobase.getProductVersion(Path(sys.executable)) == version
|
||||
|
||||
# from QDir, ko
|
||||
with pytest.raises(TypeError):
|
||||
mobase.getProductVersion(QDir(sys.executable))
|
||||
|
||||
|
||||
def test_executableinfo():
|
||||
info = mobase.ExecutableInfo("exe", QFileInfo(sys.executable))
|
||||
assert info.binary() == QFileInfo(sys.executable)
|
||||
|
||||
info = mobase.ExecutableInfo("exe", sys.executable)
|
||||
assert info.binary() == QFileInfo(sys.executable)
|
||||
|
||||
info = mobase.ExecutableInfo("exe", Path(sys.executable))
|
||||
assert info.binary() == QFileInfo(sys.executable)
|
||||
|
||||
info.withWorkingDirectory(Path(__file__).parent)
|
||||
assert info.workingDirectory() == QFileInfo(__file__).dir()
|
||||
|
||||
info.withWorkingDirectory(".")
|
||||
assert info.workingDirectory() == QDir(".")
|
||||
|
||||
info.withWorkingDirectory(Path("."))
|
||||
assert info.workingDirectory() == QDir(".")
|
||||
|
||||
info.withWorkingDirectory(".")
|
||||
assert info.workingDirectory() == QDir(".")
|
||||
Reference in New Issue
Block a user