Support no-build-isolation-package (#5894)

## Summary

Resolves #5831 

## Test Plan

`cargo test`
This commit is contained in:
Ahmed Ilyas 2024-08-08 03:35:56 +02:00 committed by GitHub
parent f7110e0a07
commit acbd367ead
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 468 additions and 16 deletions

View file

@ -434,22 +434,25 @@ impl SourceBuild {
Self::extract_pep517_backend(&source_tree, setup_py, &default_backend)
.map_err(|err| *err)?;
let package_name = project.clone().map(|p| p.name);
// Create a virtual environment, or install into the shared environment if requested.
let venv = match build_isolation {
BuildIsolation::Isolated => uv_virtualenv::create_venv(
let venv = if let Some(venv) = build_isolation.shared_environment(package_name.as_ref()) {
venv.clone()
} else {
uv_virtualenv::create_venv(
temp_dir.path(),
interpreter.clone(),
uv_virtualenv::Prompt::None,
false,
false,
false,
)?,
BuildIsolation::Shared(venv) => venv.clone(),
)?
};
// Setup the build environment. If build isolation is disabled, we assume the build
// environment is already setup.
if build_isolation.is_isolated() {
if build_isolation.is_isolated(package_name.as_ref()) {
let resolved_requirements = Self::get_resolved_requirements(
build_context,
source_build_context,
@ -499,7 +502,7 @@ impl SourceBuild {
// Create the PEP 517 build environment. If build isolation is disabled, we assume the build
// environment is already setup.
let runner = PythonRunner::new(concurrent_builds);
if build_isolation.is_isolated() {
if build_isolation.is_isolated(package_name.as_ref()) {
if let Some(pep517_backend) = &pep517_backend {
create_pep517_build_environment(
&runner,

View file

@ -3106,6 +3106,12 @@ pub struct ResolverArgs {
)]
pub no_build_isolation: bool,
/// Disable isolation when building source distributions for a specific package.
///
/// Assumes that the packages' build dependencies specified by PEP 518 are already installed.
#[arg(long, help_heading = "Build options")]
pub no_build_isolation_package: Vec<PackageName>,
#[arg(
long,
overrides_with("no_build_isolation"),
@ -3271,6 +3277,12 @@ pub struct ResolverInstallerArgs {
)]
pub no_build_isolation: bool,
/// Disable isolation when building source distributions for a specific package.
///
/// Assumes that the packages' build dependencies specified by PEP 518 are already installed.
#[arg(long, help_heading = "Build options")]
pub no_build_isolation_package: Vec<PackageName>,
#[arg(
long,
overrides_with("no_build_isolation"),

View file

@ -43,6 +43,7 @@ impl From<ResolverArgs> for PipOptions {
pre,
config_setting,
no_build_isolation,
no_build_isolation_package,
build_isolation,
exclude_newer,
link_mode,
@ -63,6 +64,7 @@ impl From<ResolverArgs> for PipOptions {
config_settings: config_setting
.map(|config_settings| config_settings.into_iter().collect::<ConfigSettings>()),
no_build_isolation: flag(no_build_isolation, build_isolation),
no_build_isolation_package: Some(no_build_isolation_package),
exclude_newer,
link_mode,
no_sources: if no_sources { Some(true) } else { None },
@ -124,6 +126,7 @@ impl From<ResolverInstallerArgs> for PipOptions {
pre,
config_setting,
no_build_isolation,
no_build_isolation_package,
build_isolation,
exclude_newer,
link_mode,
@ -148,6 +151,7 @@ impl From<ResolverInstallerArgs> for PipOptions {
config_settings: config_setting
.map(|config_settings| config_settings.into_iter().collect::<ConfigSettings>()),
no_build_isolation: flag(no_build_isolation, build_isolation),
no_build_isolation_package: Some(no_build_isolation_package),
exclude_newer,
link_mode,
compile_bytecode: flag(compile_bytecode, no_compile_bytecode),
@ -195,6 +199,7 @@ pub fn resolver_options(resolver_args: ResolverArgs, build_args: BuildArgs) -> R
pre,
config_setting,
no_build_isolation,
no_build_isolation_package,
build_isolation,
exclude_newer,
link_mode,
@ -237,6 +242,7 @@ pub fn resolver_options(resolver_args: ResolverArgs, build_args: BuildArgs) -> R
config_settings: config_setting
.map(|config_settings| config_settings.into_iter().collect::<ConfigSettings>()),
no_build_isolation: flag(no_build_isolation, build_isolation),
no_build_isolation_package: Some(no_build_isolation_package),
exclude_newer,
link_mode,
no_build: flag(no_build, build),
@ -267,6 +273,7 @@ pub fn resolver_installer_options(
pre,
config_setting,
no_build_isolation,
no_build_isolation_package,
build_isolation,
exclude_newer,
link_mode,
@ -313,6 +320,7 @@ pub fn resolver_installer_options(
config_settings: config_setting
.map(|config_settings| config_settings.into_iter().collect::<ConfigSettings>()),
no_build_isolation: flag(no_build_isolation, build_isolation),
no_build_isolation_package: Some(no_build_isolation_package),
exclude_newer,
link_mode,
compile_bytecode: flag(compile_bytecode, no_compile_bytecode),

View file

@ -205,6 +205,7 @@ pub struct ResolverOptions {
pub no_binary: Option<bool>,
pub no_binary_package: Option<Vec<PackageName>>,
pub no_build_isolation: Option<bool>,
pub no_build_isolation_package: Option<Vec<PackageName>>,
pub no_sources: Option<bool>,
}
@ -350,6 +351,18 @@ pub struct ResolverInstallerOptions {
"#
)]
pub no_build_isolation: Option<bool>,
/// Disable isolation when building source distributions for a specific package.
///
/// Assumes that the packages' build dependencies specified by [PEP 518](https://peps.python.org/pep-0518/)
/// are already installed.
#[option(
default = "[]",
value_type = "Vec<PackageName>",
example = r#"
no-build-isolation-package = ["package1", "package2"]
"#
)]
pub no_build_isolation_package: Option<Vec<PackageName>>,
/// Limit candidate packages to those that were uploaded prior to the given date.
///
/// Accepts both [RFC 3339](https://www.rfc-editor.org/rfc/rfc3339.html) timestamps (e.g.,
@ -717,6 +730,18 @@ pub struct PipOptions {
"#
)]
pub no_build_isolation: Option<bool>,
/// Disable isolation when building source distributions for a specific package.
///
/// Assumes that the packages' build dependencies specified by [PEP 518](https://peps.python.org/pep-0518/)
/// are already installed.
#[option(
default = "[]",
value_type = "Vec<PackageName>",
example = r#"
no-build-isolation-package = ["package1", "package2"]
"#
)]
pub no_build_isolation_package: Option<Vec<PackageName>>,
/// Validate the Python environment, to detect packages with missing dependencies and other
/// issues.
#[option(

View file

@ -1,3 +1,4 @@
use pep508_rs::PackageName;
use uv_python::PythonEnvironment;
/// Whether to enforce build isolation when building source distributions.
@ -6,11 +7,33 @@ pub enum BuildIsolation<'a> {
#[default]
Isolated,
Shared(&'a PythonEnvironment),
SharedPackage(&'a PythonEnvironment, &'a [PackageName]),
}
impl<'a> BuildIsolation<'a> {
/// Returns `true` if build isolation is enforced.
pub fn is_isolated(&self) -> bool {
matches!(self, Self::Isolated)
/// Returns `true` if build isolation is enforced for the given package name.
pub fn is_isolated(&self, package: Option<&PackageName>) -> bool {
match self {
Self::Isolated => true,
Self::Shared(_) => false,
Self::SharedPackage(_, packages) => {
package.map_or(true, |package| !packages.iter().any(|p| p == package))
}
}
}
/// Returns the shared environment for a given package, if build isolation is not enforced.
pub fn shared_environment(&self, package: Option<&PackageName>) -> Option<&PythonEnvironment> {
match self {
Self::Isolated => None,
Self::Shared(env) => Some(env),
Self::SharedPackage(env, packages) => {
if package.is_some_and(|package| packages.iter().any(|p| p == package)) {
Some(env)
} else {
None
}
}
}
}
}

View file

@ -76,6 +76,7 @@ pub(crate) async fn pip_compile(
config_settings: ConfigSettings,
connectivity: Connectivity,
no_build_isolation: bool,
no_build_isolation_package: Vec<PackageName>,
build_options: BuildOptions,
python_version: Option<PythonVersion>,
python_platform: Option<TargetTriple>,
@ -303,8 +304,11 @@ pub(crate) async fn pip_compile(
let build_isolation = if no_build_isolation {
environment = PythonEnvironment::from_interpreter(interpreter.clone());
BuildIsolation::Shared(&environment)
} else {
} else if no_build_isolation_package.is_empty() {
BuildIsolation::Isolated
} else {
environment = PythonEnvironment::from_interpreter(interpreter.clone());
BuildIsolation::SharedPackage(&environment, &no_build_isolation_package)
};
let build_dispatch = BuildDispatch::new(

View file

@ -3,6 +3,7 @@ use std::fmt::Write;
use anstream::eprint;
use itertools::Itertools;
use owo_colors::OwoColorize;
use pep508_rs::PackageName;
use tracing::{debug, enabled, Level};
use distribution_types::{IndexLocations, Resolution, UnresolvedRequirementSpecification};
@ -59,6 +60,7 @@ pub(crate) async fn pip_install(
connectivity: Connectivity,
config_settings: &ConfigSettings,
no_build_isolation: bool,
no_build_isolation_package: Vec<PackageName>,
build_options: BuildOptions,
python_version: Option<PythonVersion>,
python_platform: Option<TargetTriple>,
@ -289,8 +291,10 @@ pub(crate) async fn pip_install(
// Determine whether to enable build isolation.
let build_isolation = if no_build_isolation {
BuildIsolation::Shared(&environment)
} else {
} else if no_build_isolation_package.is_empty() {
BuildIsolation::Isolated
} else {
BuildIsolation::SharedPackage(&environment, &no_build_isolation_package)
};
// Initialize any shared state.

View file

@ -3,6 +3,7 @@ use std::fmt::Write;
use anstream::eprint;
use anyhow::Result;
use owo_colors::OwoColorize;
use pep508_rs::PackageName;
use tracing::debug;
use distribution_types::{IndexLocations, Resolution};
@ -51,6 +52,7 @@ pub(crate) async fn pip_sync(
connectivity: Connectivity,
config_settings: &ConfigSettings,
no_build_isolation: bool,
no_build_isolation_package: Vec<PackageName>,
build_options: BuildOptions,
python_version: Option<PythonVersion>,
python_platform: Option<TargetTriple>,
@ -229,8 +231,10 @@ pub(crate) async fn pip_sync(
// Determine whether to enable build isolation.
let build_isolation = if no_build_isolation {
BuildIsolation::Shared(&environment)
} else {
} else if no_build_isolation_package.is_empty() {
BuildIsolation::Isolated
} else {
BuildIsolation::SharedPackage(&environment, &no_build_isolation_package)
};
// Initialize any shared state.

View file

@ -229,6 +229,7 @@ async fn do_lock(
prerelease,
config_setting,
no_build_isolation,
no_build_isolation_package,
exclude_newer,
link_mode,
upgrade,
@ -298,8 +299,11 @@ async fn do_lock(
let build_isolation = if no_build_isolation {
environment = PythonEnvironment::from_interpreter(interpreter.clone());
BuildIsolation::Shared(&environment)
} else {
} else if no_build_isolation_package.is_empty() {
BuildIsolation::Isolated
} else {
environment = PythonEnvironment::from_interpreter(interpreter.clone());
BuildIsolation::SharedPackage(&environment, no_build_isolation_package)
};
let options = OptionsBuilder::new()

View file

@ -406,6 +406,7 @@ pub(crate) async fn resolve_names(
prerelease: _,
config_setting,
no_build_isolation,
no_build_isolation_package,
exclude_newer,
link_mode,
compile_bytecode: _,
@ -436,8 +437,11 @@ pub(crate) async fn resolve_names(
let build_isolation = if *no_build_isolation {
environment = PythonEnvironment::from_interpreter(interpreter.clone());
BuildIsolation::Shared(&environment)
} else {
} else if no_build_isolation_package.is_empty() {
BuildIsolation::Isolated
} else {
environment = PythonEnvironment::from_interpreter(interpreter.clone());
BuildIsolation::SharedPackage(&environment, no_build_isolation_package)
};
// TODO(charlie): These are all default values. We should consider whether we want to make them
@ -505,6 +509,7 @@ pub(crate) async fn resolve_environment<'a>(
prerelease,
config_setting,
no_build_isolation,
no_build_isolation_package,
exclude_newer,
link_mode,
upgrade: _,
@ -548,8 +553,11 @@ pub(crate) async fn resolve_environment<'a>(
let build_isolation = if no_build_isolation {
environment = PythonEnvironment::from_interpreter(interpreter.clone());
BuildIsolation::Shared(&environment)
} else {
} else if no_build_isolation_package.is_empty() {
BuildIsolation::Isolated
} else {
environment = PythonEnvironment::from_interpreter(interpreter.clone());
BuildIsolation::SharedPackage(&environment, no_build_isolation_package)
};
let options = OptionsBuilder::new()
@ -780,6 +788,7 @@ pub(crate) async fn update_environment(
prerelease,
config_setting,
no_build_isolation,
no_build_isolation_package,
exclude_newer,
link_mode,
compile_bytecode,
@ -848,8 +857,10 @@ pub(crate) async fn update_environment(
// Determine whether to enable build isolation.
let build_isolation = if *no_build_isolation {
BuildIsolation::Shared(&venv)
} else {
} else if no_build_isolation_package.is_empty() {
BuildIsolation::Isolated
} else {
BuildIsolation::SharedPackage(&venv, no_build_isolation_package)
};
let options = OptionsBuilder::new()

View file

@ -280,6 +280,7 @@ async fn run(cli: Cli) -> Result<ExitStatus> {
args.settings.config_setting,
globals.connectivity,
args.settings.no_build_isolation,
args.settings.no_build_isolation_package,
args.settings.build_options,
args.settings.python_version,
args.settings.python_platform,
@ -351,6 +352,7 @@ async fn run(cli: Cli) -> Result<ExitStatus> {
globals.connectivity,
&args.settings.config_setting,
args.settings.no_build_isolation,
args.settings.no_build_isolation_package,
args.settings.build_options,
args.settings.python_version,
args.settings.python_platform,
@ -441,6 +443,7 @@ async fn run(cli: Cli) -> Result<ExitStatus> {
globals.connectivity,
&args.settings.config_setting,
args.settings.no_build_isolation,
args.settings.no_build_isolation_package,
args.settings.build_options,
args.settings.python_version,
args.settings.python_platform,

View file

@ -1531,6 +1531,7 @@ pub(crate) struct ResolverSettings {
pub(crate) prerelease: PrereleaseMode,
pub(crate) config_setting: ConfigSettings,
pub(crate) no_build_isolation: bool,
pub(crate) no_build_isolation_package: Vec<PackageName>,
pub(crate) exclude_newer: Option<ExcludeNewer>,
pub(crate) link_mode: LinkMode,
pub(crate) upgrade: Upgrade,
@ -1547,6 +1548,7 @@ pub(crate) struct ResolverSettingsRef<'a> {
pub(crate) prerelease: PrereleaseMode,
pub(crate) config_setting: &'a ConfigSettings,
pub(crate) no_build_isolation: bool,
pub(crate) no_build_isolation_package: &'a [PackageName],
pub(crate) exclude_newer: Option<ExcludeNewer>,
pub(crate) link_mode: LinkMode,
pub(crate) upgrade: &'a Upgrade,
@ -1568,6 +1570,7 @@ impl ResolverSettings {
prerelease,
config_settings,
no_build_isolation,
no_build_isolation_package,
exclude_newer,
link_mode,
compile_bytecode: _,
@ -1612,6 +1615,10 @@ impl ResolverSettings {
.no_build_isolation
.combine(no_build_isolation)
.unwrap_or_default(),
no_build_isolation_package: args
.no_build_isolation_package
.combine(no_build_isolation_package)
.unwrap_or_default(),
exclude_newer: args.exclude_newer.combine(exclude_newer),
link_mode: args.link_mode.combine(link_mode).unwrap_or_default(),
upgrade: Upgrade::from_args(
@ -1652,6 +1659,7 @@ impl ResolverSettings {
prerelease: self.prerelease,
config_setting: &self.config_setting,
no_build_isolation: self.no_build_isolation,
no_build_isolation_package: &self.no_build_isolation_package,
exclude_newer: self.exclude_newer,
link_mode: self.link_mode,
upgrade: &self.upgrade,
@ -1676,6 +1684,7 @@ pub(crate) struct ResolverInstallerSettings {
pub(crate) prerelease: PrereleaseMode,
pub(crate) config_setting: ConfigSettings,
pub(crate) no_build_isolation: bool,
pub(crate) no_build_isolation_package: Vec<PackageName>,
pub(crate) exclude_newer: Option<ExcludeNewer>,
pub(crate) link_mode: LinkMode,
pub(crate) compile_bytecode: bool,
@ -1694,6 +1703,7 @@ pub(crate) struct ResolverInstallerSettingsRef<'a> {
pub(crate) prerelease: PrereleaseMode,
pub(crate) config_setting: &'a ConfigSettings,
pub(crate) no_build_isolation: bool,
pub(crate) no_build_isolation_package: &'a [PackageName],
pub(crate) exclude_newer: Option<ExcludeNewer>,
pub(crate) link_mode: LinkMode,
pub(crate) compile_bytecode: bool,
@ -1720,6 +1730,7 @@ impl ResolverInstallerSettings {
prerelease,
config_settings,
no_build_isolation,
no_build_isolation_package,
exclude_newer,
link_mode,
compile_bytecode,
@ -1764,6 +1775,10 @@ impl ResolverInstallerSettings {
.no_build_isolation
.combine(no_build_isolation)
.unwrap_or_default(),
no_build_isolation_package: args
.no_build_isolation_package
.combine(no_build_isolation_package)
.unwrap_or_default(),
exclude_newer: args.exclude_newer.combine(exclude_newer),
link_mode: args.link_mode.combine(link_mode).unwrap_or_default(),
sources: SourceStrategy::from_args(
@ -1814,6 +1829,7 @@ impl ResolverInstallerSettings {
prerelease: self.prerelease,
config_setting: &self.config_setting,
no_build_isolation: self.no_build_isolation,
no_build_isolation_package: &self.no_build_isolation_package,
exclude_newer: self.exclude_newer,
link_mode: self.link_mode,
compile_bytecode: self.compile_bytecode,
@ -1842,6 +1858,7 @@ pub(crate) struct PipSettings {
pub(crate) index_strategy: IndexStrategy,
pub(crate) keyring_provider: KeyringProviderType,
pub(crate) no_build_isolation: bool,
pub(crate) no_build_isolation_package: Vec<PackageName>,
pub(crate) build_options: BuildOptions,
pub(crate) allow_empty_requirements: bool,
pub(crate) strict: bool,
@ -1900,6 +1917,7 @@ impl PipSettings {
no_binary,
only_binary,
no_build_isolation,
no_build_isolation_package,
strict,
extra,
all_extras,
@ -1952,6 +1970,7 @@ impl PipSettings {
prerelease: top_level_prerelease,
config_settings: top_level_config_settings,
no_build_isolation: top_level_no_build_isolation,
no_build_isolation_package: top_level_no_build_isolation_package,
exclude_newer: top_level_exclude_newer,
link_mode: top_level_link_mode,
compile_bytecode: top_level_compile_bytecode,
@ -1980,6 +1999,8 @@ impl PipSettings {
let prerelease = prerelease.combine(top_level_prerelease);
let config_settings = config_settings.combine(top_level_config_settings);
let no_build_isolation = no_build_isolation.combine(top_level_no_build_isolation);
let no_build_isolation_package =
no_build_isolation_package.combine(top_level_no_build_isolation_package);
let exclude_newer = exclude_newer.combine(top_level_exclude_newer);
let link_mode = link_mode.combine(top_level_link_mode);
let compile_bytecode = compile_bytecode.combine(top_level_compile_bytecode);
@ -2054,6 +2075,10 @@ impl PipSettings {
.no_build_isolation
.combine(no_build_isolation)
.unwrap_or_default(),
no_build_isolation_package: args
.no_build_isolation_package
.combine(no_build_isolation_package)
.unwrap_or_default(),
config_setting: args
.config_settings
.combine(config_settings)
@ -2172,6 +2197,7 @@ impl<'a> From<ResolverInstallerSettingsRef<'a>> for ResolverSettingsRef<'a> {
prerelease: settings.prerelease,
config_setting: settings.config_setting,
no_build_isolation: settings.no_build_isolation,
no_build_isolation_package: settings.no_build_isolation_package,
exclude_newer: settings.exclude_newer,
link_mode: settings.link_mode,
upgrade: settings.upgrade,

View file

@ -6324,3 +6324,96 @@ fn compatible_build_constraint() -> Result<()> {
Ok(())
}
#[test]
fn install_build_isolation_package() -> Result<()> {
let context = TestContext::new("3.12");
// Create an package.
let package = context.temp_dir.child("project");
package.create_dir_all()?;
let pyproject_toml = package.child("pyproject.toml");
pyproject_toml.write_str(
r#"
[project]
name = "project"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
"iniconfig @ https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz",
]
[build-system]
requires = [
"setuptools >= 40.9.0",
]
build-backend = "setuptools.build_meta"
"#,
)?;
// Running `uv pip install` should fail for iniconfig.
let filters = std::iter::once((r"exit code: 1", "exit status: 1"))
.chain(context.filters())
.collect::<Vec<_>>();
uv_snapshot!(filters, context.pip_install()
.arg("--no-build-isolation-package")
.arg("iniconfig")
.arg(package.path()), @r###"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
error: Failed to download and build: `iniconfig @ https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz`
Caused by: Failed to build: `iniconfig @ https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz`
Caused by: Build backend failed to determine metadata through `prepare_metadata_for_build_wheel` with exit status: 1
--- stdout:
--- stderr:
Traceback (most recent call last):
File "<string>", line 8, in <module>
ModuleNotFoundError: No module named 'hatchling'
---
"###
);
// Install `hatchinling`, `hatch-vs` for iniconfig
uv_snapshot!(context.filters(), context.pip_install().arg("hatchling").arg("hatch-vcs"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 9 packages in [TIME]
Prepared 9 packages in [TIME]
Installed 9 packages in [TIME]
+ hatch-vcs==0.4.0
+ hatchling==1.22.4
+ packaging==24.0
+ pathspec==0.12.1
+ pluggy==1.4.0
+ setuptools==69.2.0
+ setuptools-scm==8.0.4
+ trove-classifiers==2024.3.3
+ typing-extensions==4.10.0
"###);
// Running `uv pip install` should succeed.
uv_snapshot!(context.filters(), context.pip_install()
.arg("--no-build-isolation-package")
.arg("iniconfig")
.arg(package.path()), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 2 packages in [TIME]
Prepared 2 packages in [TIME]
Installed 2 packages in [TIME]
+ iniconfig==2.0.0 (from https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz)
+ project==0.1.0 (from file://[TEMP_DIR]/project)
"###);
Ok(())
}

View file

@ -122,6 +122,7 @@ fn resolve_uv_toml() -> anyhow::Result<()> {
index_strategy: FirstIndex,
keyring_provider: Disabled,
no_build_isolation: false,
no_build_isolation_package: [],
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -257,6 +258,7 @@ fn resolve_uv_toml() -> anyhow::Result<()> {
index_strategy: FirstIndex,
keyring_provider: Disabled,
no_build_isolation: false,
no_build_isolation_package: [],
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -393,6 +395,7 @@ fn resolve_uv_toml() -> anyhow::Result<()> {
index_strategy: FirstIndex,
keyring_provider: Disabled,
no_build_isolation: false,
no_build_isolation_package: [],
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -561,6 +564,7 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> {
index_strategy: FirstIndex,
keyring_provider: Disabled,
no_build_isolation: false,
no_build_isolation_package: [],
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -675,6 +679,7 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> {
index_strategy: FirstIndex,
keyring_provider: Disabled,
no_build_isolation: false,
no_build_isolation_package: [],
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -821,6 +826,7 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> {
index_strategy: FirstIndex,
keyring_provider: Disabled,
no_build_isolation: false,
no_build_isolation_package: [],
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -1004,6 +1010,7 @@ fn resolve_index_url() -> anyhow::Result<()> {
index_strategy: FirstIndex,
keyring_provider: Disabled,
no_build_isolation: false,
no_build_isolation_package: [],
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -1186,6 +1193,7 @@ fn resolve_index_url() -> anyhow::Result<()> {
index_strategy: FirstIndex,
keyring_provider: Disabled,
no_build_isolation: false,
no_build_isolation_package: [],
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -1346,6 +1354,7 @@ fn resolve_find_links() -> anyhow::Result<()> {
index_strategy: FirstIndex,
keyring_provider: Disabled,
no_build_isolation: false,
no_build_isolation_package: [],
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -1482,6 +1491,7 @@ fn resolve_top_level() -> anyhow::Result<()> {
index_strategy: FirstIndex,
keyring_provider: Disabled,
no_build_isolation: false,
no_build_isolation_package: [],
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -1656,6 +1666,7 @@ fn resolve_top_level() -> anyhow::Result<()> {
index_strategy: FirstIndex,
keyring_provider: Disabled,
no_build_isolation: false,
no_build_isolation_package: [],
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -1813,6 +1824,7 @@ fn resolve_top_level() -> anyhow::Result<()> {
index_strategy: FirstIndex,
keyring_provider: Disabled,
no_build_isolation: false,
no_build_isolation_package: [],
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -1949,6 +1961,7 @@ fn resolve_user_configuration() -> anyhow::Result<()> {
index_strategy: FirstIndex,
keyring_provider: Disabled,
no_build_isolation: false,
no_build_isolation_package: [],
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -2068,6 +2081,7 @@ fn resolve_user_configuration() -> anyhow::Result<()> {
index_strategy: FirstIndex,
keyring_provider: Disabled,
no_build_isolation: false,
no_build_isolation_package: [],
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -2187,6 +2201,7 @@ fn resolve_user_configuration() -> anyhow::Result<()> {
index_strategy: FirstIndex,
keyring_provider: Disabled,
no_build_isolation: false,
no_build_isolation_package: [],
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -2308,6 +2323,7 @@ fn resolve_user_configuration() -> anyhow::Result<()> {
index_strategy: FirstIndex,
keyring_provider: Disabled,
no_build_isolation: false,
no_build_isolation_package: [],
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -2454,6 +2470,7 @@ fn resolve_poetry_toml() -> anyhow::Result<()> {
index_strategy: FirstIndex,
keyring_provider: Disabled,
no_build_isolation: false,
no_build_isolation_package: [],
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -2624,6 +2641,7 @@ fn resolve_both() -> anyhow::Result<()> {
index_strategy: FirstIndex,
keyring_provider: Disabled,
no_build_isolation: false,
no_build_isolation_package: [],
build_options: BuildOptions {
no_binary: None,
no_build: None,
@ -2786,6 +2804,7 @@ fn resolve_config_file() -> anyhow::Result<()> {
index_strategy: FirstIndex,
keyring_provider: Disabled,
no_build_isolation: false,
no_build_isolation_package: [],
build_options: BuildOptions {
no_binary: None,
no_build: None,

View file

@ -581,3 +581,99 @@ fn sync_reset_state() -> Result<()> {
Ok(())
}
#[test]
fn sync_build_isolation_package() -> Result<()> {
let context = TestContext::new("3.12");
let pyproject_toml = context.temp_dir.child("pyproject.toml");
pyproject_toml.write_str(
r#"
[project]
name = "project"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
"iniconfig @ https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz",
]
[build-system]
requires = [
"setuptools >= 40.9.0",
]
build-backend = "setuptools.build_meta"
"#,
)?;
// Running `uv sync` should fail for iniconfig.
let filters = std::iter::once((r"exit code: 1", "exit status: 1"))
.chain(context.filters())
.collect::<Vec<_>>();
uv_snapshot!(filters, context.sync().arg("--no-build-isolation-package").arg("iniconfig"), @r###"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
warning: `uv sync` is experimental and may change without warning
error: Failed to download and build: `iniconfig @ https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz`
Caused by: Failed to build: `iniconfig @ https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz`
Caused by: Build backend failed to determine metadata through `prepare_metadata_for_build_wheel` with exit status: 1
--- stdout:
--- stderr:
Traceback (most recent call last):
File "<string>", line 8, in <module>
ModuleNotFoundError: No module named 'hatchling'
---
"###);
// Install `hatchinling`, `hatch-vs` for iniconfig
uv_snapshot!(context.filters(), context.pip_install().arg("hatchling").arg("hatch-vcs"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 9 packages in [TIME]
Prepared 9 packages in [TIME]
Installed 9 packages in [TIME]
+ hatch-vcs==0.4.0
+ hatchling==1.22.4
+ packaging==24.0
+ pathspec==0.12.1
+ pluggy==1.4.0
+ setuptools==69.2.0
+ setuptools-scm==8.0.4
+ trove-classifiers==2024.3.3
+ typing-extensions==4.10.0
"###);
// Running `uv sync` should succeed.
uv_snapshot!(context.filters(), context.sync().arg("--no-build-isolation-package").arg("iniconfig"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
warning: `uv sync` is experimental and may change without warning
Resolved 2 packages in [TIME]
Prepared 2 packages in [TIME]
Uninstalled 9 packages in [TIME]
Installed 2 packages in [TIME]
- hatch-vcs==0.4.0
- hatchling==1.22.4
+ iniconfig==2.0.0 (from https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz)
- packaging==24.0
- pathspec==0.12.1
- pluggy==1.4.0
+ project==0.1.0 (from file://[TEMP_DIR]/)
- setuptools==69.2.0
- setuptools-scm==8.0.4
- trove-classifiers==2024.3.3
- typing-extensions==4.10.0
"###);
assert!(context.temp_dir.child("uv.lock").exists());
Ok(())
}

View file

@ -162,6 +162,10 @@ uv run [OPTIONS] <COMMAND>
</ul>
</dd><dt><code>--config-setting</code>, <code>-C</code> <i>config-setting</i></dt><dd><p>Settings to pass to the PEP 517 build backend, specified as <code>KEY=VALUE</code> pairs</p>
</dd><dt><code>--no-build-isolation-package</code> <i>no-build-isolation-package</i></dt><dd><p>Disable isolation when building source distributions for a specific package.</p>
<p>Assumes that the packages&#8217; build dependencies specified by PEP 518 are already installed.</p>
</dd><dt><code>--exclude-newer</code> <i>exclude-newer</i></dt><dd><p>Limit candidate packages to those that were uploaded prior to the given date.</p>
<p>Accepts both RFC 3339 timestamps (e.g., <code>2006-12-02T02:07:43Z</code>) and UTC dates in the same format (e.g., <code>2006-12-02</code>).</p>
@ -435,6 +439,10 @@ uv add [OPTIONS] <REQUIREMENTS>...
</ul>
</dd><dt><code>--config-setting</code>, <code>-C</code> <i>config-setting</i></dt><dd><p>Settings to pass to the PEP 517 build backend, specified as <code>KEY=VALUE</code> pairs</p>
</dd><dt><code>--no-build-isolation-package</code> <i>no-build-isolation-package</i></dt><dd><p>Disable isolation when building source distributions for a specific package.</p>
<p>Assumes that the packages&#8217; build dependencies specified by PEP 518 are already installed.</p>
</dd><dt><code>--exclude-newer</code> <i>exclude-newer</i></dt><dd><p>Limit candidate packages to those that were uploaded prior to the given date.</p>
<p>Accepts both RFC 3339 timestamps (e.g., <code>2006-12-02T02:07:43Z</code>) and UTC dates in the same format (e.g., <code>2006-12-02</code>).</p>
@ -612,6 +620,10 @@ uv remove [OPTIONS] <REQUIREMENTS>...
</ul>
</dd><dt><code>--config-setting</code>, <code>-C</code> <i>config-setting</i></dt><dd><p>Settings to pass to the PEP 517 build backend, specified as <code>KEY=VALUE</code> pairs</p>
</dd><dt><code>--no-build-isolation-package</code> <i>no-build-isolation-package</i></dt><dd><p>Disable isolation when building source distributions for a specific package.</p>
<p>Assumes that the packages&#8217; build dependencies specified by PEP 518 are already installed.</p>
</dd><dt><code>--exclude-newer</code> <i>exclude-newer</i></dt><dd><p>Limit candidate packages to those that were uploaded prior to the given date.</p>
<p>Accepts both RFC 3339 timestamps (e.g., <code>2006-12-02T02:07:43Z</code>) and UTC dates in the same format (e.g., <code>2006-12-02</code>).</p>
@ -785,6 +797,10 @@ uv sync [OPTIONS]
</ul>
</dd><dt><code>--config-setting</code>, <code>-C</code> <i>config-setting</i></dt><dd><p>Settings to pass to the PEP 517 build backend, specified as <code>KEY=VALUE</code> pairs</p>
</dd><dt><code>--no-build-isolation-package</code> <i>no-build-isolation-package</i></dt><dd><p>Disable isolation when building source distributions for a specific package.</p>
<p>Assumes that the packages&#8217; build dependencies specified by PEP 518 are already installed.</p>
</dd><dt><code>--exclude-newer</code> <i>exclude-newer</i></dt><dd><p>Limit candidate packages to those that were uploaded prior to the given date.</p>
<p>Accepts both RFC 3339 timestamps (e.g., <code>2006-12-02T02:07:43Z</code>) and UTC dates in the same format (e.g., <code>2006-12-02</code>).</p>
@ -956,6 +972,10 @@ uv lock [OPTIONS]
</ul>
</dd><dt><code>--config-setting</code>, <code>-C</code> <i>config-setting</i></dt><dd><p>Settings to pass to the PEP 517 build backend, specified as <code>KEY=VALUE</code> pairs</p>
</dd><dt><code>--no-build-isolation-package</code> <i>no-build-isolation-package</i></dt><dd><p>Disable isolation when building source distributions for a specific package.</p>
<p>Assumes that the packages&#8217; build dependencies specified by PEP 518 are already installed.</p>
</dd><dt><code>--exclude-newer</code> <i>exclude-newer</i></dt><dd><p>Limit candidate packages to those that were uploaded prior to the given date.</p>
<p>Accepts both RFC 3339 timestamps (e.g., <code>2006-12-02T02:07:43Z</code>) and UTC dates in the same format (e.g., <code>2006-12-02</code>).</p>
@ -1138,6 +1158,10 @@ uv tree [OPTIONS]
</ul>
</dd><dt><code>--config-setting</code>, <code>-C</code> <i>config-setting</i></dt><dd><p>Settings to pass to the PEP 517 build backend, specified as <code>KEY=VALUE</code> pairs</p>
</dd><dt><code>--no-build-isolation-package</code> <i>no-build-isolation-package</i></dt><dd><p>Disable isolation when building source distributions for a specific package.</p>
<p>Assumes that the packages&#8217; build dependencies specified by PEP 518 are already installed.</p>
</dd><dt><code>--exclude-newer</code> <i>exclude-newer</i></dt><dd><p>Limit candidate packages to those that were uploaded prior to the given date.</p>
<p>Accepts both RFC 3339 timestamps (e.g., <code>2006-12-02T02:07:43Z</code>) and UTC dates in the same format (e.g., <code>2006-12-02</code>).</p>
@ -1384,6 +1408,10 @@ uv tool run [OPTIONS] [COMMAND]
</ul>
</dd><dt><code>--config-setting</code>, <code>-C</code> <i>config-setting</i></dt><dd><p>Settings to pass to the PEP 517 build backend, specified as <code>KEY=VALUE</code> pairs</p>
</dd><dt><code>--no-build-isolation-package</code> <i>no-build-isolation-package</i></dt><dd><p>Disable isolation when building source distributions for a specific package.</p>
<p>Assumes that the packages&#8217; build dependencies specified by PEP 518 are already installed.</p>
</dd><dt><code>--exclude-newer</code> <i>exclude-newer</i></dt><dd><p>Limit candidate packages to those that were uploaded prior to the given date.</p>
<p>Accepts both RFC 3339 timestamps (e.g., <code>2006-12-02T02:07:43Z</code>) and UTC dates in the same format (e.g., <code>2006-12-02</code>).</p>
@ -1561,6 +1589,10 @@ uv tool install [OPTIONS] <PACKAGE>
</ul>
</dd><dt><code>--config-setting</code>, <code>-C</code> <i>config-setting</i></dt><dd><p>Settings to pass to the PEP 517 build backend, specified as <code>KEY=VALUE</code> pairs</p>
</dd><dt><code>--no-build-isolation-package</code> <i>no-build-isolation-package</i></dt><dd><p>Disable isolation when building source distributions for a specific package.</p>
<p>Assumes that the packages&#8217; build dependencies specified by PEP 518 are already installed.</p>
</dd><dt><code>--exclude-newer</code> <i>exclude-newer</i></dt><dd><p>Limit candidate packages to those that were uploaded prior to the given date.</p>
<p>Accepts both RFC 3339 timestamps (e.g., <code>2006-12-02T02:07:43Z</code>) and UTC dates in the same format (e.g., <code>2006-12-02</code>).</p>
@ -2475,6 +2507,10 @@ uv pip compile [OPTIONS] <SRC_FILE>...
</ul>
</dd><dt><code>--config-setting</code>, <code>-C</code> <i>config-setting</i></dt><dd><p>Settings to pass to the PEP 517 build backend, specified as <code>KEY=VALUE</code> pairs</p>
</dd><dt><code>--no-build-isolation-package</code> <i>no-build-isolation-package</i></dt><dd><p>Disable isolation when building source distributions for a specific package.</p>
<p>Assumes that the packages&#8217; build dependencies specified by PEP 518 are already installed.</p>
</dd><dt><code>--exclude-newer</code> <i>exclude-newer</i></dt><dd><p>Limit candidate packages to those that were uploaded prior to the given date.</p>
<p>Accepts both RFC 3339 timestamps (e.g., <code>2006-12-02T02:07:43Z</code>) and UTC dates in the same format (e.g., <code>2006-12-02</code>).</p>
@ -2976,6 +3012,10 @@ uv pip install [OPTIONS] <PACKAGE|--requirement <REQUIREMENT>|--editable <EDITAB
</ul>
</dd><dt><code>--config-setting</code>, <code>-C</code> <i>config-setting</i></dt><dd><p>Settings to pass to the PEP 517 build backend, specified as <code>KEY=VALUE</code> pairs</p>
</dd><dt><code>--no-build-isolation-package</code> <i>no-build-isolation-package</i></dt><dd><p>Disable isolation when building source distributions for a specific package.</p>
<p>Assumes that the packages&#8217; build dependencies specified by PEP 518 are already installed.</p>
</dd><dt><code>--exclude-newer</code> <i>exclude-newer</i></dt><dd><p>Limit candidate packages to those that were uploaded prior to the given date.</p>
<p>Accepts both RFC 3339 timestamps (e.g., <code>2006-12-02T02:07:43Z</code>) and UTC dates in the same format (e.g., <code>2006-12-02</code>).</p>

View file

@ -476,6 +476,34 @@ are already installed.
---
#### [`no-build-isolation-package`](#no-build-isolation-package) {: #no-build-isolation-package }
Disable isolation when building source distributions for a specific package.
Assumes that the packages' build dependencies specified by [PEP 518](https://peps.python.org/pep-0518/)
are already installed.
**Default value**: `[]`
**Type**: `Vec<PackageName>`
**Example usage**:
=== "pyproject.toml"
```toml
[tool.uv]
no-build-isolation-package = ["package1", "package2"]
```
=== "uv.toml"
```toml
no-build-isolation-package = ["package1", "package2"]
```
---
#### [`no-build-package`](#no-build-package) {: #no-build-package }
Don't build source distributions for a specific package.
@ -1721,6 +1749,35 @@ are already installed.
---
#### [`no-build-isolation-package`](#pip_no-build-isolation-package) {: #pip_no-build-isolation-package }
<span id="no-build-isolation-package"></span>
Disable isolation when building source distributions for a specific package.
Assumes that the packages' build dependencies specified by [PEP 518](https://peps.python.org/pep-0518/)
are already installed.
**Default value**: `[]`
**Type**: `Vec<PackageName>`
**Example usage**:
=== "pyproject.toml"
```toml
[tool.uv.pip]
no-build-isolation-package = ["package1", "package2"]
```
=== "uv.toml"
```toml
[pip]
no-build-isolation-package = ["package1", "package2"]
```
---
#### [`no-deps`](#pip_no-deps) {: #pip_no-deps }
<span id="no-deps"></span>

20
uv.schema.json generated
View file

@ -168,6 +168,16 @@
"null"
]
},
"no-build-isolation-package": {
"description": "Disable isolation when building source distributions for a specific package.\n\nAssumes that the packages' build dependencies specified by [PEP 518](https://peps.python.org/pep-0518/) are already installed.",
"type": [
"array",
"null"
],
"items": {
"$ref": "#/definitions/PackageName"
}
},
"no-build-package": {
"description": "Don't build source distributions for a specific package.",
"type": [
@ -750,6 +760,16 @@
"null"
]
},
"no-build-isolation-package": {
"description": "Disable isolation when building source distributions for a specific package.\n\nAssumes that the packages' build dependencies specified by [PEP 518](https://peps.python.org/pep-0518/) are already installed.",
"type": [
"array",
"null"
],
"items": {
"$ref": "#/definitions/PackageName"
}
},
"no-deps": {
"description": "Ignore package dependencies, instead only add those packages explicitly listed on the command line to the resulting the requirements file.",
"type": [