Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Build backend: Add source tree -> source dist -> wheel tests #9091

Merged
merged 1 commit into from
Nov 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading