Skip to content

Commit 535ab69

Browse files
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 #9776.
1 parent 6523d90 commit 535ab69

File tree

2 files changed

+123
-16
lines changed

2 files changed

+123
-16
lines changed

crates/uv-distribution/src/distribution_database.rs

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use tempfile::TempDir;
1111
use tokio::io::{AsyncRead, AsyncSeekExt, ReadBuf};
1212
use tokio::sync::Semaphore;
1313
use tokio_util::compat::FuturesAsyncReadCompatExt;
14-
use tracing::{debug, info_span, instrument, warn, Instrument};
14+
use tracing::{info_span, instrument, warn, Instrument};
1515
use url::Url;
1616

1717
use uv_cache::{ArchiveId, CacheBucket, CacheEntry, WheelCache};
@@ -321,8 +321,6 @@ impl<'a, Context: BuildContext> DistributionDatabase<'a, Context> {
321321
.boxed_local()
322322
.await?;
323323

324-
// Validate that the metadata is consistent with the distribution.
325-
326324
// If the wheel was unzipped previously, respect it. Source distributions are
327325
// cached under a unique revision ID, so unzipped directories are never stale.
328326
match built_wheel.target.canonicalize() {
@@ -444,19 +442,6 @@ impl<'a, Context: BuildContext> DistributionDatabase<'a, Context> {
444442
}
445443
}
446444

447-
// Optimization: Skip source dist download when we must not build them anyway.
448-
if self
449-
.build_context
450-
.build_options()
451-
.no_build_requirement(source.name())
452-
{
453-
if source.is_editable() {
454-
debug!("Allowing build for editable source distribution: {source}");
455-
} else {
456-
return Err(Error::NoBuild);
457-
}
458-
}
459-
460445
let lock = self.locks.acquire(source).await;
461446
let _guard = lock.lock().await;
462447

crates/uv/tests/it/lock.rs

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19305,6 +19305,128 @@ fn no_lowest_warning_with_name_and_url() -> Result<()> {
1930519305
Ok(())
1930619306
}
1930719307

19308+
#[test]
19309+
fn lock_no_build_static_metadata() -> Result<()> {
19310+
let context = TestContext::new("3.12");
19311+
19312+
let pyproject_toml = context.temp_dir.child("pyproject.toml");
19313+
pyproject_toml.write_str(
19314+
r#"
19315+
[project]
19316+
name = "dummy"
19317+
version = "0.1.0"
19318+
requires-python = ">=3.12"
19319+
dependencies = ["iniconfig"]
19320+
"#,
19321+
)?;
19322+
19323+
uv_snapshot!(context.filters(), context.lock().arg("--no-build"), @r###"
19324+
success: true
19325+
exit_code: 0
19326+
----- stdout -----
19327+
19328+
----- stderr -----
19329+
Resolved 2 packages in [TIME]
19330+
"###);
19331+
19332+
let lock = context.read("uv.lock");
19333+
19334+
insta::with_settings!({
19335+
filters => context.filters(),
19336+
}, {
19337+
assert_snapshot!(
19338+
lock, @r###"
19339+
version = 1
19340+
requires-python = ">=3.12"
19341+
19342+
[options]
19343+
exclude-newer = "2024-03-25T00:00:00Z"
19344+
19345+
[[package]]
19346+
name = "dummy"
19347+
version = "0.1.0"
19348+
source = { virtual = "." }
19349+
dependencies = [
19350+
{ name = "iniconfig" },
19351+
]
19352+
19353+
[package.metadata]
19354+
requires-dist = [{ name = "iniconfig" }]
19355+
19356+
[[package]]
19357+
name = "iniconfig"
19358+
version = "2.0.0"
19359+
source = { registry = "https://pypi.org/simple" }
19360+
sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 }
19361+
wheels = [
19362+
{ url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 },
19363+
]
19364+
"###
19365+
);
19366+
});
19367+
19368+
// Re-run with `--locked`.
19369+
uv_snapshot!(context.filters(), context.lock().arg("--no-build").arg("--locked"), @r###"
19370+
success: true
19371+
exit_code: 0
19372+
----- stdout -----
19373+
19374+
----- stderr -----
19375+
Resolved 2 packages in [TIME]
19376+
"###);
19377+
19378+
// Re-run with `--offline`. We shouldn't need a network connection to validate an
19379+
// already-correct lockfile with immutable metadata.
19380+
uv_snapshot!(context.filters(), context.lock().arg("--no-build").arg("--locked").arg("--offline").arg("--no-cache"), @r###"
19381+
success: true
19382+
exit_code: 0
19383+
----- stdout -----
19384+
19385+
----- stderr -----
19386+
Resolved 2 packages in [TIME]
19387+
"###);
19388+
19389+
// Install from the lockfile.
19390+
uv_snapshot!(context.filters(), context.sync().arg("--no-build").arg("--frozen"), @r###"
19391+
success: false
19392+
exit_code: 2
19393+
----- stdout -----
19394+
19395+
----- stderr -----
19396+
error: Distribution `dummy==0.1.0 @ virtual+.` can't be installed because it is marked as `--no-build` but has no binary distribution
19397+
"###);
19398+
19399+
Ok(())
19400+
}
19401+
19402+
#[test]
19403+
fn lock_no_build_dynamic_metadata() -> Result<()> {
19404+
let context = TestContext::new("3.12");
19405+
19406+
let pyproject_toml = context.temp_dir.child("pyproject.toml");
19407+
pyproject_toml.write_str(
19408+
r#"
19409+
[project]
19410+
name = "dummy"
19411+
version = "0.1.0"
19412+
requires-python = ">=3.12"
19413+
dynamic = ["dependencies"]
19414+
"#,
19415+
)?;
19416+
19417+
uv_snapshot!(context.filters(), context.lock().arg("--no-build"), @r###"
19418+
success: false
19419+
exit_code: 1
19420+
----- stdout -----
19421+
19422+
----- stderr -----
19423+
× Failed to build `dummy @ file://[TEMP_DIR]/`
19424+
╰─▶ Building source distributions for dummy is disabled
19425+
"###);
19426+
19427+
Ok(())
19428+
}
19429+
1930819430
#[test]
1930919431
fn lock_self_compatible() -> Result<()> {
1931019432
let context = TestContext::new("3.12");

0 commit comments

Comments
 (0)