Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions crates/uv-build-backend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ use crate::settings::ModuleName;
pub enum Error {
#[error(transparent)]
Io(#[from] io::Error),
#[error("Invalid pyproject.toml")]
Toml(#[from] toml::de::Error),
#[error("Invalid pyproject.toml")]
#[error("Invalid metadata format in: {}", _0.user_display())]
Toml(PathBuf, #[source] toml::de::Error),
#[error("Invalid project metadata")]
Validation(#[from] ValidationError),
#[error("Invalid module name: {0}")]
InvalidModuleName(String, #[source] IdentifierParseError),
Expand Down
70 changes: 36 additions & 34 deletions crates/uv-build-backend/src/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,11 @@ impl PyProjectToml {
&self.project.version
}

pub(crate) fn parse(contents: &str) -> Result<Self, Error> {
Ok(toml::from_str(contents)?)
pub(crate) fn parse(path: &Path) -> Result<Self, Error> {
let contents = fs_err::read_to_string(path)?;
let pyproject_toml =
toml::from_str(&contents).map_err(|err| Error::Toml(path.to_path_buf(), err))?;
Ok(pyproject_toml)
}

pub(crate) fn readme(&self) -> Option<&Readme> {
Expand Down Expand Up @@ -949,7 +952,7 @@ mod tests {
requires = ["uv_build>=0.4.15,<0.5.0"]
build-backend = "uv_build"
"#;
let pyproject_toml = PyProjectToml::parse(contents).unwrap();
let pyproject_toml: PyProjectToml = toml::from_str(contents).unwrap();
let temp_dir = TempDir::new().unwrap();

let metadata = pyproject_toml.to_metadata(temp_dir.path()).unwrap();
Expand Down Expand Up @@ -1034,7 +1037,7 @@ mod tests {
"#
};

let pyproject_toml = PyProjectToml::parse(contents).unwrap();
let pyproject_toml: PyProjectToml = toml::from_str(contents).unwrap();
let metadata = pyproject_toml.to_metadata(temp_dir.path()).unwrap();

assert_snapshot!(metadata.core_metadata_format(), @r###"
Expand Down Expand Up @@ -1128,7 +1131,7 @@ mod tests {
"#
};

let pyproject_toml = PyProjectToml::parse(contents).unwrap();
let pyproject_toml: PyProjectToml = toml::from_str(contents).unwrap();
let metadata = pyproject_toml.to_metadata(temp_dir.path()).unwrap();

assert_snapshot!(metadata.core_metadata_format(), @r"
Expand Down Expand Up @@ -1220,7 +1223,7 @@ mod tests {
"#
};

let pyproject_toml = PyProjectToml::parse(contents).unwrap();
let pyproject_toml: PyProjectToml = toml::from_str(contents).unwrap();
let metadata = pyproject_toml.to_metadata(temp_dir.path()).unwrap();

assert_snapshot!(metadata.core_metadata_format(), @r###"
Expand Down Expand Up @@ -1281,7 +1284,7 @@ mod tests {
#[test]
fn build_system_valid() {
let contents = extend_project("");
let pyproject_toml = PyProjectToml::parse(&contents).unwrap();
let pyproject_toml: PyProjectToml = toml::from_str(&contents).unwrap();
assert_snapshot!(
pyproject_toml.check_build_system("0.4.15+test").join("\n"),
@""
Expand All @@ -1299,7 +1302,7 @@ mod tests {
requires = ["uv_build"]
build-backend = "uv_build"
"#};
let pyproject_toml = PyProjectToml::parse(contents).unwrap();
let pyproject_toml: PyProjectToml = toml::from_str(contents).unwrap();
assert_snapshot!(
pyproject_toml.check_build_system("0.4.15+test").join("\n"),
@r###"`build_system.requires = ["uv_build"]` is missing an upper bound on the `uv_build` version such as `<0.5`. Without bounding the `uv_build` version, the source distribution will break when a future, breaking version of `uv_build` is released."###
Expand All @@ -1317,7 +1320,7 @@ mod tests {
requires = ["uv_build>=0.4.15,<0.5.0", "wheel"]
build-backend = "uv_build"
"#};
let pyproject_toml = PyProjectToml::parse(contents).unwrap();
let pyproject_toml: PyProjectToml = toml::from_str(contents).unwrap();
assert_snapshot!(
pyproject_toml.check_build_system("0.4.15+test").join("\n"),
@"Expected a single uv requirement in `build-system.requires`, found ``"
Expand All @@ -1335,7 +1338,7 @@ mod tests {
requires = ["setuptools"]
build-backend = "uv_build"
"#};
let pyproject_toml = PyProjectToml::parse(contents).unwrap();
let pyproject_toml: PyProjectToml = toml::from_str(contents).unwrap();
assert_snapshot!(
pyproject_toml.check_build_system("0.4.15+test").join("\n"),
@"Expected a single uv requirement in `build-system.requires`, found ``"
Expand All @@ -1353,7 +1356,7 @@ mod tests {
requires = ["uv_build>=0.4.15,<0.5.0"]
build-backend = "setuptools"
"#};
let pyproject_toml = PyProjectToml::parse(contents).unwrap();
let pyproject_toml: PyProjectToml = toml::from_str(contents).unwrap();
assert_snapshot!(
pyproject_toml.check_build_system("0.4.15+test").join("\n"),
@r###"The value for `build_system.build-backend` should be `"uv_build"`, not `"setuptools"`"###
Expand All @@ -1364,7 +1367,7 @@ mod tests {
fn minimal() {
let contents = extend_project("");

let metadata = PyProjectToml::parse(&contents)
let metadata = toml::from_str::<PyProjectToml>(&contents)
.unwrap()
.to_metadata(Path::new("/do/not/read"))
.unwrap();
Expand All @@ -1383,15 +1386,14 @@ mod tests {
"#
});

let err = PyProjectToml::parse(&contents).unwrap_err();
assert_snapshot!(format_err(err), @r###"
Invalid pyproject.toml
Caused by: TOML parse error at line 4, column 10
let err = toml::from_str::<PyProjectToml>(&contents).unwrap_err();
assert_snapshot!(format_err(err), @r#"
TOML parse error at line 4, column 10
|
4 | readme = { path = "Readme.md" }
| ^^^^^^^^^^^^^^^^^^^^^^
data did not match any variant of untagged enum Readme
"###);
"#);
}

#[test]
Expand All @@ -1401,7 +1403,7 @@ mod tests {
"#
});

let err = PyProjectToml::parse(&contents)
let err = toml::from_str::<PyProjectToml>(&contents)
.unwrap()
.to_metadata(Path::new("/do/not/read"))
.unwrap_err();
Expand All @@ -1423,14 +1425,14 @@ mod tests {
"#
});

let err = PyProjectToml::parse(&contents)
let err = toml::from_str::<PyProjectToml>(&contents)
.unwrap()
.to_metadata(Path::new("/do/not/read"))
.unwrap_err();
assert_snapshot!(format_err(err), @r###"
Invalid pyproject.toml
assert_snapshot!(format_err(err), @r"
Invalid project metadata
Caused by: `project.description` must be a single line
"###);
");
}

#[test]
Expand All @@ -1441,14 +1443,14 @@ mod tests {
"#
});

let err = PyProjectToml::parse(&contents)
let err = toml::from_str::<PyProjectToml>(&contents)
.unwrap()
.to_metadata(Path::new("/do/not/read"))
.unwrap_err();
assert_snapshot!(format_err(err), @r###"
Invalid pyproject.toml
assert_snapshot!(format_err(err), @r"
Invalid project metadata
Caused by: When `project.license-files` is defined, `project.license` must be an SPDX expression string
"###);
");
}

#[test]
Expand All @@ -1457,7 +1459,7 @@ mod tests {
license = "MIT OR Apache-2.0"
"#
});
let metadata = PyProjectToml::parse(&contents)
let metadata = toml::from_str::<PyProjectToml>(&contents)
.unwrap()
.to_metadata(Path::new("/do/not/read"))
.unwrap();
Expand All @@ -1475,13 +1477,13 @@ mod tests {
license = "MIT XOR Apache-2"
"#
});
let err = PyProjectToml::parse(&contents)
let err = toml::from_str::<PyProjectToml>(&contents)
.unwrap()
.to_metadata(Path::new("/do/not/read"))
.unwrap_err();
// TODO(konsti): We mess up the indentation in the error.
assert_snapshot!(format_err(err), @r"
Invalid pyproject.toml
Invalid project metadata
Caused by: `project.license` is not a valid SPDX expression: MIT XOR Apache-2
Caused by: MIT XOR Apache-2
^^^ unknown term
Expand All @@ -1495,18 +1497,18 @@ mod tests {
"#
});

let err = PyProjectToml::parse(&contents)
let err = toml::from_str::<PyProjectToml>(&contents)
.unwrap()
.to_metadata(Path::new("/do/not/read"))
.unwrap_err();
assert_snapshot!(format_err(err), @r###"
Invalid pyproject.toml
assert_snapshot!(format_err(err), @r"
Invalid project metadata
Caused by: Dynamic metadata is not supported
"###);
");
}

fn script_error(contents: &str) -> String {
let err = PyProjectToml::parse(contents)
let err = toml::from_str::<PyProjectToml>(contents)
.unwrap()
.to_entry_points()
.unwrap_err();
Expand Down
9 changes: 3 additions & 6 deletions crates/uv-build-backend/src/source_dist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@ pub fn build_source_dist(
uv_version: &str,
show_warnings: bool,
) -> Result<SourceDistFilename, Error> {
let contents = fs_err::read_to_string(source_tree.join("pyproject.toml"))?;
let pyproject_toml = PyProjectToml::parse(&contents)?;
let pyproject_toml = PyProjectToml::parse(&source_tree.join("pyproject.toml"))?;
let filename = SourceDistFilename {
name: pyproject_toml.name().clone(),
version: pyproject_toml.version().clone(),
Expand All @@ -45,8 +44,7 @@ pub fn list_source_dist(
uv_version: &str,
show_warnings: bool,
) -> Result<(SourceDistFilename, FileList), Error> {
let contents = fs_err::read_to_string(source_tree.join("pyproject.toml"))?;
let pyproject_toml = PyProjectToml::parse(&contents)?;
let pyproject_toml = PyProjectToml::parse(&source_tree.join("pyproject.toml"))?;
let filename = SourceDistFilename {
name: pyproject_toml.name().clone(),
version: pyproject_toml.version().clone(),
Expand Down Expand Up @@ -188,8 +186,7 @@ fn write_source_dist(
uv_version: &str,
show_warnings: bool,
) -> Result<SourceDistFilename, Error> {
let contents = fs_err::read_to_string(source_tree.join("pyproject.toml"))?;
let pyproject_toml = PyProjectToml::parse(&contents)?;
let pyproject_toml = PyProjectToml::parse(&source_tree.join("pyproject.toml"))?;
for warning in pyproject_toml.check_build_system(uv_version) {
warn_user_once!("{warning}");
}
Expand Down
12 changes: 4 additions & 8 deletions crates/uv-build-backend/src/wheel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@ pub fn build_wheel(
uv_version: &str,
show_warnings: bool,
) -> Result<WheelFilename, Error> {
let contents = fs_err::read_to_string(source_tree.join("pyproject.toml"))?;
let pyproject_toml = PyProjectToml::parse(&contents)?;
let pyproject_toml = PyProjectToml::parse(&source_tree.join("pyproject.toml"))?;
for warning in pyproject_toml.check_build_system(uv_version) {
warn_user_once!("{warning}");
}
Expand Down Expand Up @@ -71,8 +70,7 @@ pub fn list_wheel(
uv_version: &str,
show_warnings: bool,
) -> Result<(WheelFilename, FileList), Error> {
let contents = fs_err::read_to_string(source_tree.join("pyproject.toml"))?;
let pyproject_toml = PyProjectToml::parse(&contents)?;
let pyproject_toml = PyProjectToml::parse(&source_tree.join("pyproject.toml"))?;
for warning in pyproject_toml.check_build_system(uv_version) {
warn_user_once!("{warning}");
}
Expand Down Expand Up @@ -273,8 +271,7 @@ pub fn build_editable(
uv_version: &str,
show_warnings: bool,
) -> Result<WheelFilename, Error> {
let contents = fs_err::read_to_string(source_tree.join("pyproject.toml"))?;
let pyproject_toml = PyProjectToml::parse(&contents)?;
let pyproject_toml = PyProjectToml::parse(&source_tree.join("pyproject.toml"))?;
for warning in pyproject_toml.check_build_system(uv_version) {
warn_user_once!("{warning}");
}
Expand Down Expand Up @@ -335,8 +332,7 @@ pub fn metadata(
metadata_directory: &Path,
uv_version: &str,
) -> Result<String, Error> {
let contents = fs_err::read_to_string(source_tree.join("pyproject.toml"))?;
let pyproject_toml = PyProjectToml::parse(&contents)?;
let pyproject_toml = PyProjectToml::parse(&source_tree.join("pyproject.toml"))?;
for warning in pyproject_toml.check_build_system(uv_version) {
warn_user_once!("{warning}");
}
Expand Down
49 changes: 43 additions & 6 deletions crates/uv/tests/it/build_backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -792,15 +792,15 @@ fn license_glob_without_matches_errors() -> Result<()> {
.build_backend()
.arg("build-wheel")
.arg(context.temp_dir.path())
.current_dir(project.path()), @r###"
.current_dir(project.path()), @r"
success: false
exit_code: 2
----- stdout -----

----- stderr -----
error: Invalid pyproject.toml
error: Invalid project metadata
Caused by: `project.license-files` glob `abc` did not match any files
"###);
");

Ok(())
}
Expand Down Expand Up @@ -835,15 +835,15 @@ fn license_file_must_be_utf8() -> Result<()> {
.build_backend()
.arg("build-wheel")
.arg(context.temp_dir.path())
.current_dir(project.path()), @r###"
.current_dir(project.path()), @r"
success: false
exit_code: 2
----- stdout -----

----- stderr -----
error: Invalid pyproject.toml
error: Invalid project metadata
Caused by: License file `LICENSE.bin` must be UTF-8 encoded
"###);
");

Ok(())
}
Expand Down Expand Up @@ -1185,3 +1185,40 @@ fn warn_on_redundant_module_names() -> Result<()> {

Ok(())
}

#[test]
fn invalid_pyproject_toml() -> Result<()> {
let context = TestContext::new("3.12");

context
.temp_dir
.child("child")
.child("pyproject.toml")
.write_str(indoc! {r#"
[project]
name = 1
version = "1.0.0"

[build-system]
requires = ["uv_build>=0.9,<10000"]
build-backend = "uv_build"
"#})?;

uv_snapshot!(context.filters(), context.build().arg("child"), @r"
success: false
exit_code: 2
----- stdout -----

----- stderr -----
Building source distribution (uv build backend)...
× Failed to build `[TEMP_DIR]/child`
├─▶ Invalid metadata format in: child/pyproject.toml
╰─▶ TOML parse error at line 2, column 8
|
2 | name = 1
| ^
invalid type: integer `1`, expected a string
");

Ok(())
}
Loading