fix: Add a hint on `uv pip install` failure if the `--system` flag is used to select an externally managed interpreter (#16318)

Hello,

# Summary
This PR makes the error message clearer when you try to install packages
into an externally managed Python environment with the `--system` flag.
Now, instead of just failing, the error explains that you're hitting
this because you explicitly used `--system`.

This closes #15639.

# Test plan

- I added a new integration test (`install_with_system_interpreter` in
`pip_install.rs`) that checks the error message includes the hint.
- I tried to run `uv pip install --system -r requirements.txt` to see
the actual error message in action, but had permission issues.
This commit is contained in:
Charles-Meldhine Madi Mnemoi 2025-10-21 09:46:39 +02:00 committed by GitHub
parent ed65c2482c
commit 29cec24d5c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 59 additions and 6 deletions

View File

@ -236,18 +236,30 @@ pub(crate) async fn pip_install(
if break_system_packages {
debug!("Ignoring externally managed environment due to `--break-system-packages`");
} else {
return if let Some(error) = externally_managed.into_error() {
Err(anyhow::anyhow!(
let base_message = match externally_managed.into_error() {
Some(error) => format!(
"The interpreter at {} is externally managed, and indicates the following:\n\n{}\n\nConsider creating a virtual environment with `uv venv`.",
environment.root().user_display().cyan(),
textwrap::indent(&error, " ").green(),
))
} else {
Err(anyhow::anyhow!(
),
None => format!(
"The interpreter at {} is externally managed. Instead, create a virtual environment with `uv venv`.",
environment.root().user_display().cyan()
))
),
};
let error_message = if system {
format!(
"{}\n{}{} This happens because the `--system` flag was used, which selected the system Python interpreter.",
base_message,
"hint".bold().cyan(),
":".bold()
)
} else {
base_message
};
return Err(anyhow::Error::msg(error_message));
}
}

View File

@ -12990,3 +12990,44 @@ fn pip_install_no_sources_editable_to_registry_switch() -> Result<()> {
Ok(())
}
#[test]
fn install_with_system_interpreter() -> Result<()> {
let context = TestContext::new("3.12");
// A simple requirements.txt file with pytest
let requirements_txt = context.temp_dir.child("requirements.txt");
requirements_txt.write_str("pytest")?;
// Add a custom filter to replace the system Python path
// works on Unix-like systems, Windows and for CPython, PyPy and GraalPy implementations
let filters: Vec<_> = context
.filters()
.into_iter()
.chain(std::iter::once((
r"(?:[A-Za-z]:)?([\\/]).+([\\/])python([\\/])(?:cpython|pypy|graalpy)-\d+\.\d+\.\[X\][^\s]+",
"[PYTHON-PATH]",
)))
.collect();
uv_snapshot!(filters, context.pip_install()
.arg("--system")
.arg("-r")
.arg("requirements.txt"), @r###"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
Using Python 3.12.[X] environment at: [PYTHON-PATH]
error: The interpreter at [PYTHON-PATH] is externally managed, and indicates the following:
This Python installation is managed by uv and should not be modified.
Consider creating a virtual environment with `uv venv`.
hint: This happens because the `--system` flag was used, which selected the system Python interpreter.
"###
);
Ok(())
}