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;
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 is_equivalent_to(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:

View File

@ -140,37 +140,28 @@ namespace mamba
namespace
{
enum struct ChannelMatch
auto
channel_match(const std::vector<specs::Channel>& ms_channels, const specs::CondaURL& pkg_url)
-> specs::Channel::Match
{
None,
ChannelOnly,
ChannelAndSubdir,
};
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)
auto match = specs::Channel::Match::No;
// More than one element means the channel spec was a custom_multi_channel
for (const auto& chan : ms_channels)
{
// More than one element means the channel spec was a custom_multi_channel.
// We need to add any repo that matches.
for (const auto& cand_chan : candidate_channels)
switch (chan.contains_package(pkg_url))
{
if (repo_chan.url_equivalent_with(cand_chan))
{
if (util::set_is_subset_of(repo_chan.platforms(), cand_chan.platforms()))
{
return ChannelMatch::ChannelAndSubdir;
}
return ChannelMatch::ChannelOnly;
}
case specs::Channel::Match::Full:
return specs::Channel::Match::Full;
case specs::Channel::Match::InOtherPlatform:
// Keep looking for full matches
match = specs::Channel::Match::InOtherPlatform;
break;
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()
);
auto ms_channel = channel_context.make_channel(*ms.channel());
auto ms_channels = channel_context.make_channel(*ms.channel());
solv::ObjQueue selected_pkgs = {};
auto other_subdir_match = std::string();
@ -206,28 +197,32 @@ namespace mamba
match,
[&](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());
// TODO this does not work with s.url(), we need to proper channel class
// to properly manage this.
auto repo = solv::ObjRepoView(*s.raw()->repo);
const auto match = channel_match(
channel_context.make_channel(repo.url()),
ms_channel
);
const auto match = channel_match(ms_channels, specs::CondaURL::parse(s.url()));
switch (match)
{
case (ChannelMatch::ChannelAndSubdir):
case (specs::Channel::Match::Full):
{
selected_pkgs.push_back(s.id());
break;
}
case (ChannelMatch::ChannelOnly):
case (specs::Channel::Match::InOtherPlatform):
{
other_subdir_match = s.subdir();
break;
}
case (ChannelMatch::None):
case (specs::Channel::Match::No):
{
break;
}
}
}

View File

@ -155,26 +155,27 @@ namespace mamba::specs
{
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());
}
auto Channel::contains_package(const CondaURL& pkg) const -> bool
auto Channel::contains_package(const CondaURL& pkg) const -> Match
{
if (is_package())
{
return url_equivalent_with_impl(url(), pkg);
}
if (!platforms().contains(std::string(pkg.platform_name())))
{
return false;
return url_equivalent_with_impl(url(), pkg) ? Match::Full : Match::No;
}
auto pkg_repo = pkg;
pkg_repo.clear_platform();
const auto plat = std::string(pkg_repo.platform_name());
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")
{
using namespace conda_url_literals;
using Match = Channel::Match;
SUBCASE("https://repo.mamba.pm/")
{
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_FALSE(chan.contains_package("https://repo.mamba.pm/win-64/pkg.conda"_cu));
CHECK_FALSE(chan.contains_package("https://repo.mamba.pm/pkg.conda"_cu));
CHECK_EQ(
chan.contains_package("https://repo.mamba.pm/linux-64/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")
{
auto chan = Channel("https://repo.mamba.pm/osx-64/foo.tar.bz2"_cu, "", {});
CHECK(chan.contains_package(chan.url()));
CHECK_FALSE(chan.contains_package("https://repo.mamba.pm/win-64/pkg.conda"_cu));
CHECK_FALSE(chan.contains_package("https://repo.mamba.pm/pkg.conda"_cu));
CHECK_EQ(chan.contains_package(chan.url()), Match::Full);
CHECK_EQ(chan.contains_package("https://repo.mamba.pm/win-64/pkg.conda"_cu), Match::No);
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(
"https://user:pass@repo.mamba.pm/conda-forge/"_cu,
"conda-forge",
{ "win-64" }
);
CHECK(chan.contains_package(chan.url() / "win-64/pkg.conda"));
CHECK(chan.contains_package("https://repo.mamba.pm/conda-forge/win-64/pkg.conda"_cu));
CHECK_FALSE(
chan.contains_package("https://repo.mamba.pm/conda-forge/osx-64/pkg.conda"_cu)
CHECK_EQ(chan.contains_package(chan.url() / "win-64/pkg.conda"), Match::Full);
CHECK_EQ(
chan.contains_package("https://repo.mamba.pm/conda-forge/win-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("__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 //
.def_property_readonly_static(
"ChannelMap",

View File

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