mirror of
https://github.com/astral-sh/uv
synced 2026-06-21 13:47:25 +00:00
Validate PEP 517 backend paths (#19834)
## Summary Prior to this change, we added `backend-path` entries to `sys.path` without checking that they existed in the current source tree. When an in-tree backend was omitted from an sdist, the subsequent wheel build failed with `ModuleNotFoundError` and suggested that the backend was an undeclared build dependency. This validates each `backend-path` entry before resolving or invoking the backend. Missing directories now produce a direct error explaining that the configured path does not exist, matching the behavior of PyPA's `build` frontend. The regression test builds an sdist with a Flit wrapper in `backend_dir`, intentionally omits that directory from the sdist, and verifies the diagnostic during the wheel-from-sdist build. Closes https://github.com/astral-sh/uv/issues/19771.
This commit is contained in:
@@ -69,6 +69,8 @@ pub enum Error {
|
||||
"`pyproject.toml` does not match the required schema. When the `[project]` table is present, `project.name` must be present and non-empty."
|
||||
)]
|
||||
InvalidPyprojectTomlSchema(#[from] toml_edit::de::Error),
|
||||
#[error("`backend-path` entry `{0}` does not exist or is not a directory")]
|
||||
InvalidBackendPath(String),
|
||||
#[error("Failed to resolve requirements from {0}")]
|
||||
RequirementsResolve(&'static str, #[source] AnyErrorBuild),
|
||||
#[error("Failed to install requirements from {0}")]
|
||||
@@ -105,6 +107,7 @@ impl IsBuildBackendError for Error {
|
||||
| Self::InvalidSourceDist(_)
|
||||
| Self::InvalidPyprojectTomlSyntax(_)
|
||||
| Self::InvalidPyprojectTomlSchema(_)
|
||||
| Self::InvalidBackendPath(_)
|
||||
| Self::RequirementsResolve(_, _)
|
||||
| Self::RequirementsInstall(_, _)
|
||||
| Self::Virtualenv(_)
|
||||
|
||||
@@ -610,6 +610,19 @@ impl SourceBuild {
|
||||
.build_system
|
||||
.as_ref()
|
||||
.and_then(|build_system| build_system.build_backend.as_deref());
|
||||
|
||||
if let Some(backend_path) = pyproject_toml
|
||||
.build_system
|
||||
.as_ref()
|
||||
.and_then(|build_system| build_system.backend_path.as_ref())
|
||||
{
|
||||
for path in backend_path.iter() {
|
||||
if !source_tree.join(path).is_dir() {
|
||||
return Err(Box::new(Error::InvalidBackendPath(path.to_string())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Only show the warning for first party and URL dependencies, not for registry dependencies
|
||||
// (which have sources disabled).
|
||||
if !no_sources.all()
|
||||
|
||||
@@ -148,6 +148,45 @@ fn build_basic() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// A source distribution must include an in-tree build backend referenced by `backend-path`.
|
||||
/// Regression test for <https://github.com/astral-sh/uv/issues/19771>.
|
||||
#[test]
|
||||
fn build_sdist_missing_backend_path() -> Result<()> {
|
||||
let context = uv_test::test_context!("3.12");
|
||||
let project = context.temp_dir.child("project");
|
||||
|
||||
project.child("pyproject.toml").write_str(indoc! {r#"
|
||||
[project]
|
||||
name = "project"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.12"
|
||||
description = "A test project"
|
||||
|
||||
[build-system]
|
||||
requires = ["flit_core >=3.4,<4"]
|
||||
build-backend = "my_backend:builder"
|
||||
backend-path = ["backend_dir"]
|
||||
"#})?;
|
||||
project.child("src/project/__init__.py").touch()?;
|
||||
project
|
||||
.child("backend_dir/my_backend.py")
|
||||
.write_str("from flit_core import buildapi\n\nbuilder = buildapi\n")?;
|
||||
|
||||
uv_snapshot!(context.filters(), context.build().arg(project.path()), @r"
|
||||
success: false
|
||||
exit_code: 2
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Building source distribution...
|
||||
Building wheel from source distribution...
|
||||
× Failed to build `[TEMP_DIR]/project`
|
||||
╰─▶ `backend-path` entry `backend_dir` does not exist or is not a directory
|
||||
");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_sdist() -> Result<()> {
|
||||
let context = uv_test::test_context!("3.12");
|
||||
|
||||
Reference in New Issue
Block a user