Don't fail with --no-build when static metadata is available (#9785)

## Summary

This optimization isn't quite right, because we can successfully extract
metadata without having to build from source. (The builder itself will
error if we reach the point at which we need to build, but builds are
disabled.)

Closes https://github.com/astral-sh/uv/issues/9776.
This commit is contained in:
Charlie Marsh
2024-12-10 15:10:51 -05:00
committed by GitHub
parent 6523d90da1
commit 535ab69508
2 changed files with 123 additions and 16 deletions
@@ -11,7 +11,7 @@ use tempfile::TempDir;
use tokio::io::{AsyncRead, AsyncSeekExt, ReadBuf};
use tokio::sync::Semaphore;
use tokio_util::compat::FuturesAsyncReadCompatExt;
use tracing::{debug, info_span, instrument, warn, Instrument};
use tracing::{info_span, instrument, warn, Instrument};
use url::Url;
use uv_cache::{ArchiveId, CacheBucket, CacheEntry, WheelCache};
@@ -321,8 +321,6 @@ impl<'a, Context: BuildContext> DistributionDatabase<'a, Context> {
.boxed_local()
.await?;
// Validate that the metadata is consistent with the distribution.
// If the wheel was unzipped previously, respect it. Source distributions are
// cached under a unique revision ID, so unzipped directories are never stale.
match built_wheel.target.canonicalize() {
@@ -444,19 +442,6 @@ impl<'a, Context: BuildContext> DistributionDatabase<'a, Context> {
}
}
// Optimization: Skip source dist download when we must not build them anyway.
if self
.build_context
.build_options()
.no_build_requirement(source.name())
{
if source.is_editable() {
debug!("Allowing build for editable source distribution: {source}");
} else {
return Err(Error::NoBuild);
}
}
let lock = self.locks.acquire(source).await;
let _guard = lock.lock().await;
+122
View File
@@ -19305,6 +19305,128 @@ fn no_lowest_warning_with_name_and_url() -> Result<()> {
Ok(())
}
#[test]
fn lock_no_build_static_metadata() -> Result<()> {
let context = TestContext::new("3.12");
let pyproject_toml = context.temp_dir.child("pyproject.toml");
pyproject_toml.write_str(
r#"
[project]
name = "dummy"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["iniconfig"]
"#,
)?;
uv_snapshot!(context.filters(), context.lock().arg("--no-build"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 2 packages in [TIME]
"###);
let lock = context.read("uv.lock");
insta::with_settings!({
filters => context.filters(),
}, {
assert_snapshot!(
lock, @r###"
version = 1
requires-python = ">=3.12"
[options]
exclude-newer = "2024-03-25T00:00:00Z"
[[package]]
name = "dummy"
version = "0.1.0"
source = { virtual = "." }
dependencies = [
{ name = "iniconfig" },
]
[package.metadata]
requires-dist = [{ name = "iniconfig" }]
[[package]]
name = "iniconfig"
version = "2.0.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 },
]
"###
);
});
// Re-run with `--locked`.
uv_snapshot!(context.filters(), context.lock().arg("--no-build").arg("--locked"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 2 packages in [TIME]
"###);
// Re-run with `--offline`. We shouldn't need a network connection to validate an
// already-correct lockfile with immutable metadata.
uv_snapshot!(context.filters(), context.lock().arg("--no-build").arg("--locked").arg("--offline").arg("--no-cache"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 2 packages in [TIME]
"###);
// Install from the lockfile.
uv_snapshot!(context.filters(), context.sync().arg("--no-build").arg("--frozen"), @r###"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
error: Distribution `dummy==0.1.0 @ virtual+.` can't be installed because it is marked as `--no-build` but has no binary distribution
"###);
Ok(())
}
#[test]
fn lock_no_build_dynamic_metadata() -> Result<()> {
let context = TestContext::new("3.12");
let pyproject_toml = context.temp_dir.child("pyproject.toml");
pyproject_toml.write_str(
r#"
[project]
name = "dummy"
version = "0.1.0"
requires-python = ">=3.12"
dynamic = ["dependencies"]
"#,
)?;
uv_snapshot!(context.filters(), context.lock().arg("--no-build"), @r###"
success: false
exit_code: 1
----- stdout -----
----- stderr -----
× Failed to build `dummy @ file://[TEMP_DIR]/`
Building source distributions for dummy is disabled
"###);
Ok(())
}
#[test]
fn lock_self_compatible() -> Result<()> {
let context = TestContext::new("3.12");