mirror of https://github.com/astral-sh/uv
Avoid un-strict syncing by-default for build isolation (#6606)
## Summary Closes https://github.com/astral-sh/uv/issues/6580. Closes https://github.com/astral-sh/uv/issues/6599.
This commit is contained in:
parent
a7850d2a1c
commit
023acbe4b0
|
|
@ -2261,10 +2261,7 @@ pub struct SyncArgs {
|
||||||
/// Do not remove extraneous packages present in the environment.
|
/// Do not remove extraneous packages present in the environment.
|
||||||
///
|
///
|
||||||
/// When enabled, uv will make the minimum necessary changes to satisfy the requirements.
|
/// When enabled, uv will make the minimum necessary changes to satisfy the requirements.
|
||||||
///
|
/// By default, syncing will remove any extraneous packages from the environment
|
||||||
/// By default, syncing will remove any extraneous packages from the environment, unless
|
|
||||||
/// `--no-build-isolation` is enabled, in which case extra packages are considered necessary for
|
|
||||||
/// builds.
|
|
||||||
#[arg(long, overrides_with("exact"), alias = "no-exact")]
|
#[arg(long, overrides_with("exact"), alias = "no-exact")]
|
||||||
pub inexact: bool,
|
pub inexact: bool,
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -621,6 +621,7 @@ impl PythonPinSettings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The resolved settings to use for a `sync` invocation.
|
/// The resolved settings to use for a `sync` invocation.
|
||||||
#[allow(clippy::struct_excessive_bools, dead_code)]
|
#[allow(clippy::struct_excessive_bools, dead_code)]
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
@ -668,16 +669,6 @@ impl SyncSettings {
|
||||||
filesystem,
|
filesystem,
|
||||||
);
|
);
|
||||||
|
|
||||||
let exact = flag(exact, inexact).unwrap_or(true);
|
|
||||||
|
|
||||||
// By default, sync with exact semantics, unless the user set `--no-build-isolation`;
|
|
||||||
// otherwise, we'll end up removing build dependencies.
|
|
||||||
let modifications = if !exact || settings.no_build_isolation {
|
|
||||||
Modifications::Sufficient
|
|
||||||
} else {
|
|
||||||
Modifications::Exact
|
|
||||||
};
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
locked,
|
locked,
|
||||||
frozen,
|
frozen,
|
||||||
|
|
@ -689,7 +680,11 @@ impl SyncSettings {
|
||||||
no_install_project,
|
no_install_project,
|
||||||
no_install_workspace,
|
no_install_workspace,
|
||||||
no_install_package,
|
no_install_package,
|
||||||
modifications,
|
modifications: if flag(exact, inexact).unwrap_or(true) {
|
||||||
|
Modifications::Exact
|
||||||
|
} else {
|
||||||
|
Modifications::Sufficient
|
||||||
|
},
|
||||||
package,
|
package,
|
||||||
python,
|
python,
|
||||||
refresh: Refresh::from(refresh),
|
refresh: Refresh::from(refresh),
|
||||||
|
|
|
||||||
|
|
@ -428,6 +428,7 @@ fn virtual_workspace_dev_dependencies() -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Use a `pip install` step to pre-install build dependencies for `--no-build-isolation`.
|
||||||
#[test]
|
#[test]
|
||||||
fn sync_build_isolation() -> Result<()> {
|
fn sync_build_isolation() -> Result<()> {
|
||||||
let context = TestContext::new("3.12");
|
let context = TestContext::new("3.12");
|
||||||
|
|
@ -479,9 +480,17 @@ fn sync_build_isolation() -> Result<()> {
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
Resolved 2 packages in [TIME]
|
Resolved 2 packages in [TIME]
|
||||||
Prepared 2 packages in [TIME]
|
Prepared 2 packages in [TIME]
|
||||||
|
Uninstalled 7 packages in [TIME]
|
||||||
Installed 2 packages in [TIME]
|
Installed 2 packages in [TIME]
|
||||||
|
- hatchling==1.22.4
|
||||||
|
- packaging==24.0
|
||||||
|
- pathspec==0.12.1
|
||||||
|
- pluggy==1.4.0
|
||||||
+ project==0.1.0 (from file://[TEMP_DIR]/)
|
+ project==0.1.0 (from file://[TEMP_DIR]/)
|
||||||
|
- setuptools==69.2.0
|
||||||
+ source-distribution==0.0.1 (from https://files.pythonhosted.org/packages/10/1f/57aa4cce1b1abf6b433106676e15f9fa2c92ed2bd4cf77c3b50a9e9ac773/source_distribution-0.0.1.tar.gz)
|
+ source-distribution==0.0.1 (from https://files.pythonhosted.org/packages/10/1f/57aa4cce1b1abf6b433106676e15f9fa2c92ed2bd4cf77c3b50a9e9ac773/source_distribution-0.0.1.tar.gz)
|
||||||
|
- trove-classifiers==2024.3.3
|
||||||
|
- wheel==0.43.0
|
||||||
"###);
|
"###);
|
||||||
|
|
||||||
assert!(context.temp_dir.child("uv.lock").exists());
|
assert!(context.temp_dir.child("uv.lock").exists());
|
||||||
|
|
@ -489,6 +498,7 @@ fn sync_build_isolation() -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Use a `pip install` step to pre-install build dependencies for `--no-build-isolation-package`.
|
||||||
#[test]
|
#[test]
|
||||||
fn sync_build_isolation_package() -> Result<()> {
|
fn sync_build_isolation_package() -> Result<()> {
|
||||||
let context = TestContext::new("3.12");
|
let context = TestContext::new("3.12");
|
||||||
|
|
@ -575,6 +585,118 @@ fn sync_build_isolation_package() -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Use dedicated extra groups to install dependencies for `--no-build-isolation-package`.
|
||||||
|
#[test]
|
||||||
|
fn sync_build_isolation_extra() -> 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 = []
|
||||||
|
|
||||||
|
[project.optional-dependencies]
|
||||||
|
build = ["hatchling"]
|
||||||
|
compile = ["source-distribution @ https://files.pythonhosted.org/packages/10/1f/57aa4cce1b1abf6b433106676e15f9fa2c92ed2bd4cf77c3b50a9e9ac773/source_distribution-0.0.1.tar.gz"]
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["setuptools >= 40.9.0"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
|
[tool.uv]
|
||||||
|
no-build-isolation-package = ["source-distribution"]
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Running `uv sync` should fail for the `compile` extra.
|
||||||
|
let filters = std::iter::once((r"exit code: 1", "exit status: 1"))
|
||||||
|
.chain(context.filters())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
uv_snapshot!(&filters, context.sync().arg("--extra").arg("compile"), @r###"
|
||||||
|
success: false
|
||||||
|
exit_code: 2
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Resolved 7 packages in [TIME]
|
||||||
|
error: Failed to prepare distributions
|
||||||
|
Caused by: Failed to fetch wheel: source-distribution @ https://files.pythonhosted.org/packages/10/1f/57aa4cce1b1abf6b433106676e15f9fa2c92ed2bd4cf77c3b50a9e9ac773/source_distribution-0.0.1.tar.gz
|
||||||
|
Caused by: Build backend failed to build wheel through `build_wheel()` with exit status: 1
|
||||||
|
--- stdout:
|
||||||
|
|
||||||
|
--- stderr:
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "<string>", line 8, in <module>
|
||||||
|
ModuleNotFoundError: No module named 'hatchling'
|
||||||
|
---
|
||||||
|
"###);
|
||||||
|
|
||||||
|
// Running `uv sync` with `--all-extras` should also fail.
|
||||||
|
uv_snapshot!(&filters, context.sync().arg("--all-extras"), @r###"
|
||||||
|
success: false
|
||||||
|
exit_code: 2
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Resolved 7 packages in [TIME]
|
||||||
|
error: Failed to prepare distributions
|
||||||
|
Caused by: Failed to fetch wheel: source-distribution @ https://files.pythonhosted.org/packages/10/1f/57aa4cce1b1abf6b433106676e15f9fa2c92ed2bd4cf77c3b50a9e9ac773/source_distribution-0.0.1.tar.gz
|
||||||
|
Caused by: Build backend failed to build wheel through `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 the build dependencies.
|
||||||
|
uv_snapshot!(context.filters(), context.sync().arg("--extra").arg("build"), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Resolved 7 packages in [TIME]
|
||||||
|
Prepared 6 packages in [TIME]
|
||||||
|
Installed 6 packages in [TIME]
|
||||||
|
+ hatchling==1.22.4
|
||||||
|
+ packaging==24.0
|
||||||
|
+ pathspec==0.12.1
|
||||||
|
+ pluggy==1.4.0
|
||||||
|
+ project==0.1.0 (from file://[TEMP_DIR]/)
|
||||||
|
+ trove-classifiers==2024.3.3
|
||||||
|
"###);
|
||||||
|
|
||||||
|
// Running `uv sync` for the `compile` extra should succeed, and remove the build dependencies.
|
||||||
|
uv_snapshot!(context.filters(), context.sync().arg("--extra").arg("compile"), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Resolved 7 packages in [TIME]
|
||||||
|
Prepared 1 package in [TIME]
|
||||||
|
Uninstalled 5 packages in [TIME]
|
||||||
|
Installed 1 package in [TIME]
|
||||||
|
- hatchling==1.22.4
|
||||||
|
- packaging==24.0
|
||||||
|
- pathspec==0.12.1
|
||||||
|
- pluggy==1.4.0
|
||||||
|
+ source-distribution==0.0.1 (from https://files.pythonhosted.org/packages/10/1f/57aa4cce1b1abf6b433106676e15f9fa2c92ed2bd4cf77c3b50a9e9ac773/source_distribution-0.0.1.tar.gz)
|
||||||
|
- trove-classifiers==2024.3.3
|
||||||
|
"###);
|
||||||
|
|
||||||
|
assert!(context.temp_dir.child("uv.lock").exists());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Avoid using incompatible versions for build dependencies that are also part of the resolved
|
/// Avoid using incompatible versions for build dependencies that are also part of the resolved
|
||||||
/// environment. This is a very subtle issue, but: when locking, we don't enforce platform
|
/// environment. This is a very subtle issue, but: when locking, we don't enforce platform
|
||||||
/// compatibility. So, if we reuse the resolver state to install, and the install itself has to
|
/// compatibility. So, if we reuse the resolver state to install, and the install itself has to
|
||||||
|
|
|
||||||
|
|
@ -1098,9 +1098,7 @@ uv sync [OPTIONS]
|
||||||
|
|
||||||
</dd><dt><code>--inexact</code></dt><dd><p>Do not remove extraneous packages present in the environment.</p>
|
</dd><dt><code>--inexact</code></dt><dd><p>Do not remove extraneous packages present in the environment.</p>
|
||||||
|
|
||||||
<p>When enabled, uv will make the minimum necessary changes to satisfy the requirements.</p>
|
<p>When enabled, uv will make the minimum necessary changes to satisfy the requirements. By default, syncing will remove any extraneous packages from the environment</p>
|
||||||
|
|
||||||
<p>By default, syncing will remove any extraneous packages from the environment, unless <code>--no-build-isolation</code> is enabled, in which case extra packages are considered necessary for builds.</p>
|
|
||||||
|
|
||||||
</dd><dt><code>--keyring-provider</code> <i>keyring-provider</i></dt><dd><p>Attempt to use <code>keyring</code> for authentication for index URLs.</p>
|
</dd><dt><code>--keyring-provider</code> <i>keyring-provider</i></dt><dd><p>Attempt to use <code>keyring</code> for authentication for index URLs.</p>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue