mirror of
https://github.com/astral-sh/uv.git
synced 2025-07-07 21:35:00 +00:00
Avoid building packages with dynamic versions (#4058)
## Summary This PR separates "gathering the requirements" from the rest of the metadata (e.g., version), which isn't required when installing a package's _dependencies_ (as opposed to installing the package itself). It thus ensures that we don't need to build a package when a static `pyproject.toml` is provided in `pip compile`. Closes https://github.com/astral-sh/uv/issues/4040.
This commit is contained in:
parent
a0173760f1
commit
191f9556b7
8 changed files with 609 additions and 382 deletions
|
@ -327,6 +327,73 @@ fn parse_version(metadata_version: &str) -> Result<(u8, u8), MetadataError> {
|
|||
Ok((major, minor))
|
||||
}
|
||||
|
||||
/// Python Package Metadata 2.3 as specified in
|
||||
/// <https://packaging.python.org/specifications/core-metadata/>.
|
||||
///
|
||||
/// This is a subset of [`Metadata23`]; specifically, it omits the `version` and `requires-python`
|
||||
/// fields, which aren't necessary when extracting the requirements of a package without installing
|
||||
/// the package itself.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub struct RequiresDist {
|
||||
pub name: PackageName,
|
||||
pub requires_dist: Vec<Requirement<VerbatimParsedUrl>>,
|
||||
pub provides_extras: Vec<ExtraName>,
|
||||
}
|
||||
|
||||
impl RequiresDist {
|
||||
/// Extract the [`RequiresDist`] from a `pyproject.toml` file, as specified in PEP 621.
|
||||
pub fn parse_pyproject_toml(contents: &str) -> Result<Self, MetadataError> {
|
||||
let pyproject_toml: PyProjectToml = toml::from_str(contents)?;
|
||||
|
||||
let project = pyproject_toml
|
||||
.project
|
||||
.ok_or(MetadataError::FieldNotFound("project"))?;
|
||||
|
||||
// If any of the fields we need were declared as dynamic, we can't use the `pyproject.toml`
|
||||
// file.
|
||||
let dynamic = project.dynamic.unwrap_or_default();
|
||||
for field in dynamic {
|
||||
match field.as_str() {
|
||||
"dependencies" => return Err(MetadataError::DynamicField("dependencies")),
|
||||
"optional-dependencies" => {
|
||||
return Err(MetadataError::DynamicField("optional-dependencies"))
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
let name = project.name;
|
||||
|
||||
// Extract the requirements.
|
||||
let mut requires_dist = project
|
||||
.dependencies
|
||||
.unwrap_or_default()
|
||||
.into_iter()
|
||||
.map(Requirement::from)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Extract the optional dependencies.
|
||||
let mut provides_extras: Vec<ExtraName> = Vec::new();
|
||||
for (extra, requirements) in project.optional_dependencies.unwrap_or_default() {
|
||||
requires_dist.extend(
|
||||
requirements
|
||||
.into_iter()
|
||||
.map(Requirement::from)
|
||||
.map(|requirement| requirement.with_extra_marker(&extra))
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
provides_extras.push(extra);
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
name,
|
||||
requires_dist,
|
||||
provides_extras,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// The headers of a distribution metadata file.
|
||||
#[derive(Debug)]
|
||||
struct Headers<'a>(Vec<mailparse::MailHeader<'a>>);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue