From 1f36330a2d4964511078834535989963e51ddca2 Mon Sep 17 00:00:00 2001 From: Cornelius Roemer Date: Sun, 4 May 2025 21:57:32 +0200 Subject: [PATCH 1/5] feat: allow listing only direct dependencies via --direct-deps-only resolves #3344 I can't test micromamba as I can't get it to build (see https://github.com/mamba-org/mamba/issues/3920) It seems to work for mamba --- libmamba/src/api/list.cpp | 29 ++++++++++++++- micromamba/src/list.cpp | 7 ++++ micromamba/tests/test_list.py | 69 +++++++++++++++++++++++++++++++++++ 3 files changed, 104 insertions(+), 1 deletion(-) diff --git a/libmamba/src/api/list.cpp b/libmamba/src/api/list.cpp index a26d6be816..c5affb342e 100644 --- a/libmamba/src/api/list.cpp +++ b/libmamba/src/api/list.cpp @@ -32,6 +32,7 @@ namespace mamba bool canonical = false; bool export_ = false; bool revisions = false; + bool direct_deps_only = false; }; struct formatted_pkg @@ -152,6 +153,7 @@ namespace mamba { 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(); if (ctx.output_params.json) { @@ -185,6 +187,11 @@ namespace mamba 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()) + { + continue; + } + if (accept_package(pkg_info)) { auto channels = channel_context.make_channel(pkg_info.package_url); @@ -290,6 +297,11 @@ namespace mamba } for (auto p : packages) { + if (options.direct_deps_only && requested_specs.find(p.name) == requested_specs.end()) + { + continue; + } + if (options.md5 && options.sha256) { throw mamba_error( @@ -320,6 +332,11 @@ namespace mamba } for (auto p : packages) { + if (options.direct_deps_only && requested_specs.find(p.name) == requested_specs.end()) + { + continue; + } + std::cout << p.channel << "/" << p.platform << "::" << p.name << "-" << p.version << "-" << p.build_string << std::endl; } @@ -328,12 +345,16 @@ namespace mamba { for (auto p : packages) { + if (options.direct_deps_only && requested_specs.find(p.name) == requested_specs.end()) + { + continue; + } + std::cout << p.name << "=" << p.version << "=" << p.build_string << std::endl; } } 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, @@ -343,6 +364,11 @@ namespace mamba for (auto p : packages) { + if (options.direct_deps_only && requested_specs.find(p.name) == requested_specs.end()) + { + continue; + } + printers::FormattedString formatted_name(p.name); if (requested_specs.find(p.name) != requested_specs.end()) { @@ -379,6 +405,7 @@ namespace mamba options.canonical = config.at("canonical").value(); options.export_ = config.at("export").value(); options.revisions = config.at("revisions").value(); + options.direct_deps_only = config.at("direct_deps_only").value(); auto channel_context = ChannelContext::make_conda_compatible(config.context()); detail::list_packages(config.context(), regex, channel_context, std::move(options)); diff --git a/micromamba/src/list.cpp b/micromamba/src/list.cpp index 613dd4ff78..ba713ceaef 100644 --- a/micromamba/src/list.cpp +++ b/micromamba/src/list.cpp @@ -80,6 +80,13 @@ init_list_parser(CLI::App* subcom, Configuration& config) Configurable("revisions", false).group("cli").description("List the revision history.") ); subcom->add_flag("--revisions", revisions.get_cli_config(), revisions.description()); + + auto& direct_deps_only = config.insert( + Configurable("direct_deps_only", false) + .group("cli") + .description("Show only directly installed packages (user requested).") + ); + subcom->add_flag("--direct-deps-only", direct_deps_only.get_cli_config(), direct_deps_only.description()); } void diff --git a/micromamba/tests/test_list.py b/micromamba/tests/test_list.py index 8accb86b61..722e2b17c8 100644 --- a/micromamba/tests/test_list.py +++ b/micromamba/tests/test_list.py @@ -1,6 +1,7 @@ import platform import subprocess import sys +import json import pytest import re @@ -8,6 +9,12 @@ from . import helpers +@pytest.fixture +def tmp_xtensor_env(tmp_home, tmp_root_prefix, tmp_env_name, shared_pkgs_dirs): + helpers.install("xtensor", "-n", tmp_env_name) + return helpers.get_env_path(tmp_env_name) + + @pytest.mark.parametrize("reverse_flag", ["", "--reverse"]) @pytest.mark.parametrize("quiet_flag", ["", "-q", "--quiet"]) @pytest.mark.parametrize("env_selector", ["", "name", "prefix"]) @@ -154,6 +161,68 @@ 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 From fcafcadf56d9678e037265b776a17ff18bb44922 Mon Sep 17 00:00:00 2001 From: Cornelius Roemer Date: Sun, 4 May 2025 22:00:24 +0200 Subject: [PATCH 2/5] run precommit --- libmamba/src/api/list.cpp | 15 ++++++++++----- micromamba/src/list.cpp | 6 +++++- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/libmamba/src/api/list.cpp b/libmamba/src/api/list.cpp index c5affb342e..acdfb93b1b 100644 --- a/libmamba/src/api/list.cpp +++ b/libmamba/src/api/list.cpp @@ -187,7 +187,8 @@ namespace mamba 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()) + if (options.direct_deps_only + && requested_specs.find(pkg_info.name) == requested_specs.end()) { continue; } @@ -297,7 +298,8 @@ namespace mamba } for (auto p : packages) { - if (options.direct_deps_only && requested_specs.find(p.name) == requested_specs.end()) + if (options.direct_deps_only + && requested_specs.find(p.name) == requested_specs.end()) { continue; } @@ -332,7 +334,8 @@ namespace mamba } for (auto p : packages) { - if (options.direct_deps_only && requested_specs.find(p.name) == requested_specs.end()) + if (options.direct_deps_only + && requested_specs.find(p.name) == requested_specs.end()) { continue; } @@ -345,7 +348,8 @@ namespace mamba { for (auto p : packages) { - if (options.direct_deps_only && requested_specs.find(p.name) == requested_specs.end()) + if (options.direct_deps_only + && requested_specs.find(p.name) == requested_specs.end()) { continue; } @@ -364,7 +368,8 @@ namespace mamba for (auto p : packages) { - if (options.direct_deps_only && requested_specs.find(p.name) == requested_specs.end()) + if (options.direct_deps_only + && requested_specs.find(p.name) == requested_specs.end()) { continue; } diff --git a/micromamba/src/list.cpp b/micromamba/src/list.cpp index ba713ceaef..109296b34b 100644 --- a/micromamba/src/list.cpp +++ b/micromamba/src/list.cpp @@ -86,7 +86,11 @@ init_list_parser(CLI::App* subcom, Configuration& config) .group("cli") .description("Show only directly installed packages (user requested).") ); - subcom->add_flag("--direct-deps-only", direct_deps_only.get_cli_config(), direct_deps_only.description()); + subcom->add_flag( + "--direct-deps-only", + direct_deps_only.get_cli_config(), + direct_deps_only.description() + ); } void From 1a8e6adfb6248864bb108cd1d5a93b4ed74a3d39 Mon Sep 17 00:00:00 2001 From: Cornelius Roemer Date: Sun, 4 May 2025 22:04:36 +0200 Subject: [PATCH 3/5] run precommit again --- micromamba/tests/test_list.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/micromamba/tests/test_list.py b/micromamba/tests/test_list.py index 722e2b17c8..b705fb6ef2 100644 --- a/micromamba/tests/test_list.py +++ b/micromamba/tests/test_list.py @@ -210,8 +210,12 @@ def test_list_direct_deps_only_no_json( 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] + 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) From 34a22dac394c8a8749be753b3d5d4070d87431be Mon Sep 17 00:00:00 2001 From: Cornelius Roemer Date: Sun, 4 May 2025 22:33:20 +0200 Subject: [PATCH 4/5] Fix --- libmamba/src/api/list.cpp | 59 +++++++++++++-------------------------- 1 file changed, 19 insertions(+), 40 deletions(-) diff --git a/libmamba/src/api/list.cpp b/libmamba/src/api/list.cpp index acdfb93b1b..9623f904ca 100644 --- a/libmamba/src/api/list.cpp +++ b/libmamba/src/api/list.cpp @@ -221,25 +221,29 @@ namespace mamba << "\n\n"; formatted_pkg formatted_pkgs; - std::vector 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()) + { + continue; + } + + 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; packages.push_back(formatted_pkgs); } } @@ -248,7 +252,6 @@ namespace mamba : compare_alphabetically; std::sort(packages.begin(), packages.end(), comparator); - // format and print output if (options.revisions) { if (options.explicit_) @@ -298,12 +301,6 @@ namespace mamba } for (auto p : packages) { - if (options.direct_deps_only - && requested_specs.find(p.name) == requested_specs.end()) - { - continue; - } - if (options.md5 && options.sha256) { throw mamba_error( @@ -334,12 +331,6 @@ namespace mamba } for (auto p : packages) { - if (options.direct_deps_only - && requested_specs.find(p.name) == requested_specs.end()) - { - continue; - } - std::cout << p.channel << "/" << p.platform << "::" << p.name << "-" << p.version << "-" << p.build_string << std::endl; } @@ -348,12 +339,6 @@ namespace mamba { for (auto p : packages) { - if (options.direct_deps_only - && requested_specs.find(p.name) == requested_specs.end()) - { - continue; - } - std::cout << p.name << "=" << p.version << "=" << p.build_string << std::endl; } } @@ -368,12 +353,6 @@ namespace mamba for (auto p : packages) { - if (options.direct_deps_only - && requested_specs.find(p.name) == requested_specs.end()) - { - continue; - } - printers::FormattedString formatted_name(p.name); if (requested_specs.find(p.name) != requested_specs.end()) { From fe9c53c4eb3468f502f6b45c176935451ac4d54e Mon Sep 17 00:00:00 2001 From: Cornelius Roemer Date: Sun, 4 May 2025 22:36:16 +0200 Subject: [PATCH 5/5] Fix test? --- micromamba/tests/test_list.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/micromamba/tests/test_list.py b/micromamba/tests/test_list.py index b705fb6ef2..00065fb15d 100644 --- a/micromamba/tests/test_list.py +++ b/micromamba/tests/test_list.py @@ -9,12 +9,6 @@ from . import helpers -@pytest.fixture -def tmp_xtensor_env(tmp_home, tmp_root_prefix, tmp_env_name, shared_pkgs_dirs): - helpers.install("xtensor", "-n", tmp_env_name) - return helpers.get_env_path(tmp_env_name) - - @pytest.mark.parametrize("reverse_flag", ["", "--reverse"]) @pytest.mark.parametrize("quiet_flag", ["", "-q", "--quiet"]) @pytest.mark.parametrize("env_selector", ["", "name", "prefix"])