Skip to content

Commit 438ec16

Browse files
committed
Don't fail with --no-build when static metadata is available
1 parent 459269f commit 438ec16

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)