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:
@@ -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;
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user