Skip to content

Commit

Permalink
Add libsolv.Database Bindings and tests (#3186)
Browse files Browse the repository at this point in the history
  • Loading branch information
AntoinePrv authored Feb 12, 2024
1 parent 4db0c06 commit 00219a5
Show file tree
Hide file tree
Showing 11 changed files with 498 additions and 140 deletions.
9 changes: 9 additions & 0 deletions libmamba/include/mamba/fs/filesystem.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ namespace mamba::fs
{
public:

using value_type = char;
using string_type = std::basic_string<value_type>;

u8path() = default;

// Copy is allowed.
Expand Down Expand Up @@ -303,6 +306,12 @@ namespace mamba::fs
return to_utf8(m_path);
}

// Returns a default encoded string.
decltype(auto) native() const
{
return m_path.native();
}

// Returns an utf-8 string.
operator std::string() const
{
Expand Down
34 changes: 32 additions & 2 deletions libmamba/include/mamba/solver/problems_graph.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,22 @@ namespace mamba::solver

private:

auto base() const -> const Base&;

auto remove_asym(const key_type& a, const key_type& b) -> bool;

template <typename TT>
friend auto operator==(const conflict_map<TT>& lhs, const conflict_map<TT>& rhs) -> bool;
template <typename TT>
friend auto operator!=(const conflict_map<TT>& lhs, const conflict_map<TT>& rhs) -> bool;
};

template <typename T>
auto operator==(const conflict_map<T>& lhs, const conflict_map<T>& rhs) -> bool;

template <typename T>
auto operator!=(const conflict_map<T>& lhs, const conflict_map<T>& rhs) -> bool;

/**
* A directed graph of the packages involved in a libsolv conflict.
*/
Expand Down Expand Up @@ -313,6 +326,12 @@ namespace mamba::solver
return inserted;
}

template <typename T>
auto conflict_map<T>::base() const -> const Base&
{
return static_cast<const Base&>(*this);
}

template <typename T>
auto conflict_map<T>::remove_asym(const key_type& a, const key_type& b) -> bool
{
Expand Down Expand Up @@ -355,6 +374,18 @@ namespace mamba::solver
return true;
}

template <typename T>
auto operator==(const conflict_map<T>& lhs, const conflict_map<T>& rhs) -> bool
{
return lhs.base() == rhs.base();
}

template <typename T>
auto operator!=(const conflict_map<T>& lhs, const conflict_map<T>& rhs) -> bool
{
return !(lhs == rhs);
}

/*********************************
* Implementation of NamedList *
*********************************/
Expand All @@ -369,5 +400,4 @@ namespace mamba::solver
}
}
}

#endif // MAMBA_PROBLEMS_GRAPH_HPP
#endif
12 changes: 6 additions & 6 deletions libmamba/src/solver/libsolv/unsolvable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,19 +41,19 @@ namespace mamba::solver::libsolv
return *m_solver;
}

auto UnSolvable::problems(Database& mpool) const -> std::vector<std::string>
auto UnSolvable::problems(Database& db) const -> std::vector<std::string>
{
auto& pool = Database::Impl::get(mpool);
auto& pool = Database::Impl::get(db);
std::vector<std::string> problems;
solver().for_each_problem_id([&](solv::ProblemId pb)
{ problems.emplace_back(solver().problem_to_string(pool, pb)); }
);
return problems;
}

