mirror of
https://github.com/astral-sh/uv.git
synced 2025-07-07 21:35:00 +00:00
Reinstall and recreate environments when interpreter is removed (#4935)
## Summary We now recreate the environment in `uv sync`, `uv tool install`, and `uv tool run` if the underlying interpreter has been removed. Closes https://github.com/astral-sh/uv/issues/4933.
This commit is contained in:
parent
53db63f6dd
commit
bb703b8343
4 changed files with 34 additions and 14 deletions
|
@ -11,7 +11,7 @@ use std::str::FromStr;
|
||||||
|
|
||||||
use fs_err::File;
|
use fs_err::File;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tracing::debug;
|
use tracing::{debug, warn};
|
||||||
|
|
||||||
use install_wheel_rs::read_record_file;
|
use install_wheel_rs::read_record_file;
|
||||||
|
|
||||||
|
@ -178,6 +178,9 @@ impl InstalledTools {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the [`PythonEnvironment`] for a given tool, if it exists.
|
/// Return the [`PythonEnvironment`] for a given tool, if it exists.
|
||||||
|
///
|
||||||
|
/// Returns `Ok(None)` if the environment does not exist or is linked to a non-existent
|
||||||
|
/// interpreter.
|
||||||
pub fn get_environment(
|
pub fn get_environment(
|
||||||
&self,
|
&self,
|
||||||
name: &PackageName,
|
name: &PackageName,
|
||||||
|
@ -186,14 +189,25 @@ impl InstalledTools {
|
||||||
let _lock = self.acquire_lock();
|
let _lock = self.acquire_lock();
|
||||||
let environment_path = self.root.join(name.to_string());
|
let environment_path = self.root.join(name.to_string());
|
||||||
|
|
||||||
if environment_path.is_dir() {
|
match PythonEnvironment::from_root(&environment_path, cache) {
|
||||||
debug!(
|
Ok(venv) => {
|
||||||
"Using existing environment for tool `{name}` at `{}`.",
|
debug!(
|
||||||
environment_path.user_display()
|
"Using existing environment for tool `{name}`: {}",
|
||||||
);
|
environment_path.user_display()
|
||||||
Ok(Some(PythonEnvironment::from_root(environment_path, cache)?))
|
);
|
||||||
} else {
|
Ok(Some(venv))
|
||||||
Ok(None)
|
}
|
||||||
|
Err(uv_python::Error::MissingEnvironment(_)) => Ok(None),
|
||||||
|
Err(uv_python::Error::Query(uv_python::InterpreterError::NotFound(
|
||||||
|
interpreter_path,
|
||||||
|
))) => {
|
||||||
|
warn!(
|
||||||
|
"Ignoring existing virtual environment linked to non-existent Python interpreter: {}",
|
||||||
|
interpreter_path.user_display()
|
||||||
|
);
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
Err(err) => Err(err.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,7 +224,7 @@ impl InstalledTools {
|
||||||
match fs_err::remove_dir_all(&environment_path) {
|
match fs_err::remove_dir_all(&environment_path) {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
debug!(
|
debug!(
|
||||||
"Removed existing environment for tool `{name}` at `{}`.",
|
"Removed existing environment for tool `{name}`: {}",
|
||||||
environment_path.user_display()
|
environment_path.user_display()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -219,7 +233,7 @@ impl InstalledTools {
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
"Creating environment for tool `{name}` at `{}`.",
|
"Creating environment for tool `{name}`: {}",
|
||||||
environment_path.user_display()
|
environment_path.user_display()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ use uv_python::{
|
||||||
use uv_requirements::{NamedRequirementsResolver, RequirementsSpecification};
|
use uv_requirements::{NamedRequirementsResolver, RequirementsSpecification};
|
||||||
use uv_resolver::{FlatIndex, OptionsBuilder, PythonRequirement, RequiresPython, ResolutionGraph};
|
use uv_resolver::{FlatIndex, OptionsBuilder, PythonRequirement, RequiresPython, ResolutionGraph};
|
||||||
use uv_types::{BuildIsolation, EmptyInstalledPackages, HashStrategy};
|
use uv_types::{BuildIsolation, EmptyInstalledPackages, HashStrategy};
|
||||||
|
use uv_warnings::warn_user;
|
||||||
|
|
||||||
use crate::commands::pip::operations::Modifications;
|
use crate::commands::pip::operations::Modifications;
|
||||||
use crate::commands::reporters::{PythonDownloadReporter, ResolverReporter};
|
use crate::commands::reporters::{PythonDownloadReporter, ResolverReporter};
|
||||||
|
@ -174,6 +175,12 @@ impl FoundInterpreter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(uv_python::Error::MissingEnvironment(_)) => {}
|
Err(uv_python::Error::MissingEnvironment(_)) => {}
|
||||||
|
Err(uv_python::Error::Query(uv_python::InterpreterError::NotFound(path))) => {
|
||||||
|
warn_user!(
|
||||||
|
"Ignoring existing virtual environment linked to non-existent Python interpreter: {}",
|
||||||
|
path.user_display().cyan()
|
||||||
|
);
|
||||||
|
}
|
||||||
Err(err) => return Err(err.into()),
|
Err(err) => return Err(err.into()),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -166,9 +166,8 @@ pub(crate) async fn install(
|
||||||
} else {
|
} else {
|
||||||
let _ = writeln!(
|
let _ = writeln!(
|
||||||
printer.stderr(),
|
printer.stderr(),
|
||||||
"Existing environment for `{}` does not satisfy the requested Python interpreter (`{}`)",
|
"Existing environment for `{}` does not satisfy the requested Python interpreter",
|
||||||
from.name,
|
from.name,
|
||||||
python_request
|
|
||||||
);
|
);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
|
@ -1393,7 +1393,7 @@ fn tool_install_python_request() {
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
warning: `uv tool install` is experimental and may change without warning.
|
warning: `uv tool install` is experimental and may change without warning.
|
||||||
Existing environment for `black` does not satisfy the requested Python interpreter (`Python 3.11`)
|
Existing environment for `black` does not satisfy the requested Python interpreter
|
||||||
Resolved [N] packages in [TIME]
|
Resolved [N] packages in [TIME]
|
||||||
Prepared [N] packages in [TIME]
|
Prepared [N] packages in [TIME]
|
||||||
Installed [N] packages in [TIME]
|
Installed [N] packages in [TIME]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue