Add pyproject.toml manifest support (PEP 621) - #1842
Conversation
Parse a repository's pyproject.toml [project] table so onefetch reports the project's dependency count, name, version, description and license for Python projects, the same way it already does for Cargo.toml and package.json.
spenserblack
left a comment
There was a problem hiding this comment.
Thanks! Mostly looks good, I just have a few nitpicks.
| pub enum ManifestType { | ||
| Npm, | ||
| Cargo, | ||
| #[strum(to_string = "pyproject.toml")] |
There was a problem hiding this comment.
Do we want to set the to_string to "pyproject.toml"? We don't call the Npm variant "package.json", or the Cargo variant "Cargo.toml".
| // A `license = { file = "LICENSE" }` table carries no identifier, so leave it | ||
| // unset and let onefetch fall back to detecting the license from the repo. | ||
| let license = project.license.and_then(|license| match license { | ||
| PyProjectLicense::Spdx(spdx) => Some(spdx), | ||
| PyProjectLicense::Table { text } => text, | ||
| }); |
There was a problem hiding this comment.
I was a bit confused by this for a moment, but, if I understand it correctly, the value of license.text (if license is a table) may also be the SPDX identifier? But it seems like license.text may be any string if the project uses a non-standard license? Could you clarify this in the comment?
| // SPDX expression string (PEP 639) | ||
| let spdx: PyProjectTable = toml::from_str("license = \"MIT\"").unwrap(); | ||
| assert!(matches!(spdx.license, Some(PyProjectLicense::Spdx(s)) if s == "MIT")); | ||
|
|
||
| // `{ text = "..." }` table (older PEP 621 form) | ||
| let text: PyProjectTable = toml::from_str("license = { text = \"Apache-2.0\" }").unwrap(); | ||
| assert!( | ||
| matches!(text.license, Some(PyProjectLicense::Table { text: Some(t) }) if t == "Apache-2.0") | ||
| ); | ||
|
|
||
| // `{ file = "LICENSE" }` table carries no identifier | ||
| let file: PyProjectTable = toml::from_str("license = { file = \"LICENSE\" }").unwrap(); | ||
| assert!(matches!( | ||
| file.license, | ||
| Some(PyProjectLicense::Table { text: None }) | ||
| )); | ||
| } |
There was a problem hiding this comment.
Instead of using 3 assert!, I think you can use rstest to make this 3 test cases instead. That way a prior failure wouldn't block the following test cases from running.
… comment, use rstest - Remove the strum to_string override so PyProject renders like the other variants (Npm/Cargo) instead of pyproject.toml. - Expand the PyProjectLicense doc/comment to explain that license.text is free-form and only conventionally an SPDX identifier. - Convert the three license-form assertions into rstest cases so one failure no longer masks the others.
|
Thanks for the review! Pushed a commit addressing all three:
|
spenserblack
left a comment
There was a problem hiding this comment.
Thanks! Just these last two changes and LGTM!
|
I hate to do this, but one more thing, since it looks like you've managed to author over 90 pull requests today alone: have you read our contributing guidelines (including our AI policy)? |
spenserblack
left a comment
There was a problem hiding this comment.
Oops, I thought just using rstest.workspace = true was enough to synchronize the versions, but looks like a little bit more work is needed.
TBH you can just revert bd4491b. I'll look into utilizing that feature some other time.
Closes #1590. This adds
pyproject.tomlto the set of manifests onefetch understands, so Python projects show a dependency count, description, version and license just like Cargo and npm projects already do.Following the discussion on the issue, I focused on the format specified by the PEPs rather than tool-specific layouts. A new
ManifestType::PyProjectreads the PEP 621[project]table:number_of_dependenciesfromproject.dependenciesname/version/descriptionfrom theirproject.*fieldslicensefromproject.license, handled as either a PEP 639 SPDX string or the older{ text = "..." }table. A{ file = "LICENSE" }table carries no identifier, so it's left unset and onefetch falls back to detecting the license from the repo as before.Tool-specific tables such as
[tool.poetry]use a different shape and are intentionally out of scope here.Parsing uses
toml, which was already in the tree viacargo_toml, soCargo.lockonly gains the existing version as a direct dependency of the manifest crate. In the Dependencies field this reads as e.g.3 (pyproject.toml).Tests
manifest/tests/pyproject.rswith atests/fixtures/pyproject/fixture, mirroring the existing cargo/npm integration tests.manifest/src/lib.rscovering the three PEP 621licenseforms (SPDX string,{ text }table,{ file }table).pyproject.toml:onefetchprintsDependencies: 3 (pyproject.toml)and picks up the description, version and license.cargo fmt --all --check,cargo clippy, and the full test suite pass.