Skip to content

Commit

Permalink
uv-tool: ignore existing environment on interpreter mismatch
Browse files Browse the repository at this point in the history
  • Loading branch information
lucab committed Sep 17, 2024
1 parent c87ce7a commit b26e08f
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 18 deletions.
2 changes: 1 addition & 1 deletion crates/uv-tool/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ impl InstalledTools {
match PythonEnvironment::from_root(&environment_path, cache) {
Ok(venv) => {
debug!(
"Using existing environment for tool `{name}`: {}",
"Found existing environment for tool `{name}`: {}",
environment_path.user_display()
);
Ok(Some(venv))
Expand Down
34 changes: 21 additions & 13 deletions crates/uv/src/commands/tool/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,19 +276,27 @@ pub(crate) async fn install(
installed_tools
.get_environment(&from.name, &cache)?
.filter(|environment| {
python_request.as_ref().map_or(true, |python_request| {
if python_request.satisfied(environment.interpreter(), &cache) {
debug!("Found existing environment for `{from}`", from = from.name.cyan());
true
} else {
let _ = writeln!(
printer.stderr(),
"Existing environment for `{from}` does not satisfy the requested Python interpreter",
from = from.name.cyan(),
);
false
}
})
// NOTE(lucab): this compares `base_prefix` paths as a proxy for
// detecting interpreters mismatches. Directly comparing interpreters
// (by paths or binaries on-disk) would result in several false
// positives on Windows due to file-copying and shims.
let old_base_prefix = environment.interpreter().sys_base_prefix();
let selected_base_prefix = interpreter.sys_base_prefix();
if old_base_prefix == selected_base_prefix {
debug!(
"Found existing interpreter for tool `{}`: {}",
from.name,
environment.interpreter().sys_executable().display()
);
true
} else {
let _ = writeln!(
printer.stderr(),
"Ignored existing environment for `{from}` due to stale Python interpreter",
from = from.name.cyan(),
);
false
}
});

// If the requested and receipt requirements are the same...
Expand Down
74 changes: 70 additions & 4 deletions crates/uv/tests/tool_install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2072,9 +2072,10 @@ fn tool_install_upgrade() {
});
}

/// Test reinstalling tools with varying `--python` requests.
/// Test reinstalling tools with varying `--python` and
/// `--python-preference` parameters.
#[test]
fn tool_install_python_request() {
fn tool_install_python_params() {
let context = TestContext::new_with_versions(&["3.11", "3.12"])
.with_filtered_counts()
.with_filtered_exe_suffix();
Expand Down Expand Up @@ -2122,10 +2123,12 @@ fn tool_install_python_request() {
`black` is already installed
"###);

// Install with Python 3.11 (incompatible).
// Install with system Python 3.11 (different version, incompatible).
uv_snapshot!(context.filters(), context.tool_install()
.arg("-p")
.arg("3.11")
.arg("--python-preference")
.arg("only-system")
.arg("black")
.env("UV_TOOL_DIR", tool_dir.as_os_str())
.env("XDG_BIN_HOME", bin_dir.as_os_str())
Expand All @@ -2135,7 +2138,7 @@ fn tool_install_python_request() {
----- stdout -----
----- stderr -----
Existing environment for `black` does not satisfy the requested Python interpreter
Ignored existing environment for `black` due to stale Python interpreter
Resolved [N] packages in [TIME]
Prepared [N] packages in [TIME]
Installed [N] packages in [TIME]
Expand All @@ -2147,6 +2150,69 @@ fn tool_install_python_request() {
+ platformdirs==4.2.0
Installed 2 executables: black, blackd
"###);

// Install with system Python 3.11 (compatible).
uv_snapshot!(context.filters(), context.tool_install()
.arg("-p")
.arg("3.11")
.arg("--python-preference")
.arg("only-system")
.arg("black")
.env("UV_TOOL_DIR", tool_dir.as_os_str())
.env("XDG_BIN_HOME", bin_dir.as_os_str())
.env("PATH", bin_dir.as_os_str()), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
`black` is already installed
"###);

// Install with managed Python 3.11 (different source, incompatible).
uv_snapshot!(context.filters(), context.tool_install()
.arg("-p")
.arg("3.11")
.arg("--python-preference")
.arg("only-managed")
.arg("black")
.env("UV_TOOL_DIR", tool_dir.as_os_str())
.env("XDG_BIN_HOME", bin_dir.as_os_str())
.env("PATH", bin_dir.as_os_str()), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Ignored existing environment for `black` due to stale Python interpreter
Resolved [N] packages in [TIME]
Installed [N] packages in [TIME]
+ black==24.3.0
+ click==8.1.7
+ mypy-extensions==1.0.0
+ packaging==24.0
+ pathspec==0.12.1
+ platformdirs==4.2.0
Installed 2 executables: black, blackd
"###);

// Install with managed Python 3.11 (compatible).
uv_snapshot!(context.filters(), context.tool_install()
.arg("-p")
.arg("3.11")
.arg("--python-preference")
.arg("only-managed")
.arg("black")
.env("UV_TOOL_DIR", tool_dir.as_os_str())
.env("XDG_BIN_HOME", bin_dir.as_os_str())
.env("PATH", bin_dir.as_os_str()), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
`black` is already installed
"###);
}

/// Test preserving a tool environment when new but incompatible requirements are requested.
Expand Down

0 comments on commit b26e08f

Please sign in to comment.