Allow virtual packages with --no-build (#12314)

## Summary

Closes #12311.
This commit is contained in:
Charlie Marsh 2025-03-22 09:00:02 -07:00 committed by GitHub
parent 149102a4e7
commit 7ac6c6963d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 157 additions and 16 deletions

View file

@ -639,6 +639,7 @@ impl SourceDist {
}
}
/// Returns the [`Version`] of the distribution, if it is known.
pub fn version(&self) -> Option<&Version> {
match self {
Self::Registry(source_dist) => Some(&source_dist.version),
@ -646,7 +647,7 @@ impl SourceDist {
}
}
/// Return true if the distribution is editable.
/// Returns `true` if the distribution is editable.
pub fn is_editable(&self) -> bool {
match self {
Self::Directory(DirectorySourceDist { editable, .. }) => *editable,
@ -654,7 +655,15 @@ impl SourceDist {
}
}
/// Return true if the distribution refers to a local file or directory.
/// Returns `true` if the distribution is virtual.
pub fn is_virtual(&self) -> bool {
match self {
Self::Directory(DirectorySourceDist { r#virtual, .. }) => *r#virtual,
_ => false,
}
}
/// Returns `true` if the distribution refers to a local file or directory.
pub fn is_local(&self) -> bool {
matches!(self, Self::Directory(_) | Self::Path(_))
}
@ -668,7 +677,7 @@ impl SourceDist {
}
}
/// Return the source tree of the distribution, if available.
/// Returns the source tree of the distribution, if available.
pub fn source_tree(&self) -> Option<&Path> {
match self {
Self::Directory(dist) => Some(&dist.install_path),

View file

@ -2196,8 +2196,11 @@ impl Package {
};
}
if !no_build {
if let Some(sdist) = self.to_source_dist(workspace_root)? {
// Even with `--no-build`, allow virtual packages. (In the future, we may want to allow
// any local source tree, or at least editable source trees, which we allow in
// `uv pip`.)
if !no_build || sdist.is_virtual() {
return Ok(Dist::Source(sdist));
}
}

View file

@ -23005,14 +23005,16 @@ fn lock_no_build_static_metadata() -> Result<()> {
"###);
// Install from the lockfile.
uv_snapshot!(context.filters(), context.sync().arg("--no-build").arg("--frozen"), @r###"
success: false
exit_code: 2
uv_snapshot!(context.filters(), context.sync().arg("--no-build").arg("--frozen"), @r"
success: true
exit_code: 0
----- 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
"###);
Prepared 1 package in [TIME]
Installed 1 package in [TIME]
+ iniconfig==2.0.0
");
Ok(())
}

View file

@ -3544,6 +3544,133 @@ fn no_install_project_no_build() -> Result<()> {
Ok(())
}
#[test]
fn virtual_no_build() -> 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 = ["anyio==3.7.0"]
"#,
)?;
// Generate a lockfile.
context.lock().assert().success();
// Clear the cache.
fs_err::remove_dir_all(&context.cache_dir)?;
// `--no-build` should not raise an error, since we don't install virtual projects.
uv_snapshot!(context.filters(), context.sync().arg("--no-build"), @r"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 4 packages in [TIME]
Prepared 3 packages in [TIME]
Installed 3 packages in [TIME]
+ anyio==3.7.0
+ idna==3.6
+ sniffio==1.3.1
");
Ok(())
}
#[test]
fn virtual_no_build_dynamic_cached() -> 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"
dynamic = ["dependencies"]
[tool.setuptools.dynamic]
dependencies = {file = ["requirements.txt"]}
"#,
)?;
context
.temp_dir
.child("requirements.txt")
.write_str("anyio==3.7.0")?;
// Generate a lockfile.
context.lock().assert().success();
// `--no-build` should not raise an error, since we don't build or install the project (given
// that it's virtual and the metadata is cached).
uv_snapshot!(context.filters(), context.sync().arg("--no-build"), @r"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 4 packages in [TIME]
Prepared 3 packages in [TIME]
Installed 3 packages in [TIME]
+ anyio==3.7.0
+ idna==3.6
+ sniffio==1.3.1
");
Ok(())
}
#[test]
fn virtual_no_build_dynamic_no_cache() -> 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"
dynamic = ["dependencies"]
[tool.setuptools.dynamic]
dependencies = {file = ["requirements.txt"]}
"#,
)?;
context
.temp_dir
.child("requirements.txt")
.write_str("anyio==3.7.0")?;
// Generate a lockfile.
context.lock().assert().success();
// Clear the cache.
fs_err::remove_dir_all(&context.cache_dir)?;
// `--no-build` should raise an error, since we need to build the project.
uv_snapshot!(context.filters(), context.sync().arg("--no-build"), @r"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
error: Failed to generate package metadata for `project==0.1.0 @ virtual+.`
Caused by: Building source distributions for `project` is disabled
");
Ok(())
}
/// Convert from a package to a virtual project.
#[test]
fn convert_to_virtual() -> Result<()> {
@ -4815,25 +4942,25 @@ fn no_build_error() -> Result<()> {
error: Distribution `django-allauth==0.51.0 @ registry+https://pypi.org/simple` can't be installed because it is marked as `--no-build` but has no binary distribution
"###);
uv_snapshot!(context.filters(), context.sync().arg("--no-build"), @r###"
uv_snapshot!(context.filters(), context.sync().arg("--no-build"), @r"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
Resolved 19 packages in [TIME]
error: Distribution `project==0.1.0 @ virtual+.` can't be installed because it is marked as `--no-build` but has no binary distribution
"###);
error: Distribution `django-allauth==0.51.0 @ registry+https://pypi.org/simple` can't be installed because it is marked as `--no-build` but has no binary distribution
");
uv_snapshot!(context.filters(), context.sync().arg("--reinstall").env("UV_NO_BUILD", "1"), @r###"
uv_snapshot!(context.filters(), context.sync().arg("--reinstall").env("UV_NO_BUILD", "1"), @r"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
Resolved 19 packages in [TIME]
error: Distribution `project==0.1.0 @ virtual+.` can't be installed because it is marked as `--no-build` but has no binary distribution
"###);
error: Distribution `django-allauth==0.51.0 @ registry+https://pypi.org/simple` can't be installed because it is marked as `--no-build` but has no binary distribution
");
uv_snapshot!(context.filters(), context.sync().arg("--reinstall").env("UV_NO_BUILD_PACKAGE", "django-allauth"), @r###"
success: false