Improve Python conflicts (#2318)

Better handle the (almost) cyclic conflict due to the reversal of the dependencies bewteen python and python_abi.
This commit is contained in:
Antoine Prouvost 2023-03-07 16:19:55 +01:00 committed by GitHub
parent 0919fbd514
commit 66c721c220
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 810 additions and 468 deletions

View File

@ -41,11 +41,6 @@ namespace mamba
std::size_t build_number
);
PackageInfo(const PackageInfo&) = default;
PackageInfo(PackageInfo&&) noexcept = default;
PackageInfo& operator=(const PackageInfo&) = default;
PackageInfo& operator=(PackageInfo&&) noexcept = default;
bool operator==(const PackageInfo& other) const;
nlohmann::json json_record() const;

View File

@ -74,15 +74,13 @@ namespace mamba
public:
using dependency_graph = DiGraph<PackageInfo>;
using package_list = dependency_graph::node_list;
using package_view_list = std::vector<package_list::const_iterator>;
query_result(QueryType type, const std::string& query, dependency_graph&& dep_graph);
~query_result() = default;
query_result(const query_result&);
query_result& operator=(const query_result&);
query_result(const query_result&) = default;
query_result& operator=(const query_result&) = default;
query_result(query_result&&) = default;
query_result& operator=(query_result&&) = default;
@ -104,15 +102,18 @@ namespace mamba
private:
using node_id = dependency_graph::node_id;
using package_id_list = std::vector<node_id>;
using ordered_package_list = std::map<std::string, package_id_list>;
void reset_pkg_view_list();
std::string get_package_repr(const PackageInfo& pkg) const;
QueryType m_type;
std::string m_query;
dependency_graph m_dep_graph;
package_view_list m_pkg_view_list;
using ordered_package_list = std::map<std::string, package_view_list>;
ordered_package_list m_ordered_pkg_list;
package_id_list m_pkg_id_list = {};
ordered_package_list m_ordered_pkg_id_list = {};
};
} // namespace mamba

View File

@ -9,6 +9,7 @@
#include <array>
#include <functional>
#include <initializer_list>
#include <optional>
#include <ostream>
#include <string>
@ -19,7 +20,6 @@
#include <vector>
#include <fmt/color.h>
#include <solv/solver.h>
#include "mamba/core/match_spec.hpp"
#include "mamba/core/package_info.hpp"
@ -42,6 +42,7 @@ namespace mamba
using typename Base::value_type;
conflict_map() = default;
conflict_map(std::initializer_list<std::pair<T, T>> conflicts_pairs);
using Base::empty;
using Base::size;
@ -55,7 +56,13 @@ namespace mamba
const_iterator end() const noexcept;
using Base::clear;
void add(const key_type& a, const key_type& b);
bool add(const key_type& a, const key_type& b);
bool remove(const key_type& a, const key_type& b);
bool remove(const key_type& a);
private:
bool remove_asym(const key_type& a, const key_type& b);
};
/**
@ -70,31 +77,14 @@ namespace mamba
};
struct PackageNode : PackageInfo
{
std::optional<SolverRuleinfo> problem_type = {};
PackageNode(const PackageNode&) = default;
PackageNode(PackageNode&&) noexcept = default;
PackageNode& operator=(const PackageNode&) = default;
PackageNode& operator=(PackageNode&&) noexcept = default;
};
struct UnresolvedDependencyNode : MatchSpec
{
SolverRuleinfo problem_type;
UnresolvedDependencyNode(const UnresolvedDependencyNode&) = default;
UnresolvedDependencyNode(UnresolvedDependencyNode&&) noexcept = default;
UnresolvedDependencyNode& operator=(const UnresolvedDependencyNode&) = default;
UnresolvedDependencyNode& operator=(UnresolvedDependencyNode&&) noexcept = default;
};
struct ConstraintNode : MatchSpec
{
static constexpr SolverRuleinfo problem_type = SOLVER_RULE_PKG_CONSTRAINS;
ConstraintNode(const ConstraintNode&) = default;
ConstraintNode(ConstraintNode&&) noexcept = default;
ConstraintNode& operator=(const ConstraintNode&) = default;
ConstraintNode& operator=(ConstraintNode&&) noexcept = default;
};
using node_t = std::variant<RootNode, PackageNode, UnresolvedDependencyNode, ConstraintNode>;
using edge_t = MatchSpec;
@ -118,6 +108,11 @@ namespace mamba
node_id m_root_node;
};
/**
* Hand-crafted hurisitics to simplify conflicts in messy situations.
*/
ProblemsGraph simplify_conflicts(const ProblemsGraph& pbs);
class CompressedProblemsGraph
{
public:
@ -252,9 +247,6 @@ namespace mamba
std::array<std::string_view, 4> indents = { "", " ", "├─ ", "└─ " };
};
std::ostream& print_problem_summary_msg(std::ostream& out, const CompressedProblemsGraph& pbs);
std::string problem_summary_msg(const CompressedProblemsGraph& pbs);
std::ostream& print_problem_tree_msg(
std::ostream& out,
const CompressedProblemsGraph& pbs,
@ -267,6 +259,15 @@ namespace mamba
* Implementation of conflict_map *
************************************/
template <typename T>
conflict_map<T>::conflict_map(std::initializer_list<std::pair<T, T>> conflicts_pairs)
{
for (const auto& [a, b] : conflicts_pairs)
{
add(a, b);
}
}
template <typename T>
bool conflict_map<T>::has_conflict(const key_type& a) const
{
@ -298,10 +299,56 @@ namespace mamba
}
template <typename T>
void conflict_map<T>::add(const key_type& a, const key_type& b)
bool conflict_map<T>::add(const key_type& a, const key_type& b)
{
Base::operator[](a).insert(b);
Base::operator[](b).insert(a);
auto [_, inserted] = Base::operator[](a).insert(b);
if (a != b)
{
Base::operator[](b).insert(a);
}
return inserted;
}
template <typename T>
bool conflict_map<T>::remove_asym(const key_type& a, const key_type& b)
{
auto iter = Base::find(a);
if (iter == Base::end())
{
return false;
}
auto& cflcts = iter->second;
const bool erased = cflcts.erase(b);
if (cflcts.empty())
{
Base::erase(a);
}
return erased;
};
template <typename T>
bool conflict_map<T>::remove(const key_type& a, const key_type& b)
{
return remove_asym(a, b) && ((a == b) || remove_asym(b, a));
}
template <typename T>
bool conflict_map<T>::remove(const key_type& a)
{
auto a_iter = Base::find(a);
if (a_iter == Base::end())
{
return false;
}
for (const auto& b : a_iter->second)
{
if (a != b) // Cannot modify while we iterate on it
{
remove_asym(b, a);
}
}
Base::erase(a);
return true;
}
/*********************************

View File

@ -33,6 +33,7 @@ namespace mamba
using typename Base::allocator_type;
using typename Base::const_iterator;
using typename Base::const_reverse_iterator;
using typename Base::size_type;
using typename Base::value_type;
using key_compare = Compare;
using value_compare = Compare;
@ -86,6 +87,10 @@ namespace mamba
template <typename InputIterator>
void insert(InputIterator first, InputIterator last);
const_iterator erase(const_iterator pos);
const_iterator erase(const_iterator first, const_iterator last);
size_type erase(const value_type& value);
private:
key_compare m_compare;
@ -138,20 +143,22 @@ namespace mamba
using node_t = Node;
using node_id = std::size_t;
using node_list = std::vector<node_t>;
using node_map = std::map<node_id, node_t>;
using node_id_list = vector_set<node_id>;
using adjacency_list = std::vector<node_id_list>;
node_id add_node(const node_t& value);
node_id add_node(node_t&& value);
void add_edge(node_id from, node_id to);
bool add_edge(node_id from, node_id to);
bool remove_edge(node_id from, node_id to);
bool remove_node(node_id id);
bool empty() const;
std::size_t number_of_nodes() const;
std::size_t number_of_edges() const;
std::size_t number_of_nodes() const noexcept;
std::size_t number_of_edges() const noexcept;
std::size_t in_degree(node_id id) const noexcept;
std::size_t out_degree(node_id id) const noexcept;
const node_list& nodes() const;
const node_map& nodes() const;
const node_t& node(node_id id) const;
node_t& node(node_id id);
const node_id_list& successors(node_id id) const;
@ -163,16 +170,18 @@ namespace mamba
// TODO C++20 better to return a range since this search cannot be interupted from the
// visitor
template <typename UnaryFunc>
UnaryFunc for_each_node_id(UnaryFunc func) const;
template <typename BinaryFunc>
BinaryFunc for_each_edge(BinaryFunc func) const;
BinaryFunc for_each_edge_id(BinaryFunc func) const;
template <typename UnaryFunc>
UnaryFunc for_each_leaf(UnaryFunc func) const;
UnaryFunc for_each_leaf_id(UnaryFunc func) const;
template <typename UnaryFunc>
UnaryFunc for_each_leaf_from(node_id source, UnaryFunc func) const;
UnaryFunc for_each_leaf_id_from(node_id source, UnaryFunc func) const;
template <typename UnaryFunc>
UnaryFunc for_each_root(UnaryFunc func) const;
UnaryFunc for_each_root_id(UnaryFunc func) const;
template <typename UnaryFunc>
UnaryFunc for_each_root_from(node_id source, UnaryFunc func) const;
UnaryFunc for_each_root_id_from(node_id source, UnaryFunc func) const;
// TODO C++20 better to return a range since this search cannot be interupted from the
// visitor
@ -181,6 +190,8 @@ namespace mamba
protected:
using derived_t = Derived;
DiGraphBase() = default;
DiGraphBase(const DiGraphBase&) = default;
DiGraphBase(DiGraphBase&&) = default;
@ -188,14 +199,10 @@ namespace mamba
DiGraphBase& operator=(DiGraphBase&&) = default;
~DiGraphBase() = default;
Derived& derived_cast()
{
return static_cast<Derived&>(*this);
}
const Derived& derived_cast() const
{
return static_cast<const Derived&>(*this);
}
node_id number_of_node_id() const noexcept;
Derived& derived_cast();
const Derived& derived_cast() const;
private:
@ -219,8 +226,11 @@ namespace mamba
const adjacency_list& successors
) const;
node_list m_node_list;
// Source of truth for exsising nodes
node_map m_node_map;
// May contains empty slots after `remove_node`
adjacency_list m_predecessors;
// May contains empty slots after `remove_node`
adjacency_list m_successors;
std::size_t m_number_of_edges = 0;
};
@ -265,18 +275,46 @@ namespace mamba
};
template <typename Node, typename Edge = void>
class DiGraph : public DiGraphBase<Node, DiGraph<Node, Edge>>
class DiGraph : private DiGraphBase<Node, DiGraph<Node, Edge>>
{
public:
using Base = DiGraphBase<Node, DiGraph<Node, Edge>>;
using edge_t = Edge;
using typename Base::adjacency_list;
using typename Base::node_id;
using typename Base::node_id_list;
using typename Base::node_map;
using typename Base::node_t;
using edge_t = Edge;
using edge_id = std::pair<node_id, node_id>;
using edge_map = std::map<edge_id, edge_t>;
void add_edge(node_id from, node_id to, const edge_t& data);
void add_edge(node_id from, node_id to, edge_t&& data);
using Base::empty;
using Base::has_edge;
using Base::has_node;
using Base::in_degree;
using Base::node;
using Base::nodes;
using Base::number_of_edges;
using Base::number_of_nodes;
using Base::out_degree;
using Base::predecessors;
using Base::successors;
using Base::for_each_edge_id;
using Base::for_each_leaf_id;
using Base::for_each_leaf_id_from;
using Base::for_each_node_id;
using Base::for_each_root_id;
using Base::for_each_root_id_from;
using Base::depth_first_search;
using Base::add_node;
bool add_edge(node_id from, node_id to, const edge_t& data);
bool add_edge(node_id from, node_id to, edge_t&& data);
bool remove_edge(node_id from, node_id to);
bool remove_node(node_id id);
const edge_map& edges() const;
const edge_t& edge(node_id from, node_id to) const;
@ -286,8 +324,10 @@ namespace mamba
private:
friend class DiGraphBase<Node, DiGraph<Node, Edge>>; // required for private CRTP
template <typename T>
void add_edge_impl(node_id from, node_id to, T&& data);
bool add_edge_impl(node_id from, node_id to, T&& data);
edge_map m_edges;
};
@ -422,13 +462,40 @@ namespace mamba
template <typename K, typename C, typename A>
template <typename U>
auto vector_set<K, C, A>::insert_impl(U&& value) -> std::pair<const_iterator, bool>
{
auto it = std::lower_bound(begin(), end(), value, m_compare);
if ((it != end()) && (key_eq(*it, value)))
{
return { it, false };
}
it = Base::insert(it, std::forward<U>(value));
return { it, true };
}
template <typename K, typename C, typename A>
auto vector_set<K, C, A>::erase(const_iterator pos) -> const_iterator
{
// No need to sort or remove duplicates again
return Base::erase(pos);
}
template <typename K, typename C, typename A>
auto vector_set<K, C, A>::erase(const_iterator first, const_iterator last) -> const_iterator
{
// No need to sort or remove duplicates again
return Base::erase(first, last);
}
template <typename K, typename C, typename A>
auto vector_set<K, C, A>::erase(const value_type& value) -> size_type
{
auto it = std::lower_bound(begin(), end(), value, m_compare);
if ((it == end()) || (!(key_eq(*it, value))))
{
Base::insert(it, std::forward<U>(value));
return 0;
}
return { it, false };
erase(it);
return 1;
}
template <typename K, typename C, typename A>
@ -455,13 +522,13 @@ namespace mamba
}
template <typename N, typename G>
auto DiGraphBase<N, G>::number_of_nodes() const -> std::size_t
auto DiGraphBase<N, G>::number_of_nodes() const noexcept -> std::size_t
{
return m_node_list.size();
return m_node_map.size();
}
template <typename N, typename G>
auto DiGraphBase<N, G>::number_of_edges() const -> std::size_t
auto DiGraphBase<N, G>::number_of_edges() const noexcept -> std::size_t
{
return m_number_of_edges;
}
@ -479,21 +546,21 @@ namespace mamba
}
template <typename N, typename G>
auto DiGraphBase<N, G>::nodes() const -> const node_list&
auto DiGraphBase<N, G>::nodes() const -> const node_map&
{
return m_node_list;
return m_node_map;
}
template <typename N, typename G>
auto DiGraphBase<N, G>::node(node_id id) const -> const node_t&
{
return m_node_list[id];
return m_node_map.at(id);
}
template <typename N, typename G>
auto DiGraphBase<N, G>::node(node_id id) -> node_t&
{
return m_node_list[id];
return m_node_map.at(id);
}
template <typename N, typename G>
@ -523,7 +590,7 @@ namespace mamba
template <typename N, typename G>
auto DiGraphBase<N, G>::has_node(node_id id) const -> bool
{
return id < number_of_nodes();
return nodes().count(id) > 0;
}
template <typename N, typename G>
@ -545,64 +612,129 @@ namespace mamba
}
template <typename N, typename G>
void DiGraphBase<N, G>::add_edge(node_id from, node_id to)
template <class V>
auto DiGraphBase<N, G>::add_node_impl(V&& value) -> node_id
{
const node_id id = number_of_node_id();
m_node_map.emplace(id, std::forward<V>(value));
m_successors.push_back(node_id_list());
m_predecessors.push_back(node_id_list());
return id;
}
template <typename N, typename G>
bool DiGraphBase<N, G>::remove_node(node_id id)
{
if (!has_node(id))
{
return false;
}
const auto succs = successors(id); // Cannot iterate on object being modified
for (const auto& to : succs)
{
remove_edge(id, to);
}
const auto preds = predecessors(id); // Cannot iterate on object being modified
for (const auto& from : preds)
{
remove_edge(from, id);
}
m_node_map.erase(id);
return true;
}
template <typename N, typename G>
bool DiGraphBase<N, G>::add_edge(node_id from, node_id to)
{
if (has_edge(from, to))
{
return false;
}
m_successors[from].insert(to);
m_predecessors[to].insert(from);
++m_number_of_edges;
return true;
}
template <typename N, typename G>
bool DiGraphBase<N, G>::remove_edge(node_id from, node_id to)
{
if (!has_edge(from, to))
{
return false;
}
m_successors[from].erase(to);
m_predecessors[to].erase(from);
--m_number_of_edges;
return true;
}
template <typename N, typename G>
template <typename UnaryFunc>
UnaryFunc DiGraphBase<N, G>::for_each_node_id(UnaryFunc func) const
{
for (const auto& [i, _] : m_node_map)
{
func(i);
}
return func;
}
template <typename N, typename G>
template <typename BinaryFunc>
BinaryFunc DiGraphBase<N, G>::for_each_edge(BinaryFunc func) const
BinaryFunc DiGraphBase<N, G>::for_each_edge_id(BinaryFunc func) const
{
const auto n_nodes = number_of_nodes();
for (node_id i = 0; i < n_nodes; ++i)
{
for (node_id j : successors(i))
for_each_node_id(
[&](node_id i)
{
func(i, j);
for (node_id j : successors(i))
{
func(i, j);
}
}
}
);
return func;
}
template <typename N, typename G>
template <typename UnaryFunc>
UnaryFunc DiGraphBase<N, G>::for_each_leaf(UnaryFunc func) const
UnaryFunc DiGraphBase<N, G>::for_each_leaf_id(UnaryFunc func) const
{
const auto n_nodes = number_of_nodes();
for (node_id i = 0; i < n_nodes; ++i)
{
if (out_degree(i) == 0)
for_each_node_id(
[&](node_id i)
{
func(i);
if (out_degree(i) == 0)
{
func(i);
}
}
}
);
return func;
}
template <typename N, typename G>
template <typename UnaryFunc>
UnaryFunc DiGraphBase<N, G>::for_each_root(UnaryFunc func) const
UnaryFunc DiGraphBase<N, G>::for_each_root_id(UnaryFunc func) const
{
const auto n_nodes = number_of_nodes();
for (node_id i = 0; i < n_nodes; ++i)
{
if (in_degree(i) == 0)
for_each_node_id(
[&](node_id i)
{
func(i);
if (in_degree(i) == 0)
{
func(i);
}
}
}
);
return func;
}
template <typename N, typename G>
template <typename UnaryFunc>
UnaryFunc DiGraphBase<N, G>::for_each_leaf_from(node_id source, UnaryFunc func) const
UnaryFunc DiGraphBase<N, G>::for_each_leaf_id_from(node_id source, UnaryFunc func) const
{
using graph_t = DiGraphBase<N, G>;
struct LeafVisitor : default_visitor<graph_t>
struct LeafVisitor : default_visitor<derived_t>
{
UnaryFunc& m_func;
@ -611,7 +743,7 @@ namespace mamba
{
}
void start_node(node_id n, const graph_t& g)
void start_node(node_id n, const derived_t& g)
{
if (g.out_degree(n) == 0)
{
@ -626,10 +758,9 @@ namespace mamba
template <typename N, typename G>
template <typename UnaryFunc>
UnaryFunc DiGraphBase<N, G>::for_each_root_from(node_id source, UnaryFunc func) const
UnaryFunc DiGraphBase<N, G>::for_each_root_id_from(node_id source, UnaryFunc func) const
{
using graph_t = DiGraphBase<N, G>;
struct RootVisitor : default_visitor<graph_t>
struct RootVisitor : default_visitor<derived_t>
{
UnaryFunc& m_func;
@ -638,7 +769,7 @@ namespace mamba
{
}
void start_node(node_id n, const graph_t& g)
void start_node(node_id n, const derived_t& g)
{
if (g.in_degree(n) == 0)
{
@ -657,21 +788,11 @@ namespace mamba
{
if (!empty())
{
visited_list status(m_node_list.size(), visited::no);
visited_list status(number_of_node_id(), visited::no);
depth_first_search_impl(visitor, node, status, reverse ? m_predecessors : m_successors);
}
}
template <typename N, typename G>
template <class V>
auto DiGraphBase<N, G>::add_node_impl(V&& value) -> node_id
{
m_node_list.push_back(std::forward<V>(value));
m_successors.push_back(node_id_list());
m_predecessors.push_back(node_id_list());
return m_node_list.size() - 1u;
}
template <typename N, typename G>
template <class V>
void DiGraphBase<N, G>::depth_first_search_impl(
@ -705,26 +826,41 @@ namespace mamba
visitor.finish_node(node, derived_cast());
}
template <typename N, typename G>
auto DiGraphBase<N, G>::number_of_node_id() const noexcept -> node_id
{
// Not number_of_nodes because due to remove nodes it may be larger
return m_successors.size();
}
template <typename N, typename G>
auto DiGraphBase<N, G>::derived_cast() -> derived_t&
{
return static_cast<derived_t&>(*this);
}
template <typename N, typename G>
auto DiGraphBase<N, G>::derived_cast() const -> const derived_t&
{
return static_cast<const derived_t&>(*this);
}
/*******************************
* Algorithms implementation *
*******************************/
template <typename Node, typename Derived>
auto is_reachable(
const DiGraphBase<Node, Derived>& graph,
typename DiGraphBase<Node, Derived>::node_id source,
typename DiGraphBase<Node, Derived>::node_id target
) -> bool
template <typename Graph>
auto
is_reachable(const Graph& graph, typename Graph::node_id source, typename Graph::node_id target)
-> bool
{
using graph_t = DiGraphBase<Node, Derived>;
using node_id = typename graph_t::node_id;
struct : default_visitor<graph_t>
struct : default_visitor<Graph>
{
using node_id = typename Graph::node_id;
node_id target;
bool target_visited = false;
void start_node(node_id node, const graph_t&)
void start_node(node_id node, const Graph&)
{
target_visited = target_visited || (node == target);
}
@ -739,24 +875,50 @@ namespace mamba
*********************************/
template <typename N, typename E>
void DiGraph<N, E>::add_edge(node_id from, node_id to, const edge_t& data)
bool DiGraph<N, E>::add_edge(node_id from, node_id to, const edge_t& data)
{
add_edge_impl(from, to, data);
return add_edge_impl(from, to, data);
}
template <typename N, typename E>
void DiGraph<N, E>::add_edge(node_id from, node_id to, edge_t&& data)
bool DiGraph<N, E>::add_edge(node_id from, node_id to, edge_t&& data)
{
add_edge_impl(from, to, std::move(data));
return add_edge_impl(from, to, std::move(data));
}
template <typename N, typename E>
template <typename T>
void DiGraph<N, E>::add_edge_impl(node_id from, node_id to, T&& data)
bool DiGraph<N, E>::add_edge_impl(node_id from, node_id to, T&& data)
{
Base::add_edge(from, to);
auto ledge_id = std::make_pair(from, to);
m_edges.insert(std::make_pair(ledge_id, std::forward<T>(data)));
if (const bool added = Base::add_edge(from, to); added)
{
auto l_edge_id = std::pair(from, to);
m_edges.insert(std::pair(l_edge_id, std::forward<T>(data)));
return true;
}
return false;
}
template <typename N, typename E>
bool DiGraph<N, E>::remove_edge(node_id from, node_id to)
{
m_edges.erase({ from, to }); // No-op if edge does not exists
return Base::remove_edge(from, to);
}
template <typename N, typename E>
bool DiGraph<N, E>::remove_node(node_id id)
{
// No-op if edge does not exists
for (const auto& to : successors(id))
{
m_edges.erase({ id, to });
}
for (const auto& from : predecessors(id))
{
m_edges.erase({ from, id });
}
return Base::remove_node(id);
}
template <typename N, typename E>

View File

@ -151,57 +151,60 @@ namespace mamba
m_pool.get().create_whatprovides();
}
auto print_solvable = [](auto& pkg)
namespace
{
auto out = Console::stream();
std::string header = fmt::format("{} {} {}", pkg->name, pkg->version, pkg->build_string);
fmt::print(out, "{:^40}\n{:-^{}}\n\n", header, "", header.size() > 40 ? header.size() : 40);
static constexpr const char* fmtstring = " {:<15} {}\n";
fmt::print(out, fmtstring, "File Name", pkg->fn);
fmt::print(out, fmtstring, "Name", pkg->name);
fmt::print(out, fmtstring, "Version", pkg->version);
fmt::print(out, fmtstring, "Build", pkg->build_string);
fmt::print(out, fmtstring, "Build Number", pkg->build_number);
fmt::print(out, " {:<15} {} Kb\n", "Size", pkg->size / 1000);
fmt::print(out, fmtstring, "License", pkg->license);
fmt::print(out, fmtstring, "Subdir", pkg->subdir);
std::string url_remaining, url_scheme, url_auth, url_token;
split_scheme_auth_token(pkg->url, url_remaining, url_scheme, url_auth, url_token);
fmt::print(out, " {:<15} {}://{}\n", "URL", url_scheme, url_remaining);
fmt::print(out, fmtstring, "MD5", pkg->md5.empty() ? "Not available" : pkg->md5);
fmt::print(out, fmtstring, "SHA256", pkg->sha256.empty() ? "Not available" : pkg->sha256);
if (pkg->track_features.size())
auto print_solvable(const PackageInfo& pkg)
{
fmt::print(out, fmtstring, "Track Features", pkg->track_features);
}
auto out = Console::stream();
std::string header = fmt::format("{} {} {}", pkg.name, pkg.version, pkg.build_string);
fmt::print(out, "{:^40}\n{:-^{}}\n\n", header, "", header.size() > 40 ? header.size() : 40);
// std::cout << fmt::format<char>(
// " {:<15} {:%Y-%m-%d %H:%M:%S} UTC\n", "Timestamp", fmt::gmtime(pkg->timestamp));
static constexpr const char* fmtstring = " {:<15} {}\n";
fmt::print(out, fmtstring, "File Name", pkg.fn);
fmt::print(out, fmtstring, "Name", pkg.name);
fmt::print(out, fmtstring, "Version", pkg.version);
fmt::print(out, fmtstring, "Build", pkg.build_string);
fmt::print(out, fmtstring, "Build Number", pkg.build_number);
fmt::print(out, " {:<15} {} Kb\n", "Size", pkg.size / 1000);
fmt::print(out, fmtstring, "License", pkg.license);
fmt::print(out, fmtstring, "Subdir", pkg.subdir);
if (!pkg->depends.empty())
{
fmt::print(out, "\n Dependencies:\n");
for (auto& d : pkg->depends)
std::string url_remaining, url_scheme, url_auth, url_token;
split_scheme_auth_token(pkg.url, url_remaining, url_scheme, url_auth, url_token);
fmt::print(out, " {:<15} {}://{}\n", "URL", url_scheme, url_remaining);
fmt::print(out, fmtstring, "MD5", pkg.md5.empty() ? "Not available" : pkg.md5);
fmt::print(out, fmtstring, "SHA256", pkg.sha256.empty() ? "Not available" : pkg.sha256);
if (pkg.track_features.size())
{
fmt::print(out, " - {}\n", d);
fmt::print(out, fmtstring, "Track Features", pkg.track_features);
}
}
if (!pkg->constrains.empty())
{
fmt::print(out, "\n Run Constraints:\n");
for (auto& c : pkg->constrains)
// std::cout << fmt::format<char>(
// " {:<15} {:%Y-%m-%d %H:%M:%S} UTC\n", "Timestamp", fmt::gmtime(pkg.timestamp));
if (!pkg.depends.empty())
{
fmt::print(out, " - {}\n", c);
fmt::print(out, "\n Dependencies:\n");
for (auto& d : pkg.depends)
{
fmt::print(out, " - {}\n", d);
}
}
}
out << '\n';
};
if (!pkg.constrains.empty())
{
fmt::print(out, "\n Run Constraints:\n");
for (auto& c : pkg.constrains)
{
fmt::print(out, " - {}\n", c);
}
}
out << '\n';
}
}
query_result Query::find(const std::string& query) const
{
@ -326,59 +329,10 @@ namespace mamba
: m_type(type)
, m_query(query)
, m_dep_graph(std::move(dep_graph))
, m_pkg_view_list(m_dep_graph.number_of_nodes())
, m_ordered_pkg_list()
{
reset_pkg_view_list();
}
query_result::query_result(const query_result& rhs)
: m_type(rhs.m_type)
, m_query(rhs.m_query)
, m_dep_graph(rhs.m_dep_graph)
, m_pkg_view_list()
, m_ordered_pkg_list()
{
using std::swap;
auto offset_lbd = [&rhs, this](auto iter)
{ return m_dep_graph.nodes().begin() + (iter - rhs.m_dep_graph.nodes().begin()); };
{
package_view_list tmp(rhs.m_pkg_view_list.size());
std::transform(rhs.m_pkg_view_list.begin(), rhs.m_pkg_view_list.end(), tmp.begin(), offset_lbd);
swap(tmp, m_pkg_view_list);
}
if (!rhs.m_ordered_pkg_list.empty())
{
auto tmp(rhs.m_ordered_pkg_list);
std::for_each(
tmp.begin(),
tmp.end(),
[offset_lbd](auto& entry) {
std::transform(
entry.second.begin(),
entry.second.end(),
entry.second.begin(),
offset_lbd
);
}
);
swap(m_ordered_pkg_list, tmp);
}
}
query_result& query_result::operator=(const query_result& rhs)
{
if (this != &rhs)
{
using std::swap;
query_result tmp(rhs);
swap(*this, tmp);
}
return *this;
}
QueryType query_result::query_type() const
{
return m_type;
@ -391,26 +345,19 @@ namespace mamba
query_result& query_result::sort(std::string field)
{
auto fun = PackageInfo::less(field);
auto compare_ids = [&, fun = PackageInfo::less(field)](node_id lhs, node_id rhs)
{ return fun(m_dep_graph.node(lhs), m_dep_graph.node(rhs)); };
if (!m_ordered_pkg_list.empty())
if (!m_ordered_pkg_id_list.empty())
{
for (auto& entry : m_ordered_pkg_list)
for (auto& [_, pkg_id_list] : m_ordered_pkg_id_list)
{
std::sort(
entry.second.begin(),
entry.second.end(),
[fun](const auto& lhs, const auto& rhs) { return fun(*lhs, *rhs); }
);
std::sort(pkg_id_list.begin(), pkg_id_list.end(), compare_ids);
}
}
else
{
std::sort(
m_pkg_view_list.begin(),
m_pkg_view_list.end(),
[fun](const auto& lhs, const auto& rhs) { return fun(*lhs, *rhs); }
);
std::sort(m_pkg_id_list.begin(), m_pkg_id_list.end(), compare_ids);
}
return *this;
@ -419,25 +366,25 @@ namespace mamba
query_result& query_result::groupby(std::string field)
{
auto fun = PackageInfo::get_field_getter(field);
if (m_ordered_pkg_list.empty())
if (m_ordered_pkg_id_list.empty())
{
for (auto& pkg : m_pkg_view_list)
for (auto& id : m_pkg_id_list)
{
m_ordered_pkg_list[fun(*pkg)].push_back(pkg);
m_ordered_pkg_id_list[fun(m_dep_graph.node(id))].push_back(id);
}
}
else
{
ordered_package_list tmp;
for (auto& entry : m_ordered_pkg_list)
for (auto& entry : m_ordered_pkg_id_list)
{
for (auto& pkg : entry.second)
for (auto& id : entry.second)
{
std::string key = entry.first + '/' + fun(*pkg);
tmp[key].push_back(pkg);
std::string key = entry.first + '/' + fun(m_dep_graph.node(id));
tmp[std::move(key)].push_back(id);
}
}
m_ordered_pkg_list = std::move(tmp);
m_ordered_pkg_id_list = std::move(tmp);
}
return *this;
}
@ -445,7 +392,7 @@ namespace mamba
query_result& query_result::reset()
{
reset_pkg_view_list();
m_ordered_pkg_list.clear();
m_ordered_pkg_id_list.clear();
return *this;
}
@ -456,7 +403,7 @@ namespace mamba
std::ostream& query_result::table(std::ostream& out, const std::vector<std::string>& fmt) const
{
if (m_pkg_view_list.empty())
if (m_pkg_id_list.empty())
{
out << "No entries matching \"" << m_query << "\" found" << std::endl;
}
@ -480,7 +427,7 @@ namespace mamba
}
}
auto format_row = [&](auto& pkg)
auto format_row = [&](const PackageInfo& pkg)
{
std::vector<mamba::printers::FormattedString> row;
for (std::size_t i = 0; i < cmds.size(); ++i)
@ -488,24 +435,24 @@ namespace mamba
const auto& cmd = cmds[i];
if (cmd == "Name")
{
row.push_back(pkg->name);
row.push_back(pkg.name);
}
else if (cmd == "Version")
{
row.push_back(pkg->version);
row.push_back(pkg.version);
}
else if (cmd == "Build")
{
row.push_back(pkg->build_string);
row.push_back(pkg.build_string);
}
else if (cmd == "Channel")
{
row.push_back(cut_repo_name(pkg->channel));
row.push_back(cut_repo_name(pkg.channel));
}
else if (cmd == "Depends")
{
std::string depends_qualifier;
for (const auto& dep : pkg->depends)
for (const auto& dep : pkg.depends)
{
if (starts_with(dep, args[i]))
{
@ -521,21 +468,21 @@ namespace mamba
printers::Table printer(headers);
if (!m_ordered_pkg_list.empty())
if (!m_ordered_pkg_id_list.empty())
{
for (auto& entry : m_ordered_pkg_list)
for (auto& entry : m_ordered_pkg_id_list)
{
for (auto& pkg : entry.second)
for (const auto& id : entry.second)
{
printer.add_row(format_row(pkg));
printer.add_row(format_row(m_dep_graph.node(id)));
}
}
}
else
{
for (const auto& pkg : m_pkg_view_list)
for (const auto& id : m_pkg_id_list)
{
printer.add_row(format_row(pkg));
printer.add_row(format_row(m_dep_graph.node(id)));
}
}
return printer.print(out);
@ -557,7 +504,7 @@ namespace mamba
void start_node(node_id node, const graph_type& g)
{
print_prefix(node);
m_out << get_package_repr(g.nodes()[node]) << '\n';
m_out << get_package_repr(g.node(node)) << '\n';
if (node == 0u)
{
m_prefix_stack.push_back(" ");
@ -595,7 +542,7 @@ namespace mamba
void forward_or_cross_edge(node_id, node_id to, const graph_type& g)
{
print_prefix(to);
m_out << g.nodes()[to].name
m_out << g.node(to).name
<< fmt::format(Context::instance().palette.shown, " already visited\n");
}
@ -645,14 +592,14 @@ namespace mamba
graph_printer printer(out);
m_dep_graph.depth_first_search(printer);
}
else if (!m_pkg_view_list.empty())
else if (!m_pkg_id_list.empty())
{
out << m_query << '\n';
for (size_t i = 0; i < m_pkg_view_list.size() - 1; ++i)
for (size_t i = 0; i < m_pkg_id_list.size() - 1; ++i)
{
out << " ├─ " << get_package_repr(*m_pkg_view_list[i]) << '\n';
out << " ├─ " << get_package_repr(m_dep_graph.node(m_pkg_id_list[i])) << '\n';
}
out << " └─ " << get_package_repr(*m_pkg_view_list.back()) << '\n';
out << " └─ " << get_package_repr(m_dep_graph.node(m_pkg_id_list.back())) << '\n';
}
return out;
@ -666,22 +613,22 @@ namespace mamba
: (m_type == QueryType::kDEPENDS ? "depends" : "whoneeds");
j["query"] = { { "query", MatchSpec(m_query).conda_build_form() }, { "type", query_type } };
std::string msg = m_pkg_view_list.empty() ? "No entries matching \"" + m_query + "\" found"
: "";
std::string msg = m_pkg_id_list.empty() ? "No entries matching \"" + m_query + "\" found"
: "";
j["result"] = { { "msg", msg }, { "status", "OK" } };
j["result"]["pkgs"] = nlohmann::json::array();
for (size_t i = 0; i < m_pkg_view_list.size(); ++i)
for (size_t i = 0; i < m_pkg_id_list.size(); ++i)
{
j["result"]["pkgs"].push_back(m_pkg_view_list[i]->json_record());
j["result"]["pkgs"].push_back(m_dep_graph.node(m_pkg_id_list[i]).json_record());
}
if (m_type != QueryType::kSEARCH && !m_pkg_view_list.empty())
if (m_type != QueryType::kSEARCH && !m_pkg_id_list.empty())
{
bool has_root = !m_dep_graph.successors(0).empty();
j["result"]["graph_roots"] = nlohmann::json::array();
j["result"]["graph_roots"].push_back(
has_root ? m_dep_graph.nodes()[0].json_record() : nlohmann::json(m_query)
has_root ? m_dep_graph.node(0).json_record() : nlohmann::json(m_query)
);
}
return j;
@ -689,11 +636,11 @@ namespace mamba
std::ostream& query_result::pretty(std::ostream& out) const
{
if (!m_pkg_view_list.empty())
if (!m_pkg_id_list.empty())
{
for (const auto& pkg : m_pkg_view_list)
for (const auto& id : m_pkg_id_list)
{
print_solvable(pkg);
print_solvable(m_dep_graph.node(id));
}
}
return out;
@ -706,12 +653,13 @@ namespace mamba
void query_result::reset_pkg_view_list()
{
auto it = m_dep_graph.nodes().begin();
std::generate(m_pkg_view_list.begin(), m_pkg_view_list.end(), [&it]() { return it++; });
m_pkg_id_list.clear();
m_pkg_id_list.reserve(m_dep_graph.number_of_nodes());
m_dep_graph.for_each_node_id([&](node_id id) { m_pkg_id_list.push_back(id); });
}
std::string query_result::get_package_repr(const PackageInfo& pkg) const
{
return pkg.version.empty() ? pkg.name : pkg.name + '[' + pkg.version + ']';
return pkg.version.empty() ? pkg.name : fmt::format("{}[{}]", pkg.name, pkg.version);
}
} // namespace mamba

View File

@ -57,18 +57,9 @@ namespace mamba
using edge_t = ProblemsGraph::edge_t;
using conflicts_t = ProblemsGraph::conflicts_t;
ProblemsGraphCreator(const MSolver& solver, const MPool& pool)
: m_solver{ solver }
, m_pool{ pool }
{
m_root_node = m_graph.add_node(RootNode());
parse_problems();
}
ProblemsGraphCreator(const MSolver& solver, const MPool& pool);
operator ProblemsGraph() &&
{
return { std::move(m_graph), std::move(m_conflicts), m_root_node };
}
ProblemsGraph problem_graph() &&;
private:
@ -94,6 +85,19 @@ namespace mamba
void parse_problems();
};
ProblemsGraphCreator::ProblemsGraphCreator(const MSolver& solver, const MPool& pool)
: m_solver{ solver }
, m_pool{ pool }
{
m_root_node = m_graph.add_node(RootNode());
parse_problems();
}
ProblemsGraph ProblemsGraphCreator::problem_graph() &&
{
return { std::move(m_graph), std::move(m_conflicts), m_root_node };
}
auto ProblemsGraphCreator::add_solvable(SolvId solv_id, node_t&& node, bool update) -> node_id
{
if (const auto iter = m_solv2node.find(solv_id); iter != m_solv2node.end())
@ -123,11 +127,7 @@ namespace mamba
{
added = true;
PackageInfo pkg_info(pool_id2solvable(m_pool, solv_id));
node_id to_id = add_solvable(
solv_id,
PackageNode{ std::move(pkg_info), std::nullopt },
false
);
node_id to_id = add_solvable(solv_id, PackageNode{ std::move(pkg_info) }, false);
m_graph.add_edge(from_id, to_id, edge);
}
return added;
@ -157,11 +157,11 @@ namespace mamba
}
auto src_id = add_solvable(
problem.source_id,
PackageNode{ std::move(source).value(), std::nullopt }
PackageNode{ std::move(source).value() }
);
node_id tgt_id = add_solvable(
problem.target_id,
PackageNode{ std::move(target).value(), { type } }
PackageNode{ std::move(target).value() }
);
node_id cons_id = add_solvable(problem.dep_id, ConstraintNode{ dep.value() });
MatchSpec edge(dep.value());
@ -182,7 +182,7 @@ namespace mamba
}
auto src_id = add_solvable(
problem.source_id,
PackageNode{ std::move(source).value(), std::nullopt }
PackageNode{ std::move(source).value() }
);
MatchSpec edge(dep.value());
bool added = add_expanded_deps_edges(src_id, problem.dep_id, edge);
@ -225,7 +225,7 @@ namespace mamba
MatchSpec edge(dep.value());
node_id dep_id = add_solvable(
problem.dep_id,
UnresolvedDependencyNode{ std::move(dep).value(), type }
UnresolvedDependencyNode{ std::move(dep).value() }
);
m_graph.add_edge(m_root_node, dep_id, std::move(edge));
break;
@ -244,11 +244,11 @@ namespace mamba
MatchSpec edge(dep.value());
node_id src_id = add_solvable(
problem.source_id,
PackageNode{ std::move(source).value(), std::nullopt }
PackageNode{ std::move(source).value() }
);
node_id dep_id = add_solvable(
problem.dep_id,
UnresolvedDependencyNode{ std::move(dep).value(), type }
UnresolvedDependencyNode{ std::move(dep).value() }
);
m_graph.add_edge(src_id, dep_id, std::move(edge));
break;
@ -267,11 +267,11 @@ namespace mamba
}
node_id src_id = add_solvable(
problem.source_id,
PackageNode{ std::move(source).value(), { type } }
PackageNode{ std::move(source).value() }
);
node_id tgt_id = add_solvable(
problem.target_id,
PackageNode{ std::move(target).value(), { type } }
PackageNode{ std::move(target).value() }
);
add_conflict(src_id, tgt_id);
break;
@ -289,13 +289,13 @@ namespace mamba
break;
}
}
};
}
}
}
auto ProblemsGraph::from_solver(const MSolver& solver, const MPool& pool) -> ProblemsGraph
{
return ProblemsGraphCreator(solver, pool);
return ProblemsGraphCreator(solver, pool).problem_graph();
}
ProblemsGraph::ProblemsGraph(graph_t graph, conflicts_t conflicts, node_id root_node)
@ -320,6 +320,74 @@ namespace mamba
return m_root_node;
}
/******************************************
* Implementation of simplify_conflicts *
******************************************/
ProblemsGraph simplify_conflicts(const ProblemsGraph& pbs)
{
using node_id = ProblemsGraph::node_id;
using node_t = ProblemsGraph::node_t;
const auto& old_graph = pbs.graph();
const auto& old_conflicts = pbs.conflicts();
const auto is_constraint = [](const node_t& node) -> bool
{ return std::holds_alternative<ProblemsGraph::ConstraintNode>(node); };
const auto has_constraint_child = [&](node_id n)
{
if (old_graph.out_degree(n) == 1)
{
node_id const s = old_graph.successors(n).front();
// If s is of constraint type, it has conflicts
assert(!is_constraint(old_graph.node(s)) || old_conflicts.has_conflict(s));
return is_constraint(old_graph.node(s));
}
return false;
};
// Working on a copy since we cannot safely iterate through object
// and modify them at the same time
auto graph = pbs.graph();
auto conflicts = pbs.conflicts();
for (const auto& [id, id_conflicts] : old_conflicts)
{
// We are trying to detect node that are in conflicts but are not leaves.
// This shows up in Pyhon dependencies because the constraint on ``python`` and
// ``python_abi`` was inversed.
if (has_constraint_child(id))
{
const node_id id_child = old_graph.successors(id).front();
for (const auto& c : id_conflicts)
{
// Since ``id`` has a constraint node child (``id-child``) and is itself in
// conflict with another constraint node ``c``, we are moving the conflict
// between the ``id_child`` and the parents ``c_parent`` of the ``c``.
// This is a bit hacky but the intuition is to replicate the structure of
// nodes conflicting with ``id_child`` with ``c_parent``.
// They likely reprensent the same thing and so we want them to be able to
// merge later on.
if (is_constraint(old_graph.node(c)))
{
for (const node_id c_parent : old_graph.predecessors(c))
{
conflicts.add(id_child, c_parent);
graph.remove_edge(c_parent, c);
}
conflicts.remove(id, c);
if (!conflicts.has_conflict(c))
{
graph.remove_node(c);
}
}
}
}
}
return { std::move(graph), std::move(conflicts), pbs.root_node() };
}
/***********************************************
* Implementation of CompressedProblemsGraph *
***********************************************/
@ -388,21 +456,20 @@ namespace mamba
using node_type_list = std::vector<T>;
/**
* Group indices of variants with same alternative together.
* Group ids of nodes with same alternative together.
*
* If a variant at index ``i`` in the input @p vrnts holds alternative with index ``k``
* then the output will contain ``i`` in position ``k``.
* If a node variant at with id ``id`` in the input graph @p g holds alternative
* with variant index ``k`` then the output will contain ``id`` in position ``k``.
*/
template <typename... T>
auto variant_by_index(const std::vector<std::variant<T...>>& vrnts)
-> node_type_list<std::vector<std::size_t>>
auto node_id_by_type(const ProblemsGraph::graph_t& g) -> node_type_list<old_node_id_list>
{
auto out = node_type_list<std::vector<std::size_t>>(sizeof...(T));
const auto n = vrnts.size();
for (std::size_t i = 0; i < n; ++i)
{
out[vrnts[i].index()].push_back(i);
}
using node_id = ProblemsGraph::node_id;
using node_t = ProblemsGraph::node_t;
static constexpr auto n_types = std::variant_size_v<node_t>;
auto out = node_type_list<old_node_id_list>(n_types);
g.for_each_node_id([&](node_id id) { out[g.node(id).index()].push_back(id); });
return out;
}
@ -417,7 +484,7 @@ namespace mamba
*/
template <typename CompFunc>
auto
merge_node_indices(const ProblemsGraph::graph_t::node_list& nodes, CompFunc&& merge_criteria)
merge_node_indices(const node_type_list<old_node_id_list>& nodes_by_type, CompFunc&& merge_criteria)
-> node_type_list<std::vector<old_node_id_list>>
{
auto merge_func = [&merge_criteria](const auto& node_indices_of_one_node_type)
@ -427,7 +494,6 @@ namespace mamba
std::forward<CompFunc>(merge_criteria)
);
};
const auto nodes_by_type = variant_by_index(nodes);
node_type_list<std::vector<old_node_id_list>> groups(nodes_by_type.size());
std::transform(nodes_by_type.begin(), nodes_by_type.end(), groups.begin(), merge_func);
return groups;
@ -519,21 +585,21 @@ namespace mamba
auto leaves_from = [&g](node_id n) -> vector_set<node_id>
{
auto leaves = std::vector<node_id>();
g.for_each_leaf_from(n, [&leaves](node_id m) { leaves.push_back(m); });
g.for_each_leaf_id_from(n, [&leaves](node_id m) { leaves.push_back(m); });
return vector_set(std::move(leaves));
};
return (node_name(g.node(n1)) == node_name(g.node(n2)))
// Merging conflicts would be counter-productive in explaining problems
&& !(pbs.conflicts().in_conflict(n1, n2))
// We don't want to use leaves_from for leaves because it resove to themselves,
// We don't want to use leaves_from for leaves because it resolve to themselves,
// preventing any merging.
&& ((is_leaf(n1) && is_leaf(n2)) || (leaves_from(n1) == leaves_from(n2)))
// We only check the parents for non-leaves meaning parents can "inject"
// We only check the parents for leaves meaning parents can "inject"
// themselves into a bigger problem
&& ((!is_leaf(n1) && !is_leaf(n2)) || (g.predecessors(n1) == g.predecessors(n2)));
}
using node_id_mapping = std::vector<CompressedProblemsGraph::node_id>;
using node_id_mapping = std::map<ProblemsGraph::node_id, CompressedProblemsGraph::node_id>;
/**
* For a given type of node, merge nodes together and add them to the new graph.
@ -552,9 +618,6 @@ namespace mamba
node_id_mapping& old_to_new
)
{
// Check nothrow move for efficient push_back
static_assert(std::is_nothrow_move_constructible_v<Node>);
auto get_old_node = [&old_graph](ProblemsGraph::node_id id)
{
auto node = old_graph.node(id);
@ -591,11 +654,10 @@ namespace mamba
auto new_graph = CompressedProblemsGraph::graph_t();
const auto new_root_node = new_graph.add_node(CompressedProblemsGraph::RootNode());
auto old_to_new = std::vector<CompressedProblemsGraph::node_id>(
old_graph.number_of_nodes()
);
auto old_to_new = node_id_mapping{};
auto old_ids_groups = merge_node_indices(
old_graph.nodes(),
node_id_by_type(pbs.graph()),
std::forward<CompFunc>(merge_criteria)
);
@ -656,15 +718,15 @@ namespace mamba
{
auto add_new_edge = [&](ProblemsGraph::node_id old_from, ProblemsGraph::node_id old_to)
{
auto const new_from = old_to_new[old_from];
auto const new_to = old_to_new[old_to];
auto const new_from = old_to_new.at(old_from);
auto const new_to = old_to_new.at(old_to);
if (!new_graph.has_edge(new_from, new_to))
{
new_graph.add_edge(new_from, new_to, CompressedProblemsGraph::edge_t());
}
new_graph.edge(new_from, new_to).insert(old_graph.edge(old_from, old_to));
};
old_graph.for_each_edge(add_new_edge);
old_graph.for_each_edge_id(add_new_edge);
}
/**
@ -680,10 +742,10 @@ namespace mamba
auto new_conflicts = CompressedProblemsGraph::conflicts_t();
for (const auto& [old_from, old_with] : old_conflicts)
{
const auto new_from = old_to_new[old_from];
const auto new_from = old_to_new.at(old_from);
for (const auto old_to : old_with)
{
new_conflicts.add(new_from, old_to_new[old_to]);
new_conflicts.add(new_from, old_to_new.at(old_to));
}
}
return new_conflicts;
@ -696,9 +758,9 @@ namespace mamba
const merge_criteria_t& merge_criteria
) -> CompressedProblemsGraph
{
graph_t graph;
node_id root_node;
node_id_mapping old_to_new;
graph_t graph = {};
node_id root_node = {};
node_id_mapping old_to_new = {};
if (merge_criteria)
{
auto merge_func =
@ -957,18 +1019,6 @@ namespace mamba
* Implementation of summary_msg *
***********************************/
std::ostream& print_problem_summary_msg(std::ostream& out, const CompressedProblemsGraph&)
{
return out << "Could not solve for environment specs\n";
}
std::string problem_summary_msg(const CompressedProblemsGraph& pbs)
{
std::stringstream ss;
print_problem_summary_msg(ss, pbs);
return ss.str();
}
/****************************************
* Implementation of problem_tree_msg *
****************************************/
@ -1077,7 +1127,7 @@ namespace mamba
using TreeNodeIter = typename TreeNodeList::iterator;
vector_set<node_id> leaf_installables = {};
std::vector<std::optional<Status>> m_node_visited;
std::map<node_id, std::optional<Status>> m_node_visited = {};
const CompressedProblemsGraph& m_pbs;
/**
@ -1132,9 +1182,9 @@ namespace mamba
*******************************/
TreeDFS::TreeDFS(const CompressedProblemsGraph& pbs)
: m_node_visited(pbs.graph().number_of_nodes(), std::nullopt)
, m_pbs(pbs)
: m_pbs(pbs)
{
pbs.graph().for_each_node_id([&](auto id) { m_node_visited.emplace(id, std::nullopt); });
}
auto TreeDFS::explore() -> std::vector<TreeNode>
@ -1177,7 +1227,7 @@ namespace mamba
{
const bool has_predecessors = m_pbs.graph().predecessors(id).size() > 0;
const bool has_successors = m_pbs.graph().successors(id).size() > 0;
const bool is_visited = m_node_visited[id].has_value();
const bool is_visited = m_node_visited.at(id).has_value();
// We purposefully check if the node is a leaf before checking if it
// is visited because showing a single node again is more intelligible than
// refering to another one.
@ -1335,11 +1385,6 @@ namespace mamba
{
return { out, status.value() };
}
if (node_uninstallable(id))
{
m_node_visited[id] = false;
return { out, false };
}
Status status = true;
// TODO(C++20) an enumerate view ``views::zip(views::iota(), children_ids)``
@ -1348,7 +1393,8 @@ namespace mamba
{
const auto children_pos = i == successors.size() - 1 ? SiblingNumber::last
: SiblingNumber::not_last;
Status child_status;
Status child_status = true;
assert(children.size() > 0);
if (children.size() > 1)
{
std::tie(out, child_status) = visit_split(children, children_pos, ongoing, out);
@ -1362,6 +1408,15 @@ namespace mamba
++i;
}
// Node installability status is checked *after* visiting the children because there
// are cases where both non-leaf nodes and their children have conflicts so we need
// to visit children to exaplin conflicts.
// In most cases though, only leaves would have conflicts and the loop above would be
// empty in such case.
// Warning node_uninstallable has side effects and must be called unconditionally
// (first here).
status = !node_uninstallable(id) && status;
m_node_visited[id] = status;
return { out, status };
}

View File

@ -669,7 +669,8 @@ namespace mamba
const auto& ctx = Context::instance();
out << "Could not solve for environment specs\n";
const auto pbs = ProblemsGraph::from_solver(*this, pool());
const auto cp_pbs = CompressedProblemsGraph::from_problems_graph(pbs);
const auto pbs_simplified = simplify_conflicts(pbs);
const auto cp_pbs = CompressedProblemsGraph::from_problems_graph(pbs_simplified);
print_problem_tree_msg(
out,
cp_pbs,

View File

@ -12,6 +12,7 @@
#include <fmt/format.h>
#include <gtest/gtest.h>
#include <nlohmann/json.hpp>
#include <solv/solver.h>
#include "mamba/core/channel.hpp"
#include "mamba/core/mamba_fs.hpp"
@ -34,13 +35,40 @@ namespace mamba
EXPECT_EQ(c.size(), 0);
EXPECT_FALSE(c.has_conflict(0));
EXPECT_FALSE(c.in_conflict(0, 1));
c.add(0, 1);
c.add(1, 2);
EXPECT_TRUE(c.add(0, 1));
EXPECT_TRUE(c.add(1, 2));
EXPECT_FALSE(c.add(1, 2));
EXPECT_TRUE(c.has_conflict(0));
EXPECT_TRUE(c.in_conflict(0, 1));
EXPECT_TRUE(c.in_conflict(1, 2));
EXPECT_TRUE(c.has_conflict(2));
EXPECT_FALSE(c.in_conflict(0, 2));
// With same
EXPECT_TRUE(c.add(5, 5));
EXPECT_TRUE(c.has_conflict(5));
EXPECT_TRUE(c.in_conflict(5, 5));
}
TEST(conflict_map, remove)
{
auto c = conflict_map<std::size_t>({ { 1, 1 }, { 1, 2 }, { 1, 3 }, { 2, 4 } });
ASSERT_EQ(c.size(), 4);
ASSERT_TRUE(c.in_conflict(2, 4));
ASSERT_TRUE(c.in_conflict(4, 2));
EXPECT_TRUE(c.remove(2, 4));
EXPECT_FALSE(c.in_conflict(4, 2));
EXPECT_FALSE(c.in_conflict(2, 4));
EXPECT_TRUE(c.has_conflict(2));
EXPECT_FALSE(c.has_conflict(4));
EXPECT_FALSE(c.remove(2, 4));
EXPECT_TRUE(c.remove(1));
EXPECT_FALSE(c.has_conflict(1));
EXPECT_FALSE(c.in_conflict(1, 1));
EXPECT_FALSE(c.in_conflict(1, 2));
EXPECT_FALSE(c.in_conflict(3, 1));
}
/**
@ -399,6 +427,12 @@ namespace mamba
return *solver;
}
auto create_numba() -> MSolver&
{
static auto solver = create_conda_forge({ "python=3.11", "numba<0.56" });
return *solver;
}
class Problem : public testing::TestWithParam<decltype(&create_basic_conflict)>
{
};
@ -420,27 +454,6 @@ namespace mamba
);
};
auto has_problem_type(const ProblemsGraph::node_t& node) -> bool
{
return std::visit(
[](const auto& n) -> bool
{
using Node = std::remove_const_t<std::remove_reference_t<decltype(n)>>;
if constexpr (std::is_same_v<Node, ProblemsGraph::RootNode>)
{
return false;
}
if constexpr (std::is_same_v<Node, ProblemsGraph::PackageNode>)
{
return n.problem_type.has_value();
}
return true;
},
node
);
};
TEST_P(Problem, constructor)
{
auto& solver = std::invoke(GetParam());
@ -450,33 +463,34 @@ namespace mamba
const auto& g = pbs.graph();
EXPECT_GE(g.number_of_nodes(), 1);
for (std::size_t id = 0; id < g.number_of_nodes(); ++id)
{
const auto& node = g.node(id);
if (is_virtual_package(node))
g.for_each_node_id(
[&](auto id)
{
const auto& node = g.node(id);
// Currently we do not make assumption about virtual package since
// we are not sure we are including them the same way than they would be in practice
break;
// we are not sure we are including them the same way than they would be in
// practice
if (!is_virtual_package(node))
{
if (g.in_degree(id) == 0)
{
// Only one root node
EXPECT_EQ(id, pbs.root_node());
EXPECT_TRUE(std::holds_alternative<ProblemsGraph::RootNode>(node));
}
else if (g.out_degree(id) == 0)
{
EXPECT_FALSE(std::holds_alternative<ProblemsGraph::RootNode>(node));
}
else
{
EXPECT_TRUE(std::holds_alternative<ProblemsGraph::PackageNode>(node));
}
// All nodes reachable from the root
EXPECT_TRUE(is_reachable(pbs.graph(), pbs.root_node(), id));
}
}
else if (g.in_degree(id) == 0)
{
// Only one root node
EXPECT_EQ(id, pbs.root_node());
EXPECT_TRUE(std::holds_alternative<ProblemsGraph::RootNode>(node));
}
else if (g.out_degree(id) == 0)
{
EXPECT_FALSE(std::holds_alternative<ProblemsGraph::RootNode>(node));
EXPECT_TRUE(has_problem_type(node));
}
else
{
EXPECT_TRUE(std::holds_alternative<ProblemsGraph::PackageNode>(node));
}
// All nodes reachable from the root
EXPECT_TRUE(is_reachable(pbs.graph(), pbs.root_node(), id));
}
);
const auto& conflicts = pbs.conflicts();
for (const auto& [n, _] : conflicts)
@ -488,6 +502,34 @@ namespace mamba
}
}
TEST_P(Problem, simplify_conflicts)
{
auto& solver = std::invoke(GetParam());
const auto solved = solver.try_solve();
ASSERT_FALSE(solved);
const auto& pbs = ProblemsGraph::from_solver(solver, solver.pool());
const auto& pbs_simplified = simplify_conflicts(pbs);
const auto& graph_simplified = pbs_simplified.graph();
EXPECT_GE(graph_simplified.number_of_nodes(), 1);
EXPECT_LE(graph_simplified.number_of_nodes(), pbs.graph().number_of_nodes());
for (const auto& [id, _] : pbs_simplified.conflicts())
{
const auto& node = graph_simplified.node(id);
// Currently we do not make assumption about virtual package since
// we are not sure we are including them the same way than they would be in
// practice
if (!is_virtual_package(node))
{
EXPECT_TRUE(graph_simplified.has_node(id));
// Unfortunately not all conflicts are on leaves
// EXPECT_EQ(graph_simplified.out_degree(id), 0);
EXPECT_TRUE(is_reachable(graph_simplified, pbs_simplified.root_node(), id));
}
}
}
TEST(satifiability_error, NamedList)
{
auto l = CompressedProblemsGraph::PackageListNode();
@ -528,37 +570,38 @@ namespace mamba
const auto solved = solver.try_solve();
ASSERT_FALSE(solved);
const auto pbs = ProblemsGraph::from_solver(solver, solver.pool());
const auto cp_pbs = CpPbGr::from_problems_graph(pbs);
const auto cp_pbs = CpPbGr::from_problems_graph(simplify_conflicts(pbs));
const auto& cp_g = cp_pbs.graph();
EXPECT_GE(pbs.graph().number_of_nodes(), cp_g.number_of_nodes());
EXPECT_GE(cp_g.number_of_nodes(), 1);
for (std::size_t id = 0; id < cp_g.number_of_nodes(); ++id)
{
const auto& node = cp_g.node(id);
if (is_virtual_package(node))
cp_g.for_each_node_id(
[&](auto id)
{
const auto& node = cp_g.node(id);
// Currently we do not make assumption about virtual package since
// we are not sure we are including them the same way than they would be in
break;
if (!is_virtual_package(node))
{
if (cp_g.in_degree(id) == 0)
{
// Only one root node
EXPECT_EQ(id, pbs.root_node());
EXPECT_TRUE(std::holds_alternative<CpPbGr::RootNode>(node));
}
else if (cp_g.out_degree(id) == 0)
{
EXPECT_FALSE(std::holds_alternative<CpPbGr::RootNode>(node));
}
else
{
EXPECT_TRUE(std::holds_alternative<CpPbGr::PackageListNode>(node));
}
// All nodes reachable from the root
EXPECT_TRUE(is_reachable(cp_g, cp_pbs.root_node(), id));
}
}
else if (cp_g.in_degree(id) == 0)
{
// Only one root node
EXPECT_EQ(id, pbs.root_node());
EXPECT_TRUE(std::holds_alternative<CpPbGr::RootNode>(node));
}
else if (cp_g.out_degree(id) == 0)
{
EXPECT_FALSE(std::holds_alternative<CpPbGr::RootNode>(node));
}
else
{
EXPECT_TRUE(std::holds_alternative<CpPbGr::PackageListNode>(node));
}
// All nodes reachable from the root
EXPECT_TRUE(is_reachable(pbs.graph(), pbs.root_node(), id));
}
);
const auto& conflicts = cp_pbs.conflicts();
for (const auto& [n, _] : conflicts)
@ -572,26 +615,30 @@ namespace mamba
TEST_P(Problem, problem_tree_str)
{
using CpPbGr = CompressedProblemsGraph;
auto& solver = std::invoke(GetParam());
const auto solved = solver.try_solve();
ASSERT_FALSE(solved);
const auto pbs = ProblemsGraph::from_solver(solver, solver.pool());
const auto cp_pbs = CompressedProblemsGraph::from_problems_graph(pbs);
const auto cp_pbs = CpPbGr::from_problems_graph(simplify_conflicts(pbs));
const auto message = problem_tree_msg(cp_pbs);
auto message_contains = [&](const auto& node)
auto message_contains = [&message](const auto& node)
{
using Node = std::remove_cv_t<std::remove_reference_t<decltype(node)>>;
if constexpr (!std::is_same_v<Node, CompressedProblemsGraph::RootNode>)
if constexpr (!std::is_same_v<Node, CpPbGr::RootNode>)
{
EXPECT_TRUE(contains(message, node.name()));
}
};
for (const auto& node : cp_pbs.graph().nodes())
{
std::visit(message_contains, node);
}
cp_pbs.graph().for_each_node_id(
[&message_contains, &g = cp_pbs.graph()](auto id)
{
std::visit(message_contains, g.node(id)); //
}
);
}
INSTANTIATE_TEST_SUITE_P(
@ -609,7 +656,8 @@ namespace mamba
create_r_base,
create_scip,
create_jupyterlab,
create_double_python
create_double_python,
create_numba
)
);
}

View File

@ -36,7 +36,7 @@ namespace mamba
EXPECT_NE(vector_set<int>({ 2 }), vector_set<int>({}));
}
TEST(vector_set, insertion)
TEST(vector_set, insert)
{
auto s = vector_set<int>();
s.insert(33);
@ -53,6 +53,19 @@ namespace mamba
EXPECT_EQ(s, vector_set<int>({ 0, 17, 22, 33 }));
}
TEST(vector_set, erase)
{
auto s = vector_set<int>{ 4, 3, 2, 1 };
EXPECT_EQ(s.erase(4), 1);
EXPECT_EQ(s, vector_set<int>({ 1, 2, 3 }));
EXPECT_EQ(s.erase(4), 0);
EXPECT_EQ(s, vector_set<int>({ 1, 2, 3 }));
const auto it = s.erase(s.begin());
EXPECT_EQ(it, s.begin());
EXPECT_EQ(s, vector_set<int>({ 2, 3 }));
}
TEST(vector_set, contains)
{
const auto s = vector_set<int>({ 1, 3, 4, 5 });
@ -170,11 +183,16 @@ namespace mamba
TEST(graph, build_simple)
{
const auto g = build_graph();
using node_list = decltype(g)::node_list;
using node_map = decltype(g)::node_map;
using node_id_list = decltype(g)::node_id_list;
EXPECT_EQ(g.number_of_nodes(), 7ul);
EXPECT_EQ(g.number_of_edges(), 7ul);
EXPECT_EQ(g.nodes(), node_list({ 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5 }));
EXPECT_EQ(
g.nodes(),
node_map(
{ { 0, 0.5 }, { 1, 1.5 }, { 2, 2.5 }, { 3, 3.5 }, { 4, 4.5 }, { 5, 5.5 }, { 6, 6.5 } }
)
);
EXPECT_EQ(g.successors(0u), node_id_list({ 1u, 2u }));
EXPECT_EQ(g.successors(1u), node_id_list({ 3u, 4u }));
EXPECT_EQ(g.successors(2u), node_id_list({ 3u, 5u }));
@ -188,11 +206,11 @@ namespace mamba
TEST(graph, build_edge_data)
{
const auto g = build_edge_data_graph();
using node_list = decltype(g)::node_list;
using node_map = decltype(g)::node_map;
using node_id_list = decltype(g)::node_id_list;
EXPECT_EQ(g.number_of_nodes(), 3ul);
EXPECT_EQ(g.number_of_edges(), 2ul);
EXPECT_EQ(g.nodes(), node_list({ 0.5, 1.5, 2.5 }));
EXPECT_EQ(g.nodes(), node_map({ { 0, 0.5 }, { 1, 1.5 }, { 2, 2.5 } }));
EXPECT_EQ(g.successors(0ul), node_id_list({ 1ul }));
EXPECT_EQ(g.successors(1ul), node_id_list({ 2ul }));
EXPECT_EQ(g.successors(2ul), node_id_list());
@ -233,6 +251,63 @@ namespace mamba
EXPECT_EQ(g.edge(0ul, 1ul), new_edge_val);
}
TEST(graph, remove_edge)
{
auto g = build_edge_data_graph();
const auto n_edges_init = g.number_of_edges();
ASSERT_FALSE(g.has_edge(1, 0));
ASSERT_TRUE(g.has_edge(0, 1));
EXPECT_FALSE(g.remove_edge(1, 0));
EXPECT_EQ(g.number_of_edges(), n_edges_init);
EXPECT_FALSE(g.has_edge(1, 0));
EXPECT_TRUE(g.has_edge(0, 1));
ASSERT_TRUE(g.has_edge(0, 1));
EXPECT_TRUE(g.remove_edge(0, 1));
EXPECT_EQ(g.number_of_edges(), n_edges_init - 1u);
EXPECT_FALSE(g.has_edge(0, 1));
EXPECT_EQ(g.edges().count({ 0, 1 }), 0);
}
TEST(graph, remove_node)
{
auto g = build_edge_data_graph();
ASSERT_TRUE(g.has_node(0));
ASSERT_TRUE(g.has_node(1));
ASSERT_TRUE(g.has_node(2));
ASSERT_TRUE(g.has_edge(0, 1));
ASSERT_TRUE(g.has_edge(1, 2));
const auto n_edges_init = g.number_of_edges();
const auto n_nodes_init = g.number_of_nodes();
const auto node_1_degree = g.in_degree(1) + g.out_degree(1);
EXPECT_TRUE(g.remove_node(1));
EXPECT_EQ(g.number_of_nodes(), n_nodes_init - 1u);
EXPECT_EQ(g.number_of_edges(), n_edges_init - node_1_degree);
EXPECT_EQ(g.number_of_edges(), g.edges().size());
EXPECT_TRUE(g.has_node(0));
EXPECT_FALSE(g.has_node(1));
EXPECT_TRUE(g.has_node(2));
EXPECT_EQ(g.in_degree(1), 0);
EXPECT_EQ(g.out_degree(1), 0);
EXPECT_FALSE(g.has_edge(0, 1));
EXPECT_FALSE(g.has_edge(1, 2));
g.for_each_node_id([&](auto id) { EXPECT_TRUE(g.has_node(id)); });
EXPECT_FALSE(g.remove_node(1));
EXPECT_EQ(g.number_of_nodes(), n_nodes_init - 1u);
EXPECT_EQ(g.number_of_edges(), n_edges_init - node_1_degree);
EXPECT_EQ(g.number_of_edges(), g.edges().size());
const auto new_id = g.add_node(.7);
EXPECT_EQ(new_id, n_nodes_init); // Ids are not invalidated so new id is used
EXPECT_FALSE(g.has_node(1)); // Old id is not being confused
EXPECT_EQ(g.number_of_nodes(), n_nodes_init);
}
TEST(graph, degree)
{
const auto g = build_graph();
@ -244,12 +319,27 @@ namespace mamba
EXPECT_EQ(g.in_degree(6), 1);
}
TEST(graph, for_each_node)
{
const auto g = build_graph();
using node_id = decltype(g)::node_id;
std::size_t n_nodes = 0;
g.for_each_node_id(
[&](node_id id)
{
EXPECT_TRUE(g.has_node(id));
++n_nodes;
}
);
EXPECT_EQ(n_nodes, g.number_of_nodes());
}
TEST(graph, for_each_edge)
{
const auto g = build_graph();
using node_id = decltype(g)::node_id;
std::size_t n_edges = 0;
g.for_each_edge(
g.for_each_edge_id(
[&g, &n_edges](node_id from, node_id to)
{
EXPECT_TRUE(g.has_edge(from, to));
@ -265,7 +355,7 @@ namespace mamba
using node_id = decltype(g)::node_id;
using node_id_list = decltype(g)::node_id_list;
auto leaves = node_id_list();
g.for_each_leaf([&leaves](node_id leaf) { leaves.insert(leaf); });
g.for_each_leaf_id([&leaves](node_id leaf) { leaves.insert(leaf); });
EXPECT_EQ(leaves, node_id_list({ 4ul, 5ul, 6ul }));
}
@ -275,7 +365,7 @@ namespace mamba
using node_id = decltype(g)::node_id;
using node_id_list = decltype(g)::node_id_list;
auto leaves = node_id_list();
g.for_each_leaf_from(2ul, [&leaves](node_id leaf) { leaves.insert(leaf); });
g.for_each_leaf_id_from(2ul, [&leaves](node_id leaf) { leaves.insert(leaf); });
EXPECT_EQ(leaves, node_id_list({ 5ul, 6ul }));
}
@ -285,7 +375,7 @@ namespace mamba
using node_id = decltype(g)::node_id;
using node_id_list = decltype(g)::node_id_list;
auto roots = node_id_list();
g.for_each_root([&roots](node_id root) { roots.insert(root); });
g.for_each_root_id([&roots](node_id root) { roots.insert(root); });
EXPECT_EQ(roots, node_id_list({ 0ul }));
}
@ -295,7 +385,7 @@ namespace mamba
using node_id = decltype(g)::node_id;
using node_id_list = decltype(g)::node_id_list;
auto leaves = node_id_list();
g.for_each_root_from(2ul, [&leaves](node_id leaf) { leaves.insert(leaf); });
g.for_each_root_id_from(2ul, [&leaves](node_id leaf) { leaves.insert(leaf); });
EXPECT_EQ(leaves, node_id_list({ 0ul }));
}

View File

@ -123,6 +123,7 @@ __all__ = [
"get_virtual_packages",
"ostream_redirect",
"sign",
"simplify_conflicts",
"transmute",
]
@ -219,7 +220,7 @@ class CompressedProblemsGraph:
def __init__(self) -> None: ...
def __iter__(self) -> typing.Iterator: ...
def __len__(self) -> int: ...
def add(self, arg0: int, arg1: int) -> None: ...
def add(self, arg0: int, arg1: int) -> bool: ...
def clear(self) -> None: ...
def conflicts(self, arg0: int) -> typing.Set[int]: ...
def has_conflict(self, arg0: int) -> bool: ...
@ -257,7 +258,7 @@ class CompressedProblemsGraph:
) -> typing.Tuple[str, int]: ...
pass
class DependencyListList:
class DependencyList:
def __bool__(self) -> bool: ...
def __init__(self) -> None: ...
def __iter__(self) -> typing.Iterator: ...
@ -365,18 +366,18 @@ class CompressedProblemsGraph:
def graph(
self,
) -> typing.Tuple[
typing.List[
typing.Dict[
int,
typing.Union[
ProblemsGraph.RootNode,
CompressedProblemsGraph.PackageListNode,
CompressedProblemsGraph.UnresolvedDependencyListNode,
CompressedProblemsGraph.ConstraintListNode,
]
],
],
typing.Dict[typing.Tuple[int, int], CompressedProblemsGraph.DependencyListList],
typing.Dict[typing.Tuple[int, int], CompressedProblemsGraph.DependencyList],
]: ...
def root_node(self) -> int: ...
def summary_message(self) -> str: ...
def tree_message(self) -> str: ...
pass
@ -1043,7 +1044,6 @@ class ProblemsGraph:
pass
class ConstraintNode(MatchSpec):
problem_type: libmambapy.bindings.SolverRuleinfo # value = <SolverRuleinfo.SOLVER_RULE_PKG_CONSTRAINS: 267>
pass
class PackageNode(PackageInfo):
@ -1053,14 +1053,6 @@ class ProblemsGraph:
pass
class UnresolvedDependencyNode(MatchSpec):
@property
def problem_type(self) -> SolverRuleinfo:
"""
:type: SolverRuleinfo
"""
@problem_type.setter
def problem_type(self, arg0: SolverRuleinfo) -> None:
pass
pass
def conflicts(self) -> ProblemsGraph.ConflictMap: ...
@staticmethod
@ -1068,13 +1060,14 @@ class ProblemsGraph:
def graph(
self,
) -> typing.Tuple[
typing.List[
typing.Dict[
int,
typing.Union[
ProblemsGraph.RootNode,
ProblemsGraph.PackageNode,
ProblemsGraph.UnresolvedDependencyNode,
ProblemsGraph.ConstraintNode,
]
],
],
typing.Dict[typing.Tuple[int, int], MatchSpec],
]: ...
@ -1451,6 +1444,9 @@ def get_virtual_packages() -> typing.List[PackageInfo]:
def sign(data: str, secret_key: str) -> str:
pass
def simplify_conflicts(arg0: ProblemsGraph) -> ProblemsGraph:
pass
def transmute(
source_package: Path,
destination_package: Path,

View File

@ -263,10 +263,8 @@ PYBIND11_MODULE(bindings, m)
py::class_<PbGraph::RootNode>(pyPbGraph, "RootNode").def(py::init<>());
py::class_<PbGraph::PackageNode, PackageInfo>(pyPbGraph, "PackageNode");
py::class_<PbGraph::UnresolvedDependencyNode, MatchSpec>(pyPbGraph, "UnresolvedDependencyNode")
.def_readwrite("problem_type", &PbGraph::UnresolvedDependencyNode::problem_type);
py::class_<PbGraph::ConstraintNode, MatchSpec>(pyPbGraph, "ConstraintNode")
.def_readonly_static("problem_type", &PbGraph::ConstraintNode::problem_type);
py::class_<PbGraph::UnresolvedDependencyNode, MatchSpec>(pyPbGraph, "UnresolvedDependencyNode");
py::class_<PbGraph::ConstraintNode, MatchSpec>(pyPbGraph, "ConstraintNode");
py::class_<PbGraph::conflicts_t>(pyPbGraph, "ConflictMap")
.def(py::init([]() { return PbGraph::conflicts_t(); }))
@ -297,6 +295,8 @@ PYBIND11_MODULE(bindings, m)
}
);
m.def("simplify_conflicts", &simplify_conflicts);
using CpPbGraph = CompressedProblemsGraph;
auto pyCpPbGraph = py::class_<CpPbGraph>(m, "CompressedProblemsGraph");
@ -309,7 +309,7 @@ PYBIND11_MODULE(bindings, m)
py::class_<CpPbGraph::UnresolvedDependencyListNode>(pyCpPbGraph, "UnresolvedDependencyListNode")
);
bind_NamedList(py::class_<CpPbGraph::ConstraintListNode>(pyCpPbGraph, "ConstraintListNode"));
bind_NamedList(py::class_<CpPbGraph::edge_t>(pyCpPbGraph, "DependencyListList"));
bind_NamedList(py::class_<CpPbGraph::edge_t>(pyCpPbGraph, "DependencyList"));
pyCpPbGraph.def_property_readonly_static(
"ConflictMap",
[](py::handle) { return py::type::of<PbGraph::conflicts_t>(); }
@ -330,7 +330,6 @@ PYBIND11_MODULE(bindings, m)
return std::pair(g.nodes(), g.edges());
}
)
.def("summary_message", [](const CpPbGraph& self) { return problem_summary_msg(self); })
.def("tree_message", [](const CpPbGraph& self) { return problem_tree_msg(self); });
py::class_<History>(m, "History")