Skip to content

Commit

Permalink
Build backend: Add source tree -> source dist -> wheel tests (#9091)
Browse files Browse the repository at this point in the history
A first milestone: source tree -> source dist -> wheel -> install works.
This PR adds a test for this.

There's obviously a lot still missing, including basics such as the
Readme inclusion.
  • Loading branch information
konstin authored Nov 14, 2024
1 parent 9a20f8c commit 3a7db17
Show file tree
Hide file tree
Showing 21 changed files with 325 additions and 69 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 5 additions & 7 deletions crates/uv-build-backend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -513,16 +513,15 @@ pub fn build_source_dist(
let relative = entry
.path()
.strip_prefix(source_tree)
.expect("walkdir starts with root")
.to_path_buf();
.expect("walkdir starts with root");

// Fast path: Don't descend into a directory that can't be included. This is the most
// important performance optimization, it avoids descending into directories such as
// `.venv`. While walkdir is generally cheap, we still avoid traversing large data
// directories that often exist on the top level of a project. This is especially noticeable
// on network file systems with high latencies per operation (while contiguous reading may
// still be fast).
include_matcher.match_directory(&relative) && !exclude_matcher.is_match(&relative)
include_matcher.match_directory(relative) && !exclude_matcher.is_match(relative)
}) {
let entry = entry.map_err(|err| Error::WalkDir {
root: source_tree.to_path_buf(),
Expand All @@ -532,15 +531,14 @@ pub fn build_source_dist(
let relative = entry
.path()
.strip_prefix(source_tree)
.expect("walkdir starts with root")
.to_path_buf();
.expect("walkdir starts with root");

if !include_matcher.match_path(&relative) || exclude_matcher.is_match(&relative) {
if !include_matcher.match_path(relative) || exclude_matcher.is_match(relative) {
trace!("Excluding {}", relative.user_display());
continue;
};

add_source_dist_entry(&mut tar, &entry, &top_level, &source_dist_path, &relative)?;
add_source_dist_entry(&mut tar, &entry, &top_level, &source_dist_path, relative)?;
}

tar.finish()
Expand Down
78 changes: 45 additions & 33 deletions crates/uv-build-backend/src/tests.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use super::*;
use indoc::indoc;
use insta::assert_snapshot;
use std::str::FromStr;
use tempfile::TempDir;
use uv_fs::copy_dir_all;
use uv_normalize::PackageName;
use uv_pep440::Version;

Expand All @@ -28,26 +30,34 @@ fn test_wheel() {
#[test]
fn test_record() {
let record = vec![RecordEntry {
path: "uv_backend/__init__.py".to_string(),
path: "built_by_uv/__init__.py".to_string(),
hash: "89f869e53a3a0061a52c0233e6442d4d72de80a8a2d3406d9ea0bfd397ed7865".to_string(),
size: 37,
}];

let mut writer = Vec::new();
write_record(&mut writer, "uv_backend-0.1.0", record).unwrap();
write_record(&mut writer, "built_by_uv-0.1.0", record).unwrap();
assert_snapshot!(String::from_utf8(writer).unwrap(), @r"
uv_backend/__init__.py,sha256=89f869e53a3a0061a52c0233e6442d4d72de80a8a2d3406d9ea0bfd397ed7865,37
uv_backend-0.1.0/RECORD,,
built_by_uv/__init__.py,sha256=89f869e53a3a0061a52c0233e6442d4d72de80a8a2d3406d9ea0bfd397ed7865,37
built_by_uv-0.1.0/RECORD,,
");
}

/// Check that we write deterministic wheels.
#[test]
fn test_determinism() {
let built_by_uv = Path::new("../../scripts/packages/built-by-uv");
let src = TempDir::new().unwrap();
for dir in ["src", "tests", "data-dir"] {
copy_dir_all(built_by_uv.join(dir), src.path().join(dir)).unwrap();
}
for dir in ["pyproject.toml", "README.md", "uv.lock"] {
fs_err::copy(built_by_uv.join(dir), src.path().join(dir)).unwrap();
}

let temp1 = TempDir::new().unwrap();
let uv_backend = Path::new("../../scripts/packages/uv_backend");
build_wheel(
uv_backend,
src.path(),
temp1.path(),
None,
WheelSettings::default(),
Expand All @@ -57,22 +67,26 @@ fn test_determinism() {

// Touch the file to check that we don't serialize the last modified date.
fs_err::write(
uv_backend.join("src/uv_backend/__init__.py"),
"def greet():\n print(\"Hello 👋\")\n",
src.path().join("src/built_by_uv/__init__.py"),
indoc! {r#"
def greet() -> str:
return "Hello 👋"
"#
},
)
.unwrap();

let temp2 = TempDir::new().unwrap();
build_wheel(
uv_backend,
src.path(),
temp2.path(),
None,
WheelSettings::default(),
"1.0.0+test",
)
.unwrap();

let wheel_filename = "uv_backend-0.1.0-py3-none-any.whl";
let wheel_filename = "built_by_uv-0.1.0-py3-none-any.whl";
assert_eq!(
fs_err::read(temp1.path().join(wheel_filename)).unwrap(),
fs_err::read(temp2.path().join(wheel_filename)).unwrap()
Expand All @@ -83,8 +97,8 @@ fn test_determinism() {
#[test]
fn test_prepare_metadata() {
let metadata_dir = TempDir::new().unwrap();
let uv_backend = Path::new("../../scripts/packages/uv_backend");
metadata(uv_backend, metadata_dir.path(), "1.0.0+test").unwrap();
let built_by_uv = Path::new("../../scripts/packages/built-by-uv");
metadata(built_by_uv, metadata_dir.path(), "1.0.0+test").unwrap();

let mut files: Vec<_> = WalkDir::new(metadata_dir.path())
.into_iter()
Expand All @@ -101,38 +115,36 @@ fn test_prepare_metadata() {
.collect();
files.sort();
assert_snapshot!(files.join("\n"), @r"
uv_backend-0.1.0.dist-info
uv_backend-0.1.0.dist-info/METADATA
uv_backend-0.1.0.dist-info/RECORD
uv_backend-0.1.0.dist-info/WHEEL
built_by_uv-0.1.0.dist-info
built_by_uv-0.1.0.dist-info/METADATA
built_by_uv-0.1.0.dist-info/RECORD
built_by_uv-0.1.0.dist-info/WHEEL
");

let metadata_file = metadata_dir
.path()
.join("uv_backend-0.1.0.dist-info/METADATA");
.join("built_by_uv-0.1.0.dist-info/METADATA");
assert_snapshot!(fs_err::read_to_string(metadata_file).unwrap(), @r###"
Metadata-Version: 2.3
Name: uv-backend
Version: 0.1.0
Summary: Add your description here
Requires-Python: >=3.12
Description-Content-Type: text/markdown
# uv_backend
A simple package to be built with the uv build backend.
"###);
Metadata-Version: 2.3
Name: built-by-uv
Version: 0.1.0
Summary: A package to be built with the uv build backend that uses all features exposed by the build backend
Requires-Dist: anyio>=4,<5
Requires-Python: >=3.12
"###);

let record_file = metadata_dir
.path()
.join("uv_backend-0.1.0.dist-info/RECORD");
.join("built_by_uv-0.1.0.dist-info/RECORD");
assert_snapshot!(fs_err::read_to_string(record_file).unwrap(), @r###"
uv_backend-0.1.0.dist-info/WHEEL,sha256=3da1bfa0e8fd1b6cc246aa0b2b44a35815596c600cb485c39a6f8c106c3d5a8d,83
uv_backend-0.1.0.dist-info/METADATA,sha256=e4a0d390317d7182f65ea978254c71ed283e0a4242150cf1c99a694b113ff68d,224
uv_backend-0.1.0.dist-info/RECORD,,
built_by_uv-0.1.0.dist-info/WHEEL,sha256=3da1bfa0e8fd1b6cc246aa0b2b44a35815596c600cb485c39a6f8c106c3d5a8d,83
built_by_uv-0.1.0.dist-info/METADATA,sha256=ec36b5ae8830bdd248e90aaf581483ffb057f9a2d0f41e19e585531e7d07c9dc,215
built_by_uv-0.1.0.dist-info/RECORD,,
"###);

let wheel_file = metadata_dir.path().join("uv_backend-0.1.0.dist-info/WHEEL");
let wheel_file = metadata_dir
.path()
.join("built_by_uv-0.1.0.dist-info/WHEEL");
assert_snapshot!(fs_err::read_to_string(wheel_file).unwrap(), @r###"
Wheel-Version: 1.0
Generator: uv 1.0.0+test
Expand Down
10 changes: 4 additions & 6 deletions crates/uv-globfilter/src/glob_dir_filter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,20 +238,18 @@ mod tests {
let relative = entry
.path()
.strip_prefix(walkdir_root)
.expect("walkdir starts with root")
.to_path_buf();
.expect("walkdir starts with root");

include_matcher.match_directory(&relative)
include_matcher.match_directory(relative)
})
.filter_map(|entry| {
let entry = entry.as_ref().unwrap();
// TODO(konsti): This should be prettier.
let relative = entry
.path()
.strip_prefix(walkdir_root)
.expect("walkdir starts with root")
.to_path_buf();
if include_matcher.match_path(&relative) {
.expect("walkdir starts with root");
if include_matcher.match_path(relative) {
// Translate windows paths back to the unix fixture
Some(relative.to_str().unwrap().replace(MAIN_SEPARATOR, "/"))
} else {
Expand Down
2 changes: 2 additions & 0 deletions crates/uv/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -105,13 +105,15 @@ base64 = { version = "0.22.1" }
byteorder = { version = "1.5.0" }
etcetera = { workspace = true }
filetime = { version = "0.2.25" }
flate2 = { workspace = true }
ignore = { version = "0.4.23" }
indoc = { version = "2.0.5" }
insta = { version = "1.40.0", features = ["filters", "json"] }
predicates = { version = "3.1.2" }
regex = { workspace = true }
reqwest = { workspace = true, features = ["blocking"], default-features = false }
similar = { version = "2.6.0" }
tar = { workspace = true }
tempfile = { workspace = true }
zip = { workspace = true }

Expand Down
102 changes: 96 additions & 6 deletions crates/uv/tests/it/build_backend.rs
Original file line number Diff line number Diff line change
@@ -1,51 +1,141 @@
use crate::common::{uv_snapshot, TestContext};
use anyhow::Result;
use assert_cmd::assert::OutputAssertExt;
use flate2::bufread::GzDecoder;
use fs_err::File;
use indoc::indoc;
use std::env;
use std::io::BufReader;
use std::path::Path;
use tempfile::TempDir;
use uv_static::EnvVars;

const BUILT_BY_UV_TEST_SCRIPT: &str = indoc! {r#"
from built_by_uv import greet
from built_by_uv.arithmetic.circle import area
print(greet())
print(f"Area of a circle with r=2: {area(2)}")
"#};

/// Test that build backend works if we invoke it directly.
///
/// We can't test end-to-end here including the PEP 517 bridge code since we don't have a uv wheel.
#[test]
fn uv_backend_direct() -> Result<()> {
fn built_by_uv_direct_wheel() -> Result<()> {
let context = TestContext::new("3.12");
let uv_backend = Path::new("../../scripts/packages/uv_backend");
let built_by_uv = Path::new("../../scripts/packages/built-by-uv");

let temp_dir = TempDir::new()?;

uv_snapshot!(context
.build_backend()
.arg("build-wheel")
.arg(temp_dir.path())
.current_dir(uv_backend), @r###"
.current_dir(built_by_uv), @r###"
success: true
exit_code: 0
----- stdout -----
built_by_uv-0.1.0-py3-none-any.whl
----- stderr -----
"###);

context
.pip_install()
.arg(temp_dir.path().join("built_by_uv-0.1.0-py3-none-any.whl"))
.assert()
.success();

uv_snapshot!(context
.run()
.arg("python")
.arg("-c")
.arg(BUILT_BY_UV_TEST_SCRIPT)
// Python on windows
.env(EnvVars::PYTHONUTF8, "1"), @r###"
success: true
exit_code: 0
----- stdout -----
Hello 👋
Area of a circle with r=2: 12.56636
----- stderr -----
"###);

Ok(())
}

/// Test that source tree -> source dist -> wheel works.
///
/// We can't test end-to-end here including the PEP 517 bridge code since we don't have a uv wheel,
/// so we call the build backend directly.
#[test]
fn built_by_uv_direct() -> Result<()> {
let context = TestContext::new("3.12");
let built_by_uv = Path::new("../../scripts/packages/built-by-uv");

let sdist_dir = TempDir::new()?;

uv_snapshot!(context
.build_backend()
.arg("build-sdist")
.arg(sdist_dir.path())
.current_dir(built_by_uv), @r###"
success: true
exit_code: 0
----- stdout -----
uv_backend-0.1.0-py3-none-any.whl
built_by_uv-0.1.0.tar.gz
----- stderr -----
"###);

let sdist_tree = TempDir::new()?;

let sdist_reader = BufReader::new(File::open(
sdist_dir.path().join("built_by_uv-0.1.0.tar.gz"),
)?);
tar::Archive::new(GzDecoder::new(sdist_reader)).unpack(sdist_tree.path())?;

drop(sdist_dir);

let wheel_dir = TempDir::new()?;

uv_snapshot!(context
.build_backend()
.arg("build-wheel")
.arg(wheel_dir.path())
.current_dir(sdist_tree.path().join("built-by-uv-0.1.0")), @r###"
success: true
exit_code: 0
----- stdout -----
built_by_uv-0.1.0-py3-none-any.whl
----- stderr -----
"###);

drop(sdist_tree);

context
.pip_install()
.arg(temp_dir.path().join("uv_backend-0.1.0-py3-none-any.whl"))
.arg(wheel_dir.path().join("built_by_uv-0.1.0-py3-none-any.whl"))
.assert()
.success();

drop(wheel_dir);

uv_snapshot!(context
.run()
.arg("python")
.arg("-c")
.arg("import uv_backend\nuv_backend.greet()")
.arg(BUILT_BY_UV_TEST_SCRIPT)
// Python on windows
.env(EnvVars::PYTHONUTF8, "1"), @r###"
success: true
exit_code: 0
----- stdout -----
Hello 👋
Area of a circle with r=2: 12.56636
----- stderr -----
"###);
Expand Down
2 changes: 2 additions & 0 deletions scripts/packages/built-by-uv/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/dist/
/build-root/
3 changes: 3 additions & 0 deletions scripts/packages/built-by-uv/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# built_by_uv

A package to be built with the uv build backend that uses all features exposed by the build backend.
1 change: 1 addition & 0 deletions scripts/packages/built-by-uv/data-dir/dont-include-me.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
We don't want this file in the source dist or wheel.
Loading

0 comments on commit 3a7db17

Please sign in to comment.