auto UnSolvable::problems_to_str(Database& mpool) const -> std::string
auto UnSolvable::problems_to_str(Database& db) const -> std::string
{
auto& pool = Database::Impl::get(mpool);
auto& pool = Database::Impl::get(db);
std::stringstream problems;
problems << "Encountered problems while solving:\n";
solver().for_each_problem_id(
Expand All @@ -63,9 +63,9 @@ namespace mamba::solver::libsolv
return problems.str();
}

auto UnSolvable::all_problems_to_str(Database& mpool) const -> std::string
auto UnSolvable::all_problems_to_str(Database& db) const -> std::string
{
auto& pool = Database::Impl::get(mpool);
auto& pool = Database::Impl::get(db);
std::stringstream problems;
solver().for_each_problem_id(
[&](solv::ProblemId pb)
Expand Down
63 changes: 14 additions & 49 deletions libmambapy/src/libmambapy/bindings/expected_caster.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@
//
// The full license is in the file LICENSE, distributed with this software.

#include <type_traits>
#include <utility>
#include <variant>

#include <pybind11/cast.h>
#include <pybind11/pybind11.h>
Expand All @@ -20,73 +18,40 @@ namespace PYBIND11_NAMESPACE
{
namespace detail
{
namespace
{
template <
typename Expected,
typename T = typename Expected::value_type,
typename E = typename Expected::error_type>
auto expected_to_variant(Expected&& expected) -> std::variant<T, E>
{
if (expected)
{
return { std::forward<Expected>(expected).value() };
}
return { std::forward<Expected>(expected).error() };
}

template <
typename Variant,
typename T = std::decay_t<decltype(std::get<0>(std::declval<Variant>()))>,
typename E = std::decay_t<decltype(std::get<1>(std::declval<Variant>()))>>
auto expected_to_variant(Variant&& var) -> tl::expected<T, E>
{
static_assert(std::variant_size_v<Variant> == 2);
return std::visit(
[](auto&& v) -> tl::expected<T, E> { return { std::forward<deltype(v)>(v) }; },
var
);
}
}

/**
* A caster for tl::expected that converts to a union.
*
* The caster works by converting to a the expected to a variant and then calls the
* variant caster.
*
* A future direction could be considered to wrap the union into a Python "Expected",
* with methods such as ``and_then``, ``or_else``, and thowing method like ``value``
* and ``error``.
* A caster for tl::expected that throws on unexpected.
*/
template <typename T, typename E>
struct type_caster<tl::expected<T, E>>
{
using value_type = tl::expected<T, E>;
using variant_type = std::variant<T, E>;
using caster_type = variant_caster<variant_type>;
using value_type = T;

auto load(handle src, bool convert) -> bool
{
auto caster = caster_type();
auto caster = make_caster<T>();
if (caster.load(src, convert))
{
value = variant_to_expected(cast_op<variant_type>(std::move(caster)));
value = cast_op<T>(std::move(caster));
return true;
}
return false;
}

template <typename Expected>
static auto cast(Expected&& src, return_value_policy policy, handle parent) -> handle
{
return caster_type::cast(expected_to_variant(std::forward<Expected>(src)), policy, parent);
if (src)
{
return make_caster<T>::cast(std::forward<Expected>(src).value(), policy, parent);
}
else
{
throw std::forward<Expected>(src).error();
}
}

PYBIND11_TYPE_CASTER(
value_type,
const_name(R"(Union[)") + detail::concat(make_caster<T>::name, make_caster<E>::name)
+ const_name(R"(])")
);
PYBIND11_TYPE_CASTER(value_type, detail::concat(make_caster<T>::name, make_caster<E>::name));
};
}
}
Expand Down
96 changes: 28 additions & 68 deletions libmambapy/src/libmambapy/bindings/legacy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,14 @@
#include "mamba/core/transaction.hpp"
#include "mamba/core/util_os.hpp"
#include "mamba/core/virtual_packages.hpp"
#include "mamba/solver/libsolv/database.hpp"
#include "mamba/solver/libsolv/repo_info.hpp"
#include "mamba/solver/problems_graph.hpp"
#include "mamba/validation/tools.hpp"
#include "mamba/validation/update_framework_v0_6.hpp"

#include "bindings.hpp"
#include "expected_caster.hpp"
#include "flat_set_caster.hpp"
#include "path_caster.hpp"
#include "utils.hpp"

namespace py = pybind11;
Expand Down Expand Up @@ -247,10 +246,28 @@ bind_submodule_impl(pybind11::module_ m)
throw std::runtime_error( //
"Use Pool.add_repo_from_repodata_json or Pool.add_repo_from_native_serialization"
" instead and cache with Pool.native_serialize_repo."
" Also consider load_subdir_in_pool for a high_level function to load"
" subdir index and manage cache, and load_installed_packages_in_pool for loading"
" prefix packages."
"The Repo class itself has been moved to libmambapy.solver.libsolv.RepoInfo."
" Also consider load_subdir_in_database for a high_level function to load"
" subdir index and manage cache, and load_installed_packages_in_database for"
" loading prefix packages."
" The Repo class itself has been moved to libmambapy.solver.libsolv.RepoInfo."
);
}
));

struct PoolV2Migrator
{
};

