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 d3c381b commit 1232452
Show file tree
Hide file tree
Showing 3 changed files with 90 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
32 changes: 19 additions & 13 deletions crates/uv/src/commands/tool/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use owo_colors::OwoColorize;
use pep440_rs::{VersionSpecifier, VersionSpecifiers};
use pep508_rs::MarkerTree;
use pypi_types::{Requirement, RequirementSource};
use same_file::is_same_file;
use tracing::debug;
use uv_cache::{Cache, Refresh};
use uv_cache_info::Timestamp;
Expand Down Expand Up @@ -276,19 +277,24 @@ 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
}
})
let old_interpreter = environment.interpreter().sys_executable();
let selected_interpreter = interpreter.sys_executable();
let same_executable = old_interpreter == selected_interpreter
|| is_same_file(old_interpreter, selected_interpreter).unwrap_or(false);
if same_executable {
debug!(
"Found existing interpreter for tool `{}`: {}",
from.name,
old_interpreter.display()
);
} else {
let _ = writeln!(
printer.stderr(),
"Ignored existing environment for `{from}` due to stale Python interpreter",
from = from.name.cyan(),
);
}
same_executable
});

// 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 1232452

Please sign in to comment.