mirror of
https://github.com/astral-sh/uv.git
synced 2025-08-04 19:08:04 +00:00
Build backend: Fix pre-PEP 639 license files (#9965)
We were not copying the license file from a pre-PEP 639 declaration to the source distribution. Fixes #9947
This commit is contained in:
parent
a78e7468a7
commit
654ff8015a
4 changed files with 113 additions and 9 deletions
|
@ -180,9 +180,10 @@ mod tests {
|
|||
use super::*;
|
||||
use flate2::bufread::GzDecoder;
|
||||
use fs_err::File;
|
||||
use indoc::indoc;
|
||||
use insta::assert_snapshot;
|
||||
use itertools::Itertools;
|
||||
use std::io::BufReader;
|
||||
use std::io::{BufReader, Read};
|
||||
use tempfile::TempDir;
|
||||
use uv_fs::{copy_dir_all, relative_to};
|
||||
|
||||
|
@ -391,4 +392,73 @@ mod tests {
|
|||
fs_err::read(indirect_output_dir.path().join(wheel_filename)).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
/// Test that `license = { file = "LICENSE" }` is supported.
|
||||
#[test]
|
||||
fn license_file_pre_pep639() {
|
||||
let src = TempDir::new().unwrap();
|
||||
fs_err::write(
|
||||
src.path().join("pyproject.toml"),
|
||||
indoc! {r#"
|
||||
[project]
|
||||
name = "pep-pep639-license"
|
||||
version = "1.0.0"
|
||||
license = { file = "license.txt" }
|
||||
|
||||
[build-system]
|
||||
requires = ["uv>=0.5.15,<0.6"]
|
||||
build-backend = "uv"
|
||||
"#
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
fs_err::create_dir_all(src.path().join("src").join("pep_pep639_license")).unwrap();
|
||||
File::create(
|
||||
src.path()
|
||||
.join("src")
|
||||
.join("pep_pep639_license")
|
||||
.join("__init__.py"),
|
||||
)
|
||||
.unwrap();
|
||||
fs_err::write(
|
||||
src.path().join("license.txt"),
|
||||
"Copy carefully.\nSincerely, the authors",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Build a wheel from a source distribution
|
||||
let output_dir = TempDir::new().unwrap();
|
||||
build_source_dist(src.path(), output_dir.path(), "0.5.15").unwrap();
|
||||
let sdist_tree = TempDir::new().unwrap();
|
||||
let source_dist_path = output_dir.path().join("pep_pep639_license-1.0.0.tar.gz");
|
||||
let sdist_reader = BufReader::new(File::open(&source_dist_path).unwrap());
|
||||
let mut source_dist = tar::Archive::new(GzDecoder::new(sdist_reader));
|
||||
source_dist.unpack(sdist_tree.path()).unwrap();
|
||||
build_wheel(
|
||||
&sdist_tree.path().join("pep_pep639_license-1.0.0"),
|
||||
output_dir.path(),
|
||||
None,
|
||||
"0.5.15",
|
||||
)
|
||||
.unwrap();
|
||||
let wheel = output_dir
|
||||
.path()
|
||||
.join("pep_pep639_license-1.0.0-py3-none-any.whl");
|
||||
let mut wheel = zip::ZipArchive::new(File::open(wheel).unwrap()).unwrap();
|
||||
|
||||
let mut metadata = String::new();
|
||||
wheel
|
||||
.by_name("pep_pep639_license-1.0.0.dist-info/METADATA")
|
||||
.unwrap()
|
||||
.read_to_string(&mut metadata)
|
||||
.unwrap();
|
||||
|
||||
assert_snapshot!(metadata, @r###"
|
||||
Metadata-Version: 2.3
|
||||
Name: pep-pep639-license
|
||||
Version: 1.0.0
|
||||
License: Copy carefully.
|
||||
Sincerely, the authors
|
||||
"###);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -119,8 +119,31 @@ impl PyProjectToml {
|
|||
self.project.readme.as_ref()
|
||||
}
|
||||
|
||||
pub(crate) fn license_files(&self) -> Option<&[String]> {
|
||||
self.project.license_files.as_deref()
|
||||
/// The license files that need to be included in the source distribution.
|
||||
pub(crate) fn license_files_source_dist(&self) -> impl Iterator<Item = &str> {
|
||||
let license_file = self
|
||||
.project
|
||||
.license
|
||||
.as_ref()
|
||||
.and_then(|license| license.file())
|
||||
.into_iter();
|
||||
let license_files = self
|
||||
.project
|
||||
.license_files
|
||||
.iter()
|
||||
.flatten()
|
||||
.map(String::as_str);
|
||||
license_files.chain(license_file)
|
||||
}
|
||||
|
||||
/// The license files that need to be included in the wheel.
|
||||
pub(crate) fn license_files_wheel(&self) -> impl Iterator<Item = &str> {
|
||||
// The pre-PEP 639 `license = { file = "..." }` is included inline in `METADATA`.
|
||||
self.project
|
||||
.license_files
|
||||
.iter()
|
||||
.flatten()
|
||||
.map(String::as_str)
|
||||
}
|
||||
|
||||
pub(crate) fn settings(&self) -> Option<&BuildBackendSettings> {
|
||||
|
@ -682,10 +705,20 @@ pub(crate) enum License {
|
|||
},
|
||||
File {
|
||||
/// The file containing the license text.
|
||||
file: PathBuf,
|
||||
file: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl License {
|
||||
fn file(&self) -> Option<&str> {
|
||||
if let Self::File { file } = self {
|
||||
Some(file)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A `project.authors` or `project.maintainers` entry as specified in
|
||||
/// <https://packaging.python.org/en/latest/specifications/pyproject-toml/#authors-maintainers>.
|
||||
///
|
||||
|
|
|
@ -95,7 +95,7 @@ fn source_dist_matcher(
|
|||
}
|
||||
|
||||
// Include the license files
|
||||
for license_files in pyproject_toml.license_files().into_iter().flatten() {
|
||||
for license_files in pyproject_toml.license_files_source_dist() {
|
||||
trace!("Including license files at: `{license_files}`");
|
||||
let glob = parse_portable_glob(license_files).map_err(|err| Error::PortableGlob {
|
||||
field: "project.license-files".to_string(),
|
||||
|
|
|
@ -175,7 +175,7 @@ fn write_wheel(
|
|||
debug!("Visited {files_visited} files for wheel build");
|
||||
|
||||
// Add the license files
|
||||
if let Some(license_files) = &pyproject_toml.license_files() {
|
||||
if pyproject_toml.license_files_wheel().next().is_some() {
|
||||
debug!("Adding license files");
|
||||
let license_dir = format!(
|
||||
"{}-{}.dist-info/licenses/",
|
||||
|
@ -186,7 +186,7 @@ fn write_wheel(
|
|||
wheel_subdir_from_globs(
|
||||
source_tree,
|
||||
&license_dir,
|
||||
license_files,
|
||||
pyproject_toml.license_files_wheel(),
|
||||
&mut wheel_writer,
|
||||
"project.license-files",
|
||||
)?;
|
||||
|
@ -429,14 +429,15 @@ pub(crate) fn build_exclude_matcher(
|
|||
fn wheel_subdir_from_globs(
|
||||
src: &Path,
|
||||
target: &str,
|
||||
globs: &[String],
|
||||
globs: impl IntoIterator<Item = impl AsRef<str>>,
|
||||
wheel_writer: &mut impl DirectoryWriter,
|
||||
// For error messages
|
||||
globs_field: &str,
|
||||
) -> Result<(), Error> {
|
||||
let license_files_globs: Vec<_> = globs
|
||||
.iter()
|
||||
.into_iter()
|
||||
.map(|license_files| {
|
||||
let license_files = license_files.as_ref();
|
||||
trace!(
|
||||
"Including {} at `{}` with `{}`",
|
||||
globs_field,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue