Skip to content

Commit cb325e2

Browse files
authored
metadata_directory already contains dist-info directory (#10005)
From PEP 517: ```python def prepare_metadata_for_build_wheel(metadata_directory, config_settings=None): ... ``` > Must create a .dist-info directory containing wheel metadata inside the specified metadata_directory (i.e., creates a directory like {metadata_directory}/{package}-{version}.dist-info/). ```python def build_wheel(wheel_directory, config_settings=None, metadata_directory=None): ... ``` > If the build frontend has previously called prepare_metadata_for_build_wheel and depends on the wheel resulting from this call to have metadata matching this earlier call, then it should provide the path to the created .dist-info directory as the metadata_directory argument. Notice that the `metadata_directory` is different for the both hooks: For `prepare_metadata_for_build_wheel` is doesn't contain the `.dist-info` directory as final segment, for `build_wheel` it does. Previously, the code assumed that both directories didn't contain the `.dist-info` for both cases. Checked with: ``` maturin build uv init test-uv-build-backend --build-backend uv cd test-uv-build-backend uv build --sdist --preview cd .. UV_PREVIEW=1 pip install test-uv-build-backend/dist/test_uv_build_backend-0.1.0.tar.gz --no-index --find-links target/wheels/ -v --no-cache-dir ``` Fixes #9969
1 parent 9305bad commit cb325e2

File tree

2 files changed

+71
-10
lines changed

2 files changed

+71
-10
lines changed

crates/uv-build-backend/src/lib.rs

Lines changed: 70 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use std::fs::FileType;
1111
use std::io;
1212
use std::path::{Path, PathBuf, StripPrefixError};
1313
use thiserror::Error;
14+
use tracing::debug;
1415
use uv_fs::Simplified;
1516
use uv_globfilter::PortableGlobError;
1617

@@ -139,26 +140,22 @@ fn check_metadata_directory(
139140
return Ok(());
140141
};
141142

142-
let dist_info_dir = format!(
143-
"{}-{}.dist-info",
144-
pyproject_toml.name().as_dist_info_name(),
145-
pyproject_toml.version()
143+
debug!(
144+
"Checking metadata directory {}",
145+
metadata_directory.user_display()
146146
);
147147

148148
// `METADATA` is a mandatory file.
149149
let current = pyproject_toml
150150
.to_metadata(source_tree)?
151151
.core_metadata_format();
152-
let previous =
153-
fs_err::read_to_string(metadata_directory.join(&dist_info_dir).join("METADATA"))?;
152+
let previous = fs_err::read_to_string(metadata_directory.join("METADATA"))?;
154153
if previous != current {
155154
return Err(Error::InconsistentSteps("METADATA"));
156155
}
157156

158157
// `entry_points.txt` is not written if it would be empty.
159-
let entrypoints_path = metadata_directory
160-
.join(&dist_info_dir)
161-
.join("entry_points.txt");
158+
let entrypoints_path = metadata_directory.join("entry_points.txt");
162159
match pyproject_toml.to_entry_points()? {
163160
None => {
164161
if entrypoints_path.is_file() {
@@ -461,4 +458,68 @@ mod tests {
461458
Sincerely, the authors
462459
"###);
463460
}
461+
462+
/// Test that `build_wheel` works after the `prepare_metadata_for_build_wheel` hook.
463+
#[test]
464+
fn prepare_metadata_then_build_wheel() {
465+
let src = TempDir::new().unwrap();
466+
fs_err::write(
467+
src.path().join("pyproject.toml"),
468+
indoc! {r#"
469+
[project]
470+
name = "two-step-build"
471+
version = "1.0.0"
472+
473+
[build-system]
474+
requires = ["uv>=0.5.15,<0.6"]
475+
build-backend = "uv"
476+
"#
477+
},
478+
)
479+
.unwrap();
480+
fs_err::create_dir_all(src.path().join("src").join("two_step_build")).unwrap();
481+
File::create(
482+
src.path()
483+
.join("src")
484+
.join("two_step_build")
485+
.join("__init__.py"),
486+
)
487+
.unwrap();
488+
489+
// Prepare the metadata.
490+
let metadata_dir = TempDir::new().unwrap();
491+
let dist_info_dir = metadata(src.path(), metadata_dir.path(), "0.5.15").unwrap();
492+
let metadata_prepared =
493+
fs_err::read_to_string(metadata_dir.path().join(&dist_info_dir).join("METADATA"))
494+
.unwrap();
495+
496+
// Build the wheel, using the prepared metadata directory.
497+
let output_dir = TempDir::new().unwrap();
498+
build_wheel(
499+
src.path(),
500+
output_dir.path(),
501+
Some(&metadata_dir.path().join(&dist_info_dir)),
502+
"0.5.15",
503+
)
504+
.unwrap();
505+
let wheel = output_dir
506+
.path()
507+
.join("two_step_build-1.0.0-py3-none-any.whl");
508+
let mut wheel = zip::ZipArchive::new(File::open(wheel).unwrap()).unwrap();
509+
510+
let mut metadata_wheel = String::new();
511+
wheel
512+
.by_name("two_step_build-1.0.0.dist-info/METADATA")
513+
.unwrap()
514+
.read_to_string(&mut metadata_wheel)
515+
.unwrap();
516+
517+
assert_eq!(metadata_prepared, metadata_wheel);
518+
519+
assert_snapshot!(metadata_wheel, @r###"
520+
Metadata-Version: 2.3
521+
Name: two-step-build
522+
Version: 1.0.0
523+
"###);
524+
}
464525
}

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ include = [
4949
# this one isn't discovered by maturin because it's behind a feature flag
5050
{ path = "crates/uv-performance-memory-allocator/**/*", format = ["sdist", "wheel"] },
5151
{ path = "crates/uv-performance-flate2-backend/**/*", format = ["sdist", "wheel"] },
52-
{ path = "crates/uv-trampoline/trampolines/*", format = "sdist" },
52+
{ path = "crates/uv-trampoline/trampolines/*", format = "sdist" },
5353
{ path = "LICENSE-APACHE", format = "sdist" },
5454
{ path = "LICENSE-MIT", format = "sdist" },
5555
]

0 commit comments

Comments
 (0)