py::class_<PoolV2Migrator>(m, "Pool").def(py::init(
[](py::args, py::kwargs) -> PoolV2Migrator
{
throw std::runtime_error( //
"libmambapy.Pool has been moved to libmambapy.solver.libsolv.Database."
" The database contains functions to directly load packages, from a list or a"
" repodata.json."
" High level functions such as libmambapy.load_subdir_in_database and"
" libmambapy.load_installed_packages_in_database are also available to work"
" with other Mamba objects and Context parameters."
);
}
));
Expand Down Expand Up @@ -380,76 +397,19 @@ bind_submodule_impl(pybind11::module_ m)

py::add_ostream_redirect(m, "ostream_redirect");

py::class_<solver::libsolv::Database>(m, "Pool")
.def(py::init<specs::ChannelResolveParams>(), py::arg("channel_params"))
.def(
"set_logger",
&solver::libsolv::Database::set_logger,
py::call_guard<py::gil_scoped_acquire>()
)
.def(
"add_repo_from_repodata_json",
&solver::libsolv::Database::add_repo_from_repodata_json,
py::arg("path"),
py::arg("url"),
py::arg("add_pip_as_python_dependency") = solver::libsolv::PipAsPythonDependency::No,
py::arg("use_only_tar_bz2") = solver::libsolv::UseOnlyTarBz2::No,
py::arg("repodata_parsers") = solver::libsolv::RepodataParser::Mamba
)
.def(
"add_repo_from_native_serialization",
&solver::libsolv::Database::add_repo_from_native_serialization,
py::arg("path"),
py::arg("expected"),
py::arg("add_pip_as_python_dependency") = solver::libsolv::PipAsPythonDependency::No
)
.def(
"add_repo_from_packages",
[](solver::libsolv::Database& db,
py::iterable packages,
std::string_view name,
solver::libsolv::PipAsPythonDependency add)
{
// TODO(C++20): No need to copy in a vector, simply transform the input range.
auto pkg_infos = std::vector<specs::PackageInfo>();
for (py::handle pkg : packages)
{
pkg_infos.push_back(pkg.cast<specs::PackageInfo>());
}
return db.add_repo_from_packages(pkg_infos, name, add);
},
py::arg("packages"),
py::arg("name") = "",
py::arg("add_pip_as_python_dependency") = solver::libsolv::PipAsPythonDependency::No
)
.def(
"native_serialize_repo",
&solver::libsolv::Database::native_serialize_repo,
py::arg("repo"),
py::arg("path"),
py::arg("metadata")
)
.def("set_installed_repo", &solver::libsolv::Database::set_installed_repo, py::arg("repo"))
.def(
"set_repo_priority",
&solver::libsolv::Database::set_repo_priority,
py::arg("repo"),
py::arg("priorities")
);

m.def(
"load_subdir_in_pool",
"load_subdir_in_database",
&load_subdir_in_database,
py::arg("context"),
py::arg("pool"),
py::arg("database"),
py::arg("subdir")
);

m.def(
"load_installed_packages_in_pool",
"load_installed_packages_in_database",
&load_installed_packages_in_database,
py::arg("context"),
py::arg("pool"),
py::arg("database"),
py::arg("prefix_data")
);

Expand Down Expand Up @@ -534,7 +494,7 @@ bind_submodule_impl(pybind11::module_ m)
"create_repo",
[](SubdirData& subdir, solver::libsolv::Database& db) -> solver::libsolv::RepoInfo
{
deprecated("Use `load_subdir_in_pool` instead", "2.0");
deprecated("Use libmambapy.load_subdir_in_database instead", "2.0");
return extract(load_subdir_in_database(mambapy::singletons.context(), db, subdir));
}
)
Expand Down
21 changes: 21 additions & 0 deletions libmambapy/src/libmambapy/bindings/path_caster.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright (c) 2024, QuantStack and Mamba Contributors
//
// Distributed under the terms of the BSD 3-Clause License.
//
// The full license is in the file LICENSE, distributed with this software.

#include <pybind11/pybind11.h>
#include <pybind11/stl/filesystem.h>

#include "mamba/fs/filesystem.hpp"

namespace PYBIND11_NAMESPACE
{
namespace detail
{
template <>
struct type_caster<mamba::fs::u8path> : path_caster<mamba::fs::u8path>
{
};
}
}
Loading

0 comments on commit 00219a5

Please sign in to comment.