Skip to content

feat: allow listing only direct dependencies via --direct-deps-only #3921

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 28 additions & 17 deletions libmamba/src/api/list.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
bool canonical = false;
bool export_ = false;
bool revisions = false;
bool direct_deps_only = false;
};

struct formatted_pkg
Expand Down Expand Up @@ -152,6 +153,7 @@
{ return (regex.empty() || std::regex_search(pkg_info.name, spec_pat)); };

auto all_records = prefix_data.all_pkg_mgr_records();
auto requested_specs = prefix_data.history().get_requested_specs_map();

Check warning on line 156 in libmamba/src/api/list.cpp

View check run for this annotation

Codecov / codecov/patch

libmamba/src/api/list.cpp#L156

Added line #L156 was not covered by tests
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
auto requested_specs = prefix_data.history().get_requested_specs_map();
const auto requested_specs = prefix_data.history().get_requested_specs_map();

I cant find a modification for this so I suspect const works here.


if (ctx.output_params.json)
{
Expand Down Expand Up @@ -185,6 +187,12 @@
auto obj = nlohmann::json();
const auto& pkg_info = all_records.find(key)->second;

if (options.direct_deps_only
&& requested_specs.find(pkg_info.name) == requested_specs.end())

Check warning on line 191 in libmamba/src/api/list.cpp

View check run for this annotation

Codecov / codecov/patch

libmamba/src/api/list.cpp#L190-L191

Added lines #L190 - L191 were not covered by tests
{
continue;

Check warning on line 193 in libmamba/src/api/list.cpp

View check run for this annotation

Codecov / codecov/patch

libmamba/src/api/list.cpp#L193

Added line #L193 was not covered by tests
}

if (accept_package(pkg_info))
{
auto channels = channel_context.make_channel(pkg_info.package_url);
Expand Down Expand Up @@ -213,25 +221,29 @@
<< "\n\n";

formatted_pkg formatted_pkgs;

std::vector<formatted_pkg> packages;

// order list of packages from prefix_data by alphabetical order
for (const auto& package : all_records)
for (const auto& [key, pkg_info] : all_records)
{
if (accept_package(package.second))
if (options.direct_deps_only
&& requested_specs.find(pkg_info.name) == requested_specs.end())

Check warning on line 229 in libmamba/src/api/list.cpp

View check run for this annotation

Codecov / codecov/patch

libmamba/src/api/list.cpp#L228-L229

Added lines #L228 - L229 were not covered by tests
{
continue;

Check warning on line 231 in libmamba/src/api/list.cpp

View check run for this annotation

Codecov / codecov/patch

libmamba/src/api/list.cpp#L231

Added line #L231 was not covered by tests
}

if (accept_package(pkg_info))
{
auto channels = channel_context.make_channel(package.second.channel);
assert(channels.size() == 1); // A URL can only resolve to one channel
formatted_pkgs.channel = get_formatted_channel(package.second, channels.front());
formatted_pkgs.name = package.second.name;
formatted_pkgs.version = package.second.version;
formatted_pkgs.build = package.second.build_string;
formatted_pkgs.url = package.second.package_url;
formatted_pkgs.md5 = package.second.md5;
formatted_pkgs.sha256 = package.second.sha256;
formatted_pkgs.build_string = package.second.build_string;
formatted_pkgs.platform = package.second.platform;
auto channels = channel_context.make_channel(pkg_info.channel);
assert(channels.size() == 1);
formatted_pkgs.channel = get_formatted_channel(pkg_info, channels.front());
formatted_pkgs.name = pkg_info.name;
formatted_pkgs.version = pkg_info.version;
formatted_pkgs.build = pkg_info.build_string;
formatted_pkgs.url = pkg_info.package_url;
formatted_pkgs.md5 = pkg_info.md5;
formatted_pkgs.sha256 = pkg_info.sha256;
formatted_pkgs.build_string = pkg_info.build_string;
formatted_pkgs.platform = pkg_info.platform;

Check warning on line 246 in libmamba/src/api/list.cpp

View check run for this annotation

Codecov / codecov/patch

libmamba/src/api/list.cpp#L236-L246

Added lines #L236 - L246 were not covered by tests
packages.push_back(formatted_pkgs);
}
}
Expand All @@ -240,7 +252,6 @@
: compare_alphabetically;
std::sort(packages.begin(), packages.end(), comparator);

// format and print output
if (options.revisions)
{
if (options.explicit_)
Expand Down Expand Up @@ -333,7 +344,6 @@
}
else
{
auto requested_specs = prefix_data.history().get_requested_specs_map();
printers::Table t({ "Name", "Version", "Build", "Channel" });
t.set_alignment({ printers::alignment::left,
printers::alignment::left,
Expand Down Expand Up @@ -379,6 +389,7 @@
options.canonical = config.at("canonical").value<bool>();
options.export_ = config.at("export").value<bool>();
options.revisions = config.at("revisions").value<bool>();
options.direct_deps_only = config.at("direct_deps_only").value<bool>();

Check warning on line 392 in libmamba/src/api/list.cpp

View check run for this annotation

Codecov / codecov/patch

libmamba/src/api/list.cpp#L392

Added line #L392 was not covered by tests

auto channel_context = ChannelContext::make_conda_compatible(config.context());
detail::list_packages(config.context(), regex, channel_context, std::move(options));
Expand Down
11 changes: 11 additions & 0 deletions micromamba/src/list.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,17 @@
Configurable("revisions", false).group("cli").description("List the revision history.")
);
subcom->add_flag("--revisions", revisions.get_cli_config<bool>(), revisions.description());

auto& direct_deps_only = config.insert(
Configurable("direct_deps_only", false)
.group("cli")

Check warning on line 86 in micromamba/src/list.cpp

View check run for this annotation

Codecov / codecov/patch

micromamba/src/list.cpp#L84-L86

Added lines #L84 - L86 were not covered by tests
.description("Show only directly installed packages (user requested).")
);
subcom->add_flag(

Check warning on line 89 in micromamba/src/list.cpp

View check run for this annotation

Codecov / codecov/patch

micromamba/src/list.cpp#L89

Added line #L89 was not covered by tests
"--direct-deps-only",
direct_deps_only.get_cli_config<bool>(),
direct_deps_only.description()

Check warning on line 92 in micromamba/src/list.cpp

View check run for this annotation

Codecov / codecov/patch

micromamba/src/list.cpp#L91-L92

Added lines #L91 - L92 were not covered by tests
);
}

void
Expand Down
67 changes: 67 additions & 0 deletions micromamba/tests/test_list.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import platform
import subprocess
import sys
import json

import pytest
import re
Expand Down Expand Up @@ -154,6 +155,72 @@ def test_list_name(tmp_home, tmp_root_prefix, tmp_xtensor_env, quiet_flag):
assert full_names == ["xtensor"]


@pytest.mark.parametrize("direct_deps_flag", ["", "--direct-deps-only"])
@pytest.mark.parametrize("env_selector", ["", "name", "prefix"])
@pytest.mark.parametrize("shared_pkgs_dirs", [True], indirect=True)
def test_list_direct_deps_only_json(
tmp_home, tmp_root_prefix, tmp_env_name, tmp_xtensor_env, env_selector, direct_deps_flag
):
"""Check filtering with --direct-deps-only and JSON output."""
if env_selector == "prefix":
res_str = helpers.umamba_list("-p", tmp_xtensor_env, "--json", direct_deps_flag)
elif env_selector == "name":
res_str = helpers.umamba_list("-n", tmp_env_name, "--json", direct_deps_flag)
else:
# Use current env
res_str = helpers.umamba_list("--json", direct_deps_flag)

res = json.loads(res_str)
names = [i["name"] for i in res]

if direct_deps_flag == "--direct-deps-only":
assert "xtensor" in names # Explicitly installed
assert "xtl" not in names # Dependency
assert len(names) == 1 # Only xtensor should be listed
else:
assert "xtensor" in names
assert "xtl" in names # Dependency should be included without the flag
assert len(names) > 1

assert all(
i["channel"] == "conda-forge" and i["base_url"] == "https://conda.anaconda.org/conda-forge"
for i in res
)


@pytest.mark.parametrize("direct_deps_flag", ["", "--direct-deps-only"])
@pytest.mark.parametrize("env_selector", ["", "name", "prefix"])
@pytest.mark.parametrize("shared_pkgs_dirs", [True], indirect=True)
def test_list_direct_deps_only_no_json(
tmp_home, tmp_root_prefix, tmp_env_name, tmp_xtensor_env, env_selector, direct_deps_flag
):
"""Check filtering with --direct-deps-only and table output."""
if env_selector == "prefix":
res = helpers.umamba_list("-p", tmp_xtensor_env, direct_deps_flag)
elif env_selector == "name":
res = helpers.umamba_list("-n", tmp_env_name, direct_deps_flag)
else:
# Use current env
res = helpers.umamba_list(direct_deps_flag)

# Split lines and ignore header/blank lines
lines = [line.strip() for line in res.strip().split("\n") if line]
package_lines = [
line
for line in lines
if not line.startswith("#") and "Name" not in line and "Version" not in line
]

if direct_deps_flag == "--direct-deps-only":
assert any("xtensor" in line for line in package_lines)
assert not any("xtl" in line for line in package_lines)
assert len(package_lines) == 1 # Only xtensor should be listed
else:
assert any("xtensor" in line for line in package_lines)
assert any("xtl" in line for line in package_lines)
assert len(package_lines) > 1


env_yaml_content_to_install_numpy_with_pip = """
channels:
- conda-forge
Expand Down
Loading