diff --git a/micromamba/src/env.cpp b/micromamba/src/env.cpp index d17e3b4e71..8b636596f7 100644 --- a/micromamba/src/env.cpp +++ b/micromamba/src/env.cpp @@ -180,6 +180,7 @@ set_env_command(CLI::App* com, Configuration& config) History& hist = pd.history(); const auto& versions_map = pd.records(); + const auto& pip_versions_map = pd.pip_records(); auto requested_specs_map = hist.get_requested_specs_map(); std::stringstream dependencies; std::set channels; @@ -227,6 +228,21 @@ set_env_command(CLI::App* com, Configuration& config) channels.insert(chan.display_name()); } } + + // Add a `pip` subsection in `dependencies` listing wheels installed from PyPI + if (!pip_versions_map.empty()) + { + dependencies << (first_dependency_printed ? ",\n" : "") << " { \"pip\": [\n"; + first_dependency_printed = false; + for (const auto& [k, v] : pip_versions_map) + { + dependencies << (first_dependency_printed ? ",\n" : "") << " \"" + << v.name << "==" << v.version << "\""; + first_dependency_printed = true; + } + dependencies << "\n ]\n }"; + } + dependencies << (first_dependency_printed ? "\n" : ""); std::cout << "{\n"; @@ -255,6 +271,7 @@ set_env_command(CLI::App* com, Configuration& config) History& hist = pd.history(); const auto& versions_map = pd.records(); + const auto& pip_versions_map = pd.pip_records(); std::cout << "name: " << get_env_name(ctx, ctx.prefix_params.target_prefix) << "\n"; std::cout << "channels:\n"; @@ -278,7 +295,7 @@ set_env_command(CLI::App* com, Configuration& config) } else { - dependencies << "- "; + dependencies << " - "; if (channel_subdir) { dependencies @@ -303,10 +320,19 @@ set_env_command(CLI::App* com, Configuration& config) channels.insert(chan.display_name()); } } + // Add a `pip` subsection in `dependencies` listing wheels installed from PyPI + if (!pip_versions_map.empty()) + { + dependencies << " - pip:\n"; + for (const auto& [k, v] : pip_versions_map) + { + dependencies << " - " << v.name << "==" << v.version << "\n"; + } + } for (const auto& c : channels) { - std::cout << "- " << c << "\n"; + std::cout << " - " << c << "\n"; } std::cout << "dependencies:\n" << dependencies.str() << std::endl; diff --git a/micromamba/tests/test_env.py b/micromamba/tests/test_env.py index db443a44b0..e6785407dd 100644 --- a/micromamba/tests/test_env.py +++ b/micromamba/tests/test_env.py @@ -484,3 +484,38 @@ def test_env_update_empty_base(tmp_home, tmp_root_prefix, tmp_path): packages = helpers.umamba_list("-p", env_prefix, "--json") assert any(package["name"] == "xtensor" for package in packages) assert any(package["name"] == "python" for package in packages) + + +env_yaml_content_env_export_with_pip = """ +channels: +- conda-forge +dependencies: +- pip +- pip: + - requests==2.32.3 +""" + + +@pytest.mark.parametrize("json_flag", [None, "--json"]) +def test_env_export_with_pip(tmp_path, json_flag): + env_name = "env_export_with_pip" + + env_file_yml = tmp_path / "test_env_yaml_content_to_install_requests_with_pip.yaml" + env_file_yml.write_text(env_yaml_content_env_export_with_pip) + + flags = list(filter(None, [json_flag])) + helpers.create("-n", env_name, "-f", env_file_yml, no_dry_run=True) + + output = helpers.run_env("export", "-n", env_name, *flags) + + # JSON is already parsed + ret = output if json_flag else yaml.safe_load(output) + + assert ret["name"] == env_name + assert env_name in ret["prefix"] + assert set(ret["channels"]) == {"conda-forge"} + + pip_section = next( + dep for dep in ret["dependencies"] if isinstance(dep, dict) and ["pip"] == [*dep] + ) + assert pip_section["pip"] == ["requests==2.32.3"]