Bind SAT error messages to python (#2127)

* Bind ProblemsGraph and CompressProblemGraph

* Rename summary error message

* Fix Python static property

* Add stubgens for problems bindings

* Give a little help to Windows

* Windows

* Hang in there MSVC

* Fix Pybind init

* Add graph all successors accessor

* Change graph bindings to raw data

* Add NamedList remove_duplicates py::arg

* Regenerate stubgens
This commit is contained in:
Antoine Prouvost 2022-11-23 14:30:03 +01:00 committed by GitHub
parent 44f7727cf2
commit 0181538e54
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 315 additions and 7 deletions

View File

@ -65,6 +65,8 @@ namespace mamba
using typename Base::key_type;
using typename Base::value_type;
conflict_map() = default;
using Base::empty;
using Base::size;
bool has_conflict(key_type const& a) const;
@ -254,8 +256,8 @@ namespace mamba
std::array<std::string_view, 4> indents = { "", " ", "├─ ", "└─ " };
};
std::ostream& print_summary_msg(std::ostream& out, CompressedProblemsGraph const& pbs);
std::string summary_msg(CompressedProblemsGraph const& pbs);
std::ostream& print_problem_summary_msg(std::ostream& out, CompressedProblemsGraph const& pbs);
std::string problem_summary_msg(CompressedProblemsGraph const& pbs);
std::ostream& print_problem_tree_msg(std::ostream& out,
CompressedProblemsGraph const& pbs,

View File

@ -138,7 +138,9 @@ namespace mamba
node_t const& node(node_id id) const;
node_t& node(node_id id);
const node_id_list& successors(node_id id) const;
const adjacency_list& successors() const;
const node_id_list& predecessors(node_id id) const;
const adjacency_list& predecessors() const;
bool has_node(node_id id) const;
bool has_edge(node_id from, node_id to) const;
@ -457,12 +459,24 @@ namespace mamba
return m_successors[id];
}
template <typename N, typename G>
auto DiGraphBase<N, G>::successors() const -> const adjacency_list&
{
return m_successors;
}
template <typename N, typename G>
auto DiGraphBase<N, G>::predecessors(node_id id) const -> const node_id_list&
{
return m_predecessors[id];
}
template <typename N, typename G>
auto DiGraphBase<N, G>::predecessors() const -> const adjacency_list&
{
return m_predecessors;
}
template <typename N, typename G>
auto DiGraphBase<N, G>::has_node(node_id id) const -> bool
{

View File

@ -963,15 +963,15 @@ namespace mamba
* Implementation of summary_msg *
***********************************/
std::ostream& print_summary_msg(std::ostream& out, CompressedProblemsGraph const& pbs)
std::ostream& print_problem_summary_msg(std::ostream& out, CompressedProblemsGraph const& pbs)
{
return out << "Could not solve for environment specs\n";
}
std::string summary_msg(CompressedProblemsGraph const& pbs)
std::string problem_summary_msg(CompressedProblemsGraph const& pbs)
{
std::stringstream ss;
print_summary_msg(ss, pbs);
print_problem_summary_msg(ss, pbs);
return ss.str();
}

View File

@ -5,8 +5,10 @@ import typing
__all__ = [
"Channel",
"ChannelPriority",
"CompressedProblemsGraph",
"Configuration",
"Context",
"DependencyInfo",
"DownloadTargetList",
"ExtraPkgInfo",
"History",
@ -32,6 +34,7 @@ __all__ = [
"PkgMgr",
"Pool",
"PrefixData",
"ProblemsGraph",
"Query",
"QueryFormat",
"Repo",
@ -210,6 +213,114 @@ class ChannelPriority:
kStrict: libmambapy.bindings.ChannelPriority # value = <ChannelPriority.kStrict: 2>
pass
class CompressedProblemsGraph:
class ConflictMap:
def __bool__(self) -> bool: ...
def __contains__(self, arg0: int) -> bool: ...
def __init__(self) -> None: ...
def __iter__(self) -> typing.Iterator: ...
def __len__(self) -> int: ...
def add(self, arg0: int, arg1: int) -> None: ...
def clear(self) -> None: ...
def conflicts(self, arg0: int) -> typing.Set[int]: ...
def has_conflict(self, arg0: int) -> bool: ...
def in_conflict(self, arg0: int, arg1: int) -> bool: ...
pass
class ConstraintListNode:
def __bool__(self) -> bool: ...
def __init__(self) -> None: ...
def __iter__(self) -> typing.Iterator: ...
def __len__(self) -> int: ...
def add(self, arg0: ProblemsGraph.ConstraintNode) -> None: ...
def build_strings_trunc(
self, sep: str = "|", etc: str = "...", remove_duplicates: bool = True
) -> str: ...
def clear(self) -> None: ...
def name(self) -> str: ...
def versions_trunc(
self, sep: str = "|", etc: str = "...", remove_duplicates: bool = True
) -> str: ...
pass
class DependencyListList:
def __bool__(self) -> bool: ...
def __init__(self) -> None: ...
def __iter__(self) -> typing.Iterator: ...
def __len__(self) -> int: ...
def add(self, arg0: DependencyInfo) -> None: ...
def build_strings_trunc(
self, sep: str = "|", etc: str = "...", remove_duplicates: bool = True
) -> str: ...
def clear(self) -> None: ...
def name(self) -> str: ...
def versions_trunc(
self, sep: str = "|", etc: str = "...", remove_duplicates: bool = True
) -> str: ...
pass
class PackageListNode:
def __bool__(self) -> bool: ...
def __init__(self) -> None: ...
def __iter__(self) -> typing.Iterator: ...
def __len__(self) -> int: ...
def add(self, arg0: ProblemsGraph.PackageNode) -> None: ...
def build_strings_trunc(
self, sep: str = "|", etc: str = "...", remove_duplicates: bool = True
) -> str: ...
def clear(self) -> None: ...
def name(self) -> str: ...
def versions_trunc(
self, sep: str = "|", etc: str = "...", remove_duplicates: bool = True
) -> str: ...
pass
class RootNode:
def __init__(self) -> None: ...
pass
class UnresolvedDependencyListNode:
def __bool__(self) -> bool: ...
def __init__(self) -> None: ...
def __iter__(self) -> typing.Iterator: ...
def __len__(self) -> int: ...
def add(self, arg0: ProblemsGraph.UnresolvedDependencyNode) -> None: ...
def build_strings_trunc(
self, sep: str = "|", etc: str = "...", remove_duplicates: bool = True
) -> str: ...
def clear(self) -> None: ...
def name(self) -> str: ...
def versions_trunc(
self, sep: str = "|", etc: str = "...", remove_duplicates: bool = True
) -> str: ...
pass
def conflicts(self) -> ProblemsGraph.ConflictMap: ...
@staticmethod
@typing.overload
def from_problems_graph(arg0: ProblemsGraph) -> CompressedProblemsGraph: ...
@staticmethod
@typing.overload
def from_problems_graph(
arg0: ProblemsGraph, arg1: typing.Callable[[ProblemsGraph, int, int], bool]
) -> CompressedProblemsGraph: ...
def graph(
self,
) -> typing.Tuple[
typing.List[
typing.Union[
ProblemsGraph.RootNode,
CompressedProblemsGraph.PackageListNode,
CompressedProblemsGraph.UnresolvedDependencyListNode,
CompressedProblemsGraph.ConstraintListNode,
]
],
typing.Dict[typing.Tuple[int, int], CompressedProblemsGraph.DependencyListList],
]: ...
def root_node(self) -> int: ...
def summary_message(self) -> str: ...
def tree_message(self) -> str: ...
pass
class Configuration:
def __init__(self) -> None: ...
@property
@ -484,6 +595,28 @@ class Context:
pass
pass
class DependencyInfo:
def __eq__(self, arg0: DependencyInfo) -> bool: ...
def __init__(self, arg0: str) -> None: ...
def __str__(self) -> str: ...
@property
def build_string(self) -> str:
"""
:type: str
"""
@property
def name(self) -> str:
"""
:type: str
"""
@property
def version(self) -> str:
"""
:type: str
"""
__hash__ = None
pass
class DownloadTargetList:
def __init__(self) -> None: ...
def add(self, arg0: SubdirData) -> None: ...
@ -852,6 +985,49 @@ class PrefixData:
"""
pass
class ProblemsGraph:
class ConflictMap:
pass
class ConstraintNode(DependencyInfo):
problem_type: libmambapy.bindings.SolverRuleinfo # value = <SolverRuleinfo.SOLVER_RULE_PKG_CONSTRAINS: 267>
pass
class PackageNode(PackageInfo):
pass
class RootNode:
pass
class UnresolvedDependencyNode(DependencyInfo):
@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
def from_solver(arg0: Solver, arg1: Pool) -> ProblemsGraph: ...
def graph(
self,
) -> typing.Tuple[
typing.List[
typing.Union[
ProblemsGraph.RootNode,
ProblemsGraph.PackageNode,
ProblemsGraph.UnresolvedDependencyNode,
ProblemsGraph.ConstraintNode,
]
],
typing.Dict[typing.Tuple[int, int], DependencyInfo],
]: ...
def root_node(self) -> int: ...
pass
class Query:
def __init__(self, arg0: Pool) -> None: ...
def depends(self, arg0: str, arg1: QueryFormat) -> str: ...

View File

@ -4,9 +4,13 @@
//
// The full license is in the file LICENSE, distributed with this software.
#include <stdexcept>
#include <pybind11/iostream.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/functional.h>
#include <pybind11/operators.h>
#include <nlohmann/json.hpp>
#include <fmt/format.h>
@ -30,8 +34,8 @@
#include "mamba/core/virtual_packages.hpp"
#include "mamba/core/output.hpp"
#include "mamba/core/execution.hpp"
#include <stdexcept>
#include "mamba/core/util_graph.hpp"
#include "mamba/core/satisfiability_error.hpp"
namespace py = pybind11;
@ -46,6 +50,18 @@ namespace query
};
}
namespace PYBIND11_NAMESPACE
{
namespace detail
{
template <typename Key, typename Compare, typename Allocator>
struct type_caster<mamba::vector_set<Key, Compare, Allocator>>
: set_caster<mamba::vector_set<Key, Compare, Allocator>, Key>
{
};
}
}
void
deprecated(char const* message)
{
@ -54,6 +70,34 @@ deprecated(char const* message)
warnings.attr("warn")(message, builtins.attr("DeprecationWarning"), py::arg("stacklevel") = 2);
}
template <typename PyClass>
auto
bind_NamedList(PyClass pyclass)
{
using type = typename PyClass::type;
pyclass.def(py::init())
.def("__len__", [](type const& self) { return self.size(); })
.def("__bool__", [](type const& self) { return !self.empty(); })
.def(
"__iter__",
[](type const& self) { return py::make_iterator(self.begin(), self.end()); },
py::keep_alive<0, 1>())
.def("clear", [](type& self) { return self.clear(); })
.def("add", [](type& self, typename type::value_type const& v) { self.insert(v); })
.def("name", &type::name)
.def("versions_trunc",
&type::versions_trunc,
py::arg("sep") = "|",
py::arg("etc") = "...",
py::arg("remove_duplicates") = true)
.def("build_strings_trunc",
&type::build_strings_trunc,
py::arg("sep") = "|",
py::arg("etc") = "...",
py::arg("remove_duplicates") = true);
return pyclass;
}
PYBIND11_MODULE(bindings, m)
{
using namespace mamba;
@ -188,6 +232,78 @@ PYBIND11_MODULE(bindings, m)
.def_readwrite("description", &MSolverProblem::description)
.def("__str__", [](MSolverProblem const& self) { return self.description; });
py::class_<DependencyInfo>(m, "DependencyInfo")
.def(py::init<std::string const&>())
.def_property_readonly("name", &DependencyInfo::name)
.def_property_readonly("version", &DependencyInfo::version)
.def_property_readonly("build_string", &DependencyInfo::build_string)
.def("__str__", &DependencyInfo::str)
.def(py::self == py::self);
using PbGraph = ProblemsGraph;
auto pyPbGraph = py::class_<PbGraph>(m, "ProblemsGraph");
py::class_<PbGraph::RootNode>(pyPbGraph, "RootNode").def(py::init<>());
py::class_<PbGraph::PackageNode, PackageInfo>(pyPbGraph, "PackageNode");
py::class_<PbGraph::UnresolvedDependencyNode, DependencyInfo>(pyPbGraph,
"UnresolvedDependencyNode")
.def_readwrite("problem_type", &PbGraph::UnresolvedDependencyNode::problem_type);
py::class_<PbGraph::ConstraintNode, DependencyInfo>(pyPbGraph, "ConstraintNode")
.def_readonly_static("problem_type", &PbGraph::ConstraintNode::problem_type);
py::class_<PbGraph::conflicts_t>(pyPbGraph, "ConflictMap")
.def(py::init([]() { return PbGraph::conflicts_t(); }))
.def("__len__", [](PbGraph::conflicts_t const& self) { return self.size(); })
.def("__bool__", [](PbGraph::conflicts_t const& self) { return !self.empty(); })
.def(
"__iter__",
[](PbGraph::conflicts_t const& self)
{ return py::make_iterator(self.begin(), self.end()); },
py::keep_alive<0, 1>())
.def("has_conflict", &PbGraph::conflicts_t::has_conflict)
.def("__contains__", &PbGraph::conflicts_t::has_conflict)
.def("conflicts", &PbGraph::conflicts_t::conflicts)
.def("in_conflict", &PbGraph::conflicts_t::in_conflict)
.def("clear", [](PbGraph::conflicts_t& self) { return self.clear(); })
.def("add", &PbGraph::conflicts_t::add);
pyPbGraph.def_static("from_solver", &PbGraph::from_solver)
.def("root_node", &PbGraph::root_node)
.def("conflicts", &PbGraph::conflicts)
.def("graph",
[](PbGraph const& self)
{
auto const& g = self.graph();
return std::pair(g.nodes(), g.edges());
});
using CpPbGraph = CompressedProblemsGraph;
auto pyCpPbGraph = py::class_<CpPbGraph>(m, "CompressedProblemsGraph");
pyCpPbGraph.def_property_readonly_static(
"RootNode", [](py::handle) { return py::type::of<PbGraph::RootNode>(); });
bind_NamedList(py::class_<CpPbGraph::PackageListNode>(pyCpPbGraph, "PackageListNode"));
bind_NamedList(py::class_<CpPbGraph::UnresolvedDependencyListNode>(
pyCpPbGraph, "UnresolvedDependencyListNode"));
bind_NamedList(py::class_<CpPbGraph::ConstraintListNode>(pyCpPbGraph, "ConstraintListNode"));
bind_NamedList(py::class_<CpPbGraph::edge_t>(pyCpPbGraph, "DependencyListList"));
pyCpPbGraph.def_property_readonly_static(
"ConflictMap", [](py::handle) { return py::type::of<PbGraph::conflicts_t>(); });
pyCpPbGraph.def_static("from_problems_graph", &CpPbGraph::from_problems_graph)
.def_static("from_problems_graph",
[](PbGraph const& pbs) { return CpPbGraph::from_problems_graph(pbs); })
.def("root_node", &CpPbGraph::root_node)
.def("conflicts", &CpPbGraph::conflicts)
.def("graph",
[](CpPbGraph const& self)
{
auto const& g = self.graph();
return std::pair(g.nodes(), g.edges());
})
.def("summary_message", [](CpPbGraph const& self) { return problem_summary_msg(self); })
.def("tree_message", [](CpPbGraph const& self) { return problem_tree_msg(self); });
py::class_<History>(m, "History")
.def(py::init<const fs::u8path&>())
.def("get_requested_specs_map", &History::get_requested_specs_map);