Add support for disabling provider isolation with `UV_NO_PROVIDER_ISOLATION`

This commit is contained in:
konstin 2025-10-14 11:55:07 +02:00 committed by Charlie Marsh
parent 7e69f62093
commit 80db77d7c2
2 changed files with 67 additions and 19 deletions

View File

@ -1175,4 +1175,15 @@ impl EnvVars {
/// around invalid artifacts in rare cases.
#[attr_added_in("0.8.23")]
pub const UV_SKIP_WHEEL_FILENAME_CHECK: &'static str = "UV_SKIP_WHEEL_FILENAME_CHECK";
/// A comma separated list of variant provider backends that use the current environment instead
/// of an isolated environment.
///
/// The requirements need to be installed in the current environment, no provider `requires`
/// will be installed. This option is intended for development and as an escape hatch where
/// isolation fails.
///
/// Example: `UV_NO_PROVIDER_ISOLATION=gpu.provider:api,cpu,blas.backend`
#[attr_added_in("0.9.2")]
pub const UV_NO_PROVIDER_ISOLATION: &'static str = "UV_NO_PROVIDER_ISOLATION";
}

View File

@ -2,6 +2,7 @@
mod error;
use std::borrow::Cow;
use std::ffi::OsString;
use std::fmt::Write;
use std::io;
@ -68,6 +69,36 @@ impl VariantBuild {
) -> Result<Self, Error> {
let temp_dir = build_context.cache().venv_dir()?;
// TODO(konsti): This is not the right location to parse the env var.
let plugin_api = Self::plugin_api(&backend_name, backend, interpreter)?;
let no_isolation =
env::var(EnvVars::UV_NO_PROVIDER_ISOLATION).is_ok_and(|no_provider_isolation| {
no_provider_isolation
.split(",")
.any(|api| (api) == plugin_api)
});
// TODO(konsti): Integrate this properly with the configuration system.
if no_isolation {
debug!("Querying provider plugin without isolation: {backend_name}");
let runner = PythonRunner::new(concurrent_builds, level);
let env = PythonEnvironment::from_interpreter(interpreter.clone());
// The unmodified path
let modified_path = OsString::from(env.scripts());
return Ok(Self {
temp_dir,
backend_name,
backend: backend.clone(),
venv: env,
level,
modified_path,
environment_variables,
runner,
});
}
// Create a virtual environment.
let venv = uv_virtualenv::create_venv(
temp_dir.path(),
@ -149,36 +180,42 @@ impl VariantBuild {
})
}
pub fn import(&self) -> Result<String, Error> {
let import = if let Some(plugin_api) = &self.backend.plugin_api {
if let Some((path, object)) = plugin_api.split_once(':') {
format!("from {path} import {object} as backend")
} else {
format!("import {plugin_api} as backend")
}
// Not a method to be callable in the constructor.
pub fn plugin_api<'a>(
backend_name: &str,
backend: &'a Provider,
interpreter: &Interpreter,
) -> Result<Cow<'a, str>, Error> {
if let Some(plugin_api) = &backend.plugin_api {
Ok(Cow::Borrowed(plugin_api))
} else {
let requires = self
.backend
let requires = backend
.requires
.as_ref()
.ok_or_else(|| Error::MissingRequires(self.backend_name.clone()))?
.ok_or_else(|| Error::MissingRequires(backend_name.to_string()))?
.iter()
.filter(|requirement| {
requirement.evaluate_markers(
self.venv.interpreter().markers(),
&Vec::new().as_slice(),
&[],
)
requirement.evaluate_markers(interpreter.markers(), &Vec::new().as_slice(), &[])
})
.collect::<Vec<_>>();
if let [requires] = requires.as_slice() {
format!("import {} as backend", requires.name.as_dist_info_name())
Ok(requires.name.as_dist_info_name())
} else {
return Err(Error::InvalidRequires {
backend_name: self.backend_name.clone(),
Err(Error::InvalidRequires {
backend_name: backend_name.to_string(),
matching: requires.len(),
});
})
}
}
}
pub fn import(&self) -> Result<String, Error> {
let plugin_api =
Self::plugin_api(&self.backend_name, &self.backend, self.venv.interpreter())?;
let import = if let Some((path, object)) = plugin_api.split_once(':') {
format!("from {path} import {object} as backend")
} else {
format!("import {plugin_api} as backend")
};
Ok(formatdoc! {r#"