Skip to content

fix: fallback to pypi when removing deps without --pypi flag#5794

Open
theycallmeaabie wants to merge 4 commits intoprefix-dev:mainfrom
theycallmeaabie:fix/remove-fallback-pypi-deps
Open

fix: fallback to pypi when removing deps without --pypi flag#5794
theycallmeaabie wants to merge 4 commits intoprefix-dev:mainfrom
theycallmeaabie:fix/remove-fallback-pypi-deps

Conversation

@theycallmeaabie
Copy link
Copy Markdown

@theycallmeaabie theycallmeaabie commented Mar 30, 2026

Description

When a user adds a pypi dependency (e.g., pixi add gym==0.26.2 --pypi) and later runs pixi remove gym without the --pypi flag, the command would silently warn "Dependency gym doesn't exist" and remove nothing.

This PR adds a fallback: if the package isn't found in conda dependencies, the remove command checks pypi dependencies before giving up. If found there, it removes it as a pypi dependency and displays the correct output.

Before:
$ pixi add gym==0.26.2 --pypi
✔ Added gym==0.26.2
$ pixi remove gym
WARN Dependency gym doesn't exist
✔ Removed gym

After:
$ pixi add gym==0.26.2 --pypi
✔ Added gym==0.26.2
$ pixi remove gym
✔ Removed gym
Removed these as pypi-dependencies.

Fixes #1567

How Has This Been Tested?

  • cargo check -p pixi_cli : compiles clean
  • cargo test -p pixi_cli :41 passed, 0 failed
  • cargo clippy -p pixi_cli : no warnings

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • I have added sufficient tests to cover my changes.
  • I have verified that changes that would impact the JSON schema have been made in schema/model.py.

Signed-off-by: theycallmeaabie <theycallmeaabie@gmail.com>
Copilot AI review requested due to automatic review settings March 30, 2026 12:04
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adjusts pixi remove to better handle dependencies originally added from PyPI when the user omits --pypi, avoiding the misleading “doesn’t exist” warning and ensuring the correct dependency type is reported in the success output.

Changes:

  • Add a fallback in remove to check PyPI dependencies when a dependency is not found as a conda dependency.
  • Thread the resolved dependency type through the success output to print the correct “Removed these as …” message.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 6 comments.

File Description
crates/pixi_cli/src/remove.rs Adds conda→PyPI fallback detection and passes the resolved dependency type to the success display.
crates/pixi_cli/src/cli_config.rs Introduces display_success_with_type so callers can print success messages using an explicit dependency type.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread crates/pixi_cli/src/remove.rs Outdated
Comment on lines +70 to +77
let conda_deps = env.dependencies(spec_type, None);

let all_missing_from_conda = specs
.keys()
.all(|name| !conda_deps.contains_key(name));

if all_missing_from_conda {
let pypi_deps = env.pypi_dependencies(None);
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

The fallback detection uses env.dependencies(spec_type, None) / env.pypi_dependencies(None), which merges across all features in the environment and ignores args.dependency_config.platforms and the selected feature. Since the actual removal uses DependencyOptions (feature + platforms), this can prevent fallback (or trigger it incorrectly) for feature/platform-specific removals. Consider checking presence in the exact target(s) being modified instead.

Suggested change
let conda_deps = env.dependencies(spec_type, None);
let all_missing_from_conda = specs
.keys()
.all(|name| !conda_deps.contains_key(name));
if all_missing_from_conda {
let pypi_deps = env.pypi_dependencies(None);
let feature = args.dependency_config.feature.as_deref();
let conda_deps = env.dependencies(spec_type, feature);
let all_missing_from_conda = specs
.keys()
.all(|name| !conda_deps.contains_key(name));
if all_missing_from_conda {
let pypi_deps = env.pypi_dependencies(feature);

Copilot uses AI. Check for mistakes.
let any_in_pypi = specs.keys().any(|name| {
pypi_deps
.names()
.any(|pypi_name| pypi_name.as_source() == name.as_source())
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

PyPI presence is detected by comparing pypi_name.as_source() to name.as_source(). as_source() preserves the original string and won’t match common normalized equivalents (e.g. foo-bar vs foo_bar, case differences), so the fallback may fail even when the PyPI dep exists. Prefer comparing normalized forms (e.g. pypi_name.as_normalized() vs a normalized name derived from the input) or using contains_key/get on the PyPI dependency map with an appropriate normalized key type.

Suggested change
.any(|pypi_name| pypi_name.as_source() == name.as_source())
.any(|pypi_name| pypi_name.as_normalized() == name.as_normalized())

Copilot uses AI. Check for mistakes.
Comment thread crates/pixi_cli/src/remove.rs Outdated
Comment thread crates/pixi_cli/src/remove.rs Outdated
Comment on lines +72 to +76
let all_missing_from_conda = specs
.keys()
.all(|name| !conda_deps.contains_key(name));

if all_missing_from_conda {
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

Fallback only triggers when all requested specs are missing from conda (all_missing_from_conda). If the user removes multiple packages and some are conda while others are PyPI-only, the PyPI-only ones will still hit the old confusing behavior. If the intended behavior is “for each spec, if it’s not a conda dep, try PyPI before warning”, this logic will need to be per-package (or split the remove into conda + PyPI subsets).

Copilot uses AI. Check for mistakes.
Comment on lines +68 to +70
let specs = args.dependency_config.specs()?;
let env = workspace.default_environment();
let conda_deps = env.dependencies(spec_type, None);
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

args.dependency_config.specs()? is parsed here for the fallback check and then parsed again in the conda removal branch. Consider parsing once and reusing the specs map to avoid duplicate work and keep the control flow simpler.

Copilot uses AI. Check for mistakes.
Signed-off-by: theycallmeaabie <theycallmeaabie@gmail.com>
Signed-off-by: theycallmeaabie <theycallmeaabie@gmail.com>
Signed-off-by: theycallmeaabie <theycallmeaabie@gmail.com>
@theycallmeaabie
Copy link
Copy Markdown
Author

Hey @ruben-arts, please take a look, lemme know if you would prefer something changed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Confusing behaviour when removing a pypi dependency with a specified version

2 participants