Add `UV_PYTHON_EXTRA_INSTALL_DIRS`

This commit is contained in:
Jo 2025-10-10 16:02:07 +08:00
parent ea5a09215b
commit 4b69fde81b
No known key found for this signature in database
3 changed files with 67 additions and 53 deletions

View File

@ -339,54 +339,61 @@ fn python_executables_from_installed<'a>(
preference: PythonPreference, preference: PythonPreference,
preview: Preview, preview: Preview,
) -> Box<dyn Iterator<Item = Result<(PythonSource, PathBuf), Error>> + 'a> { ) -> Box<dyn Iterator<Item = Result<(PythonSource, PathBuf), Error>> + 'a> {
let from_managed_installations = iter::once_with(move || { let extra_managed_install_dirs = env::var_os(EnvVars::UV_PYTHON_EXTRA_INSTALL_DIRS)
ManagedPythonInstallations::from_settings(None) .into_iter()
.map_err(Error::from) .flat_map(|value| env::split_paths(&value).collect::<Vec<_>>())
.and_then(|installed_installations| { .filter(|path| !path.as_os_str().is_empty());
debug!(
let from_managed_installations = iter::once(None)
.chain(extra_managed_install_dirs.into_iter().map(Some))
.map(move |install_dir| {
ManagedPythonInstallations::from_settings(install_dir)
.map_err(Error::from)
.and_then(|installed_installations| {
debug!(
"Searching for managed installations at `{}`", "Searching for managed installations at `{}`",
installed_installations.root().user_display() installed_installations.root().user_display()
); );
let installations = installed_installations.find_matching_current_platform()?; let installations = installed_installations.find_matching_current_platform()?;
let build_versions = python_build_versions_from_env()?; let build_versions = python_build_versions_from_env()?;
// Check that the Python version and platform satisfy the request to avoid // Check that the Python version and platform satisfy the request to avoid
// unnecessary interpreter queries later // unnecessary interpreter queries later
Ok(installations Ok(installations
.into_iter() .into_iter()
.filter(move |installation| { .filter(move |installation| {
if !version.matches_version(&installation.version()) { if !version.matches_version(&installation.version()) {
debug!("Skipping managed installation `{installation}`: does not satisfy `{version}`"); debug!("Skipping managed installation `{installation}`: does not satisfy `{version}`");
return false; return false;
} }
if !platform.matches(installation.platform()) { if !platform.matches(installation.platform()) {
debug!("Skipping managed installation `{installation}`: does not satisfy requested platform `{platform}`"); debug!("Skipping managed installation `{installation}`: does not satisfy requested platform `{platform}`");
return false;
}
if let Some(requested_build) = build_versions.get(&installation.implementation()) {
let Some(installation_build) = installation.build() else {
debug!(
"Skipping managed installation `{installation}`: a build version was requested but is not recorded for this installation"
);
return false;
};
if installation_build != requested_build {
debug!(
"Skipping managed installation `{installation}`: requested build version `{requested_build}` does not match installation build version `{installation_build}`"
);
return false; return false;
} }
}
true if let Some(requested_build) = build_versions.get(&installation.implementation()) {
}) let Some(installation_build) = installation.build() else {
.inspect(|installation| debug!("Found managed installation `{installation}`")) debug!(
.map(move |installation| { "Skipping managed installation `{installation}`: a build version was requested but is not recorded for this installation"
// If it's not a patch version request, then attempt to read the stable );
// minor version link. return false;
let executable = version };
if installation_build != requested_build {
debug!(
"Skipping managed installation `{installation}`: requested build version `{requested_build}` does not match installation build version `{installation_build}`"
);
return false;
}
}
true
})
.inspect(|installation| debug!("Found managed installation `{installation}`"))
.map(move |installation| {
// If it's not a patch version request, then attempt to read the stable
// minor version link.
let executable = version
.patch() .patch()
.is_none() .is_none()
.then(|| { .then(|| {
@ -394,21 +401,21 @@ fn python_executables_from_installed<'a>(
&installation, &installation,
preview, preview,
) )
.filter(PythonMinorVersionLink::exists) .filter(PythonMinorVersionLink::exists)
.map( .map(
|minor_version_link| { |minor_version_link| {
minor_version_link.symlink_executable.clone() minor_version_link.symlink_executable.clone()
}, },
) )
}) })
.flatten() .flatten()
.unwrap_or_else(|| installation.executable(false)); .unwrap_or_else(|| installation.executable(false));
(PythonSource::Managed, executable) (PythonSource::Managed, executable)
}) })
) )
}) })
}) })
.flatten_ok(); .flatten_ok();
let from_search_path = iter::once_with(move || { let from_search_path = iter::once_with(move || {
python_executables_from_search_path(version, implementation) python_executables_from_search_path(version, implementation)

View File

@ -262,7 +262,7 @@ impl ManagedPythonInstallations {
) -> Result<impl DoubleEndedIterator<Item = ManagedPythonInstallation> + use<>, Error> { ) -> Result<impl DoubleEndedIterator<Item = ManagedPythonInstallation> + use<>, Error> {
let platform = Platform::from_env()?; let platform = Platform::from_env()?;
let iter = Self::from_settings(None)? let iter = self
.find_all()? .find_all()?
.filter(move |installation| { .filter(move |installation| {
if !platform.supports(installation.platform()) { if !platform.supports(installation.platform()) {

View File

@ -371,6 +371,13 @@ impl EnvVars {
#[attr_added_in("0.2.22")] #[attr_added_in("0.2.22")]
pub const UV_PYTHON_INSTALL_DIR: &'static str = "UV_PYTHON_INSTALL_DIR"; pub const UV_PYTHON_INSTALL_DIR: &'static str = "UV_PYTHON_INSTALL_DIR";
/// Specifies additional directories to search for managed Python installations.
///
/// Directories should be separated by the platform-specific path separator, i.e.,
/// `:` on Unix and `;` on Windows.
#[attr_added_in("next release")]
pub const UV_PYTHON_EXTRA_INSTALL_DIRS: &'static str = "UV_PYTHON_EXTRA_INSTALL_DIRS";
/// Whether to install the Python executable into the `UV_PYTHON_BIN_DIR` directory. /// Whether to install the Python executable into the `UV_PYTHON_BIN_DIR` directory.
#[attr_added_in("0.8.0")] #[attr_added_in("0.8.0")]
pub const UV_PYTHON_INSTALL_BIN: &'static str = "UV_PYTHON_INSTALL_BIN"; pub const UV_PYTHON_INSTALL_BIN: &'static str = "UV_PYTHON_INSTALL_BIN";