Skip to content

Commit

Permalink
Replace executables with broken symlinks during uv python install (#…
Browse files Browse the repository at this point in the history
…9706)

I somehow got in a state where we'd fail to install with

```
error: Failed to install cpython-3.13.0-macos-aarch64-none
  Caused by: Executable already exists at `/Users/zb/.local/bin/python3` but is not managed by uv; use `--force` to replace it
error: Failed to install cpython-3.13.0-macos-aarch64-none
  Caused by: Executable already exists at `/Users/zb/.local/bin/python` but is not managed by uv; use `--force` to replace it
```

but `python` / `python3` _were_ managed by uv, they just were linked to
an installation that was deleted.

This updates the logic to replace broken executables that are broken
symlinks. We apply this to broken links regardless of whether or not we
think the target is managed by uv.
  • Loading branch information
zanieb authored Dec 10, 2024
1 parent 57a7f04 commit 5894161
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 20 deletions.
58 changes: 38 additions & 20 deletions crates/uv/src/commands/python/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -358,32 +358,50 @@ pub(crate) async fn install(
target.simplified_display()
);

// Check if the existing link is valid
let valid_link = target
.read_link()
.and_then(|target| target.try_exists())
.inspect_err(|err| debug!("Failed to inspect executable with error: {err}"))
.unwrap_or(true);

// Figure out what installation it references, if any
let existing = find_matching_bin_link(
installations
.iter()
.copied()
.chain(existing_installations.iter()),
&target,
);
let existing = valid_link
.then(|| {
find_matching_bin_link(
installations
.iter()
.copied()
.chain(existing_installations.iter()),
&target,
)
})
.flatten();

match existing {
None => {
// There's an existing executable we don't manage, require `--force`
if !force {
errors.push((
installation.key(),
anyhow::anyhow!(
"Executable already exists at `{}` but is not managed by uv; use `--force` to replace it",
to.simplified_display()
),
));
continue;
if valid_link {
if !force {
errors.push((
installation.key(),
anyhow::anyhow!(
"Executable already exists at `{}` but is not managed by uv; use `--force` to replace it",
to.simplified_display()
),
));
continue;
}
debug!(
"Replacing existing executable at `{}` due to `--force`",
target.simplified_display()
);
} else {
debug!(
"Replacing broken symlink at `{}`",
target.simplified_display()
);
}
debug!(
"Replacing existing executable at `{}` due to `--force`",
target.simplified_display()
);
}
Some(existing) if existing == *installation => {
// The existing link points to the same installation, so we're done unless
Expand Down
37 changes: 37 additions & 0 deletions crates/uv/tests/it/python_install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -839,3 +839,40 @@ fn python_install_unknown() {
error: `./foo` is not a valid Python download request; see `uv python help` for supported formats and `uv python list --only-downloads` for available versions
"###);
}

#[cfg(unix)]
#[test]
fn python_install_preview_broken_link() {
use assert_fs::prelude::PathCreateDir;
use fs_err::os::unix::fs::symlink;

let context: TestContext = TestContext::new_with_versions(&[])
.with_filtered_python_keys()
.with_filtered_exe_suffix();

let bin_python = context.temp_dir.child("bin").child("python3.13");

// Create a broken symlink
context.temp_dir.child("bin").create_dir_all().unwrap();
symlink(context.temp_dir.join("does-not-exist"), &bin_python).unwrap();

// Install
uv_snapshot!(context.filters(), context.python_install().arg("--preview").arg("3.13"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Installed Python 3.13.1 in [TIME]
+ cpython-3.13.1-[PLATFORM] (python3.13)
"###);

// We should replace the broken symlink
insta::with_settings!({
filters => context.filters(),
}, {
insta::assert_snapshot!(
read_link_path(&bin_python), @"[TEMP_DIR]/managed/cpython-3.13.1-[PLATFORM]/bin/python3.13"
);
});
}

0 comments on commit 5894161

Please sign in to comment.