Pool channel match (#3122)

* Add Match return type to Channel::contains_package

* Simplify channel specific matchspec in Pool

* (Dirty) fix force reinstall
This commit is contained in:
Antoine Prouvost 2024-01-11 15:07:54 +01:00 committed by GitHub
parent f72364d404
commit 8ab10efa07
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 85 additions and 60 deletions

View File

@ -107,13 +107,20 @@ namespace mamba::specs
auto clear_display_name() -> std::string; auto clear_display_name() -> std::string;
void set_display_name(std::string display_name); void set_display_name(std::string display_name);
enum struct Match
{
No,
InOtherPlatform,
Full,
};
[[nodiscard]] auto url_equivalent_with(const Channel& other) const -> bool; [[nodiscard]] auto url_equivalent_with(const Channel& other) const -> bool;
[[nodiscard]] auto is_equivalent_to(const Channel& other) const -> bool; [[nodiscard]] auto is_equivalent_to(const Channel& other) const -> bool;
[[nodiscard]] auto contains_equivalent(const Channel& other) const -> bool; [[nodiscard]] auto contains_equivalent(const Channel& other) const -> bool;
[[nodiscard]] auto contains_package(const CondaURL& pkg) const -> bool; [[nodiscard]] auto contains_package(const CondaURL& pkg) const -> Match;
private: private:

View File

@ -140,37 +140,28 @@ namespace mamba
namespace namespace
{ {
enum struct ChannelMatch auto
channel_match(const std::vector<specs::Channel>& ms_channels, const specs::CondaURL& pkg_url)
-> specs::Channel::Match
{ {
None, auto match = specs::Channel::Match::No;
ChannelOnly, // More than one element means the channel spec was a custom_multi_channel
ChannelAndSubdir, for (const auto& chan : ms_channels)
};
auto channel_match(
const std::vector<specs::Channel>& repo_channels,
const std::vector<specs::Channel>& candidate_channels
) -> ChannelMatch
{
// More than one element means the channel spec was a custom_multi_channel,
// which should never happen for the repo
for (const auto& repo_chan : repo_channels)
{ {
// More than one element means the channel spec was a custom_multi_channel. switch (chan.contains_package(pkg_url))
// We need to add any repo that matches.
for (const auto& cand_chan : candidate_channels)
{ {
if (repo_chan.url_equivalent_with(cand_chan)) case specs::Channel::Match::Full:
{ return specs::Channel::Match::Full;
if (util::set_is_subset_of(repo_chan.platforms(), cand_chan.platforms())) case specs::Channel::Match::InOtherPlatform:
{ // Keep looking for full matches
return ChannelMatch::ChannelAndSubdir; match = specs::Channel::Match::InOtherPlatform;
} break;
return ChannelMatch::ChannelOnly; case specs::Channel::Match::No:
} // No overriding potential InOtherPlatform match
break;
} }
} }
return ChannelMatch::None; return match;
} }
/** /**
@ -198,7 +189,7 @@ namespace mamba
ms.conda_build_form().c_str() ms.conda_build_form().c_str()
); );
auto ms_channel = channel_context.make_channel(*ms.channel()); auto ms_channels = channel_context.make_channel(*ms.channel());
solv::ObjQueue selected_pkgs = {}; solv::ObjQueue selected_pkgs = {};
auto other_subdir_match = std::string(); auto other_subdir_match = std::string();
@ -206,28 +197,32 @@ namespace mamba
match, match,
[&](solv::ObjSolvableViewConst s) [&](solv::ObjSolvableViewConst s)
{ {
if (s.installed())
{
// This will have the effect that channel-specific MatchSpec will always be
// reinstalled.
// This is not the intended behaviour but an historical artifact on which
// ``--force-reinstall`` currently rely.
return;
}
assert(ms.channel().has_value()); assert(ms.channel().has_value());
// TODO this does not work with s.url(), we need to proper channel class const auto match = channel_match(ms_channels, specs::CondaURL::parse(s.url()));
// to properly manage this.
auto repo = solv::ObjRepoView(*s.raw()->repo);
const auto match = channel_match(
channel_context.make_channel(repo.url()),
ms_channel
);
switch (match) switch (match)
{ {
case (ChannelMatch::ChannelAndSubdir): case (specs::Channel::Match::Full):
{ {
selected_pkgs.push_back(s.id()); selected_pkgs.push_back(s.id());
break; break;
} }
case (ChannelMatch::ChannelOnly): case (specs::Channel::Match::InOtherPlatform):
{ {
other_subdir_match = s.subdir(); other_subdir_match = s.subdir();
break; break;
} }
case (ChannelMatch::None): case (specs::Channel::Match::No):
{ {
break;
} }
} }
} }

View File

@ -155,26 +155,27 @@ namespace mamba::specs
{ {
if (other.is_package()) if (other.is_package())
{ {
return contains_package(other.url()); return contains_package(other.url()) == Match::Full;
} }
return url_equivalent_with(other) && util::set_is_superset_of(platforms(), other.platforms()); return url_equivalent_with(other) && util::set_is_superset_of(platforms(), other.platforms());
} }
auto Channel::contains_package(const CondaURL& pkg) const -> bool auto Channel::contains_package(const CondaURL& pkg) const -> Match
{ {
if (is_package()) if (is_package())
{ {
return url_equivalent_with_impl(url(), pkg); return url_equivalent_with_impl(url(), pkg) ? Match::Full : Match::No;
}
if (!platforms().contains(std::string(pkg.platform_name())))
{
return false;
} }
auto pkg_repo = pkg; auto pkg_repo = pkg;
pkg_repo.clear_platform(); const auto plat = std::string(pkg_repo.platform_name());
pkg_repo.clear_package(); pkg_repo.clear_package();
return url_equivalent_with_impl(url(), pkg_repo); pkg_repo.clear_platform();
if (url_equivalent_with_impl(url(), pkg_repo))
{
return platforms().contains(plat) ? Match::Full : Match::InOtherPlatform;
}
return Match::No;
} }
/**************************************** /****************************************

View File

@ -182,34 +182,48 @@ TEST_SUITE("specs::channel")
SUBCASE("Contains package") SUBCASE("Contains package")
{ {
using namespace conda_url_literals; using namespace conda_url_literals;
using Match = Channel::Match;
SUBCASE("https://repo.mamba.pm/") SUBCASE("https://repo.mamba.pm/")
{ {
auto chan = Channel("https://repo.mamba.pm/"_cu, "conda-forge", { "linux-64" }); auto chan = Channel("https://repo.mamba.pm/"_cu, "conda-forge", { "linux-64" });
CHECK(chan.contains_package("https://repo.mamba.pm/linux-64/pkg.conda"_cu)); CHECK_EQ(
CHECK_FALSE(chan.contains_package("https://repo.mamba.pm/win-64/pkg.conda"_cu)); chan.contains_package("https://repo.mamba.pm/linux-64/pkg.conda"_cu),
CHECK_FALSE(chan.contains_package("https://repo.mamba.pm/pkg.conda"_cu)); Match::Full
);
CHECK_EQ(
chan.contains_package("https://repo.mamba.pm/win-64/pkg.conda"_cu),
Match::InOtherPlatform
);
CHECK_EQ(
chan.contains_package("https://repo.mamba.pm/pkg.conda"_cu),
Match::InOtherPlatform
);
} }
SUBCASE("https://repo.mamba.pm/osx-64/foo.tar.gz") SUBCASE("https://repo.mamba.pm/osx-64/foo.tar.gz")
{ {
auto chan = Channel("https://repo.mamba.pm/osx-64/foo.tar.bz2"_cu, "", {}); auto chan = Channel("https://repo.mamba.pm/osx-64/foo.tar.bz2"_cu, "", {});
CHECK(chan.contains_package(chan.url())); CHECK_EQ(chan.contains_package(chan.url()), Match::Full);
CHECK_FALSE(chan.contains_package("https://repo.mamba.pm/win-64/pkg.conda"_cu)); CHECK_EQ(chan.contains_package("https://repo.mamba.pm/win-64/pkg.conda"_cu), Match::No);
CHECK_FALSE(chan.contains_package("https://repo.mamba.pm/pkg.conda"_cu)); CHECK_EQ(chan.contains_package("https://repo.mamba.pm/pkg.conda"_cu), Match::No);
} }
SUBCASE("https://user:pass@repo.mamba.pm/conda-forge/win-64/") SUBCASE("https://user:pass@repo.mamba.pm/conda-forge/")
{ {
auto chan = Channel( auto chan = Channel(
"https://user:pass@repo.mamba.pm/conda-forge/"_cu, "https://user:pass@repo.mamba.pm/conda-forge/"_cu,
"conda-forge", "conda-forge",
{ "win-64" } { "win-64" }
); );
CHECK(chan.contains_package(chan.url() / "win-64/pkg.conda")); CHECK_EQ(chan.contains_package(chan.url() / "win-64/pkg.conda"), Match::Full);
CHECK(chan.contains_package("https://repo.mamba.pm/conda-forge/win-64/pkg.conda"_cu)); CHECK_EQ(
CHECK_FALSE( chan.contains_package("https://repo.mamba.pm/conda-forge/win-64/pkg.conda"_cu),
chan.contains_package("https://repo.mamba.pm/conda-forge/osx-64/pkg.conda"_cu) Match::Full
);
CHECK_EQ(
chan.contains_package("https://repo.mamba.pm/conda-forge/osx-64/pkg.conda"_cu),
Match::InOtherPlatform
); );
} }
} }

View File

@ -424,6 +424,13 @@ namespace mambapy
.def("__copy__", &copy<BasicHTTPAuthentication>) .def("__copy__", &copy<BasicHTTPAuthentication>)
.def("__deepcopy__", &deepcopy<BasicHTTPAuthentication>, py::arg("memo")); .def("__deepcopy__", &deepcopy<BasicHTTPAuthentication>, py::arg("memo"));
py::enum_<Channel::Match>(py_channel, "Match")
.value("No", Channel::Match::No)
.value("InOtherPlatform", Channel::Match::InOtherPlatform)
.value("Full", Channel::Match::Full)
.def(py::init(&enum_from_str<Channel::Match>));
py::implicitly_convertible<py::str, Channel::Match>();
py_channel // py_channel //
.def_property_readonly_static( .def_property_readonly_static(
"ChannelMap", "ChannelMap",

View File

@ -433,6 +433,7 @@ def test_ChannelResolveParams():
def test_Channel(): def test_Channel():
Channel = libmambapy.specs.Channel Channel = libmambapy.specs.Channel
Match = libmambapy.specs.Channel.Match
CondaURL = libmambapy.specs.CondaURL CondaURL = libmambapy.specs.CondaURL
url_1 = CondaURL.parse("https://repo.anaconda.com/conda-forge") url_1 = CondaURL.parse("https://repo.anaconda.com/conda-forge")
@ -487,8 +488,8 @@ def test_Channel():
assert chan.contains_equivalent(chan) assert chan.contains_equivalent(chan)
assert other.contains_equivalent(chan) assert other.contains_equivalent(chan)
assert not chan.contains_equivalent(other) assert not chan.contains_equivalent(other)
assert chan.contains_package(chan.url / "noarch/pkg.conda") assert chan.contains_package(chan.url / "noarch/pkg.conda") == Match.Full
assert not chan.contains_package(chan.url / "win-64/pkg.conda") assert chan.contains_package(chan.url / "win-64/pkg.conda") == Match.InOtherPlatform
def test_Channel_resolve(): def test_Channel_resolve():