mirror of https://github.com/astral-sh/uv
Detect cases where uv partially removed a virtual environment and retry
This commit is contained in:
parent
081e2010df
commit
89edcbefd3
|
|
@ -920,7 +920,9 @@ impl ProjectInterpreter {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
InvalidEnvironmentKind::MissingExecutable(_) => {
|
InvalidEnvironmentKind::MissingExecutable(_) => {
|
||||||
if fs_err::read_dir(&root).is_ok_and(|mut dir| dir.next().is_some()) {
|
if fs_err::read_dir(&root).is_ok_and(|mut dir| dir.next().is_some())
|
||||||
|
&& !root.join(".uv-partial-rm").try_exists().unwrap_or(false)
|
||||||
|
{
|
||||||
return Err(ProjectError::InvalidProjectEnvironmentDir(
|
return Err(ProjectError::InvalidProjectEnvironmentDir(
|
||||||
root,
|
root,
|
||||||
"it is not a valid Python environment (no Python executable was found)"
|
"it is not a valid Python environment (no Python executable was found)"
|
||||||
|
|
@ -1294,6 +1296,9 @@ impl ProjectEnvironment {
|
||||||
// Unless it's empty, in which case we just ignore it
|
// Unless it's empty, in which case we just ignore it
|
||||||
if root.read_dir().is_ok_and(|mut dir| dir.next().is_none()) {
|
if root.read_dir().is_ok_and(|mut dir| dir.next().is_none()) {
|
||||||
false
|
false
|
||||||
|
// Or, if there's a marker file indicating a previous removal failed
|
||||||
|
} else if root.join(".uv-partial-rm").try_exists().unwrap_or(false) {
|
||||||
|
true
|
||||||
} else {
|
} else {
|
||||||
return Err(ProjectError::InvalidProjectEnvironmentDir(
|
return Err(ProjectError::InvalidProjectEnvironmentDir(
|
||||||
root,
|
root,
|
||||||
|
|
@ -1363,7 +1368,23 @@ impl ProjectEnvironment {
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {}
|
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {}
|
||||||
Err(e) => return Err(e.into()),
|
Err(e) => {
|
||||||
|
// Try to write a marker to indicate that we failed to remove the entire
|
||||||
|
// environment, so when the next command runs we can attempt to remove
|
||||||
|
// it even if a `pyvenv.cfg` marker was removed
|
||||||
|
match fs_err::write(root.join(".uv-partial-rm"), "") {
|
||||||
|
Ok(()) => {
|
||||||
|
debug!(
|
||||||
|
"Wrote `.uv-partial-rm` marker to partially deleted environment: {}",
|
||||||
|
root.user_display().cyan()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
debug!("Failed to write `.uv-partial-rm` marker: {err}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Err(e.into());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5000,6 +5000,59 @@ fn sync_update_project() -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sync_no_pyvenv_cfg() -> Result<()> {
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
|
||||||
|
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||||
|
pyproject_toml.write_str(
|
||||||
|
r#"
|
||||||
|
[project]
|
||||||
|
name = "my-project"
|
||||||
|
version = "0.1.0"
|
||||||
|
requires-python = ">=3.12"
|
||||||
|
dependencies = ["iniconfig"]
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Break the virtual environment
|
||||||
|
fs_err::remove_dir_all(
|
||||||
|
context
|
||||||
|
.venv
|
||||||
|
.join(if cfg!(windows) { "Scripts" } else { "bin" }),
|
||||||
|
)?;
|
||||||
|
fs_err::remove_file(context.venv.join("pyvenv.cfg"))?;
|
||||||
|
|
||||||
|
// Running `uv sync` won't create the venv
|
||||||
|
uv_snapshot!(context.filters(), context.sync(), @r"
|
||||||
|
success: false
|
||||||
|
exit_code: 2
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
error: Project virtual environment directory `[VENV]/` cannot be used because it is not a valid Python environment (no Python executable was found)
|
||||||
|
");
|
||||||
|
|
||||||
|
// Write the marker that indicates the environment was partially removed by uv
|
||||||
|
fs_err::write(context.venv.join(".uv-partial-rm"), "")?;
|
||||||
|
uv_snapshot!(context.filters(), context.sync(), @r"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Using CPython 3.12.[X] interpreter at: [PYTHON-3.12]
|
||||||
|
Removed virtual environment at: .venv
|
||||||
|
Creating virtual environment at: .venv
|
||||||
|
Resolved 2 packages in [TIME]
|
||||||
|
Prepared 1 package in [TIME]
|
||||||
|
Installed 1 package in [TIME]
|
||||||
|
+ iniconfig==2.0.0
|
||||||
|
");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sync_environment_prompt() -> Result<()> {
|
fn sync_environment_prompt() -> Result<()> {
|
||||||
let context = TestContext::new_with_versions(&["3.12"]);
|
let context = TestContext::new_with_versions(&["3.12"]);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue