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

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.
This commit is contained in:
konsti 2024-11-14 20:15:32 +01:00 committed by GitHub
parent 9a20f8c7b7
commit 3a7db17147
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 325 additions and 69 deletions

1
Cargo.lock generated
View file

@ -4407,6 +4407,7 @@ dependencies = [
"serde", "serde",
"serde_json", "serde_json",
"similar", "similar",
"tar",
"tempfile", "tempfile",
"textwrap", "textwrap",
"thiserror", "thiserror",

View file

@ -513,8 +513,7 @@ pub fn build_source_dist(
let relative = entry let relative = entry
.path() .path()
.strip_prefix(source_tree) .strip_prefix(source_tree)
.expect("walkdir starts with root") .expect("walkdir starts with root");
.to_path_buf();
// Fast path: Don't descend into a directory that can't be included. This is the most // 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 // important performance optimization, it avoids descending into directories such as
@ -522,7 +521,7 @@ pub fn build_source_dist(
// directories that often exist on the top level of a project. This is especially noticeable // 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 // on network file systems with high latencies per operation (while contiguous reading may
// still be fast). // 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 { let entry = entry.map_err(|err| Error::WalkDir {
root: source_tree.to_path_buf(), root: source_tree.to_path_buf(),
@ -532,15 +531,14 @@ pub fn build_source_dist(
let relative = entry let relative = entry
.path() .path()
.strip_prefix(source_tree) .strip_prefix(source_tree)
.expect("walkdir starts with root") .expect("walkdir starts with root");
.to_path_buf();
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()); trace!("Excluding {}", relative.user_display());
continue; 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() tar.finish()

View file

@ -1,7 +1,9 @@
use super::*; use super::*;
use indoc::indoc;
use insta::assert_snapshot; use insta::assert_snapshot;
use std::str::FromStr; use std::str::FromStr;
use tempfile::TempDir; use tempfile::TempDir;
use uv_fs::copy_dir_all;
use uv_normalize::PackageName; use uv_normalize::PackageName;
use uv_pep440::Version; use uv_pep440::Version;
@ -28,26 +30,34 @@ fn test_wheel() {
#[test] #[test]
fn test_record() { fn test_record() {
let record = vec![RecordEntry { let record = vec![RecordEntry {
path: "uv_backend/__init__.py".to_string(), path: "built_by_uv/__init__.py".to_string(),
hash: "89f869e53a3a0061a52c0233e6442d4d72de80a8a2d3406d9ea0bfd397ed7865".to_string(), hash: "89f869e53a3a0061a52c0233e6442d4d72de80a8a2d3406d9ea0bfd397ed7865".to_string(),
size: 37, size: 37,
}]; }];
let mut writer = Vec::new(); 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" assert_snapshot!(String::from_utf8(writer).unwrap(), @r"
uv_backend/__init__.py,sha256=89f869e53a3a0061a52c0233e6442d4d72de80a8a2d3406d9ea0bfd397ed7865,37 built_by_uv/__init__.py,sha256=89f869e53a3a0061a52c0233e6442d4d72de80a8a2d3406d9ea0bfd397ed7865,37
uv_backend-0.1.0/RECORD,, built_by_uv-0.1.0/RECORD,,
"); ");
} }
/// Check that we write deterministic wheels. /// Check that we write deterministic wheels.
#[test] #[test]
fn test_determinism() { 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 temp1 = TempDir::new().unwrap();
let uv_backend = Path::new("../../scripts/packages/uv_backend");
build_wheel( build_wheel(
uv_backend, src.path(),
temp1.path(), temp1.path(),
None, None,
WheelSettings::default(), WheelSettings::default(),
@ -57,14 +67,18 @@ fn test_determinism() {
// Touch the file to check that we don't serialize the last modified date. // Touch the file to check that we don't serialize the last modified date.
fs_err::write( fs_err::write(
uv_backend.join("src/uv_backend/__init__.py"), src.path().join("src/built_by_uv/__init__.py"),
"def greet():\n print(\"Hello 👋\")\n", indoc! {r#"
def greet() -> str:
return "Hello 👋"
"#
},
) )
.unwrap(); .unwrap();
let temp2 = TempDir::new().unwrap(); let temp2 = TempDir::new().unwrap();
build_wheel( build_wheel(
uv_backend, src.path(),
temp2.path(), temp2.path(),
None, None,
WheelSettings::default(), WheelSettings::default(),
@ -72,7 +86,7 @@ fn test_determinism() {
) )
.unwrap(); .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!( assert_eq!(
fs_err::read(temp1.path().join(wheel_filename)).unwrap(), fs_err::read(temp1.path().join(wheel_filename)).unwrap(),
fs_err::read(temp2.path().join(wheel_filename)).unwrap() fs_err::read(temp2.path().join(wheel_filename)).unwrap()
@ -83,8 +97,8 @@ fn test_determinism() {
#[test] #[test]
fn test_prepare_metadata() { fn test_prepare_metadata() {
let metadata_dir = TempDir::new().unwrap(); let metadata_dir = TempDir::new().unwrap();
let uv_backend = Path::new("../../scripts/packages/uv_backend"); let built_by_uv = Path::new("../../scripts/packages/built-by-uv");
metadata(uv_backend, metadata_dir.path(), "1.0.0+test").unwrap(); metadata(built_by_uv, metadata_dir.path(), "1.0.0+test").unwrap();
let mut files: Vec<_> = WalkDir::new(metadata_dir.path()) let mut files: Vec<_> = WalkDir::new(metadata_dir.path())
.into_iter() .into_iter()
@ -101,38 +115,36 @@ fn test_prepare_metadata() {
.collect(); .collect();
files.sort(); files.sort();
assert_snapshot!(files.join("\n"), @r" assert_snapshot!(files.join("\n"), @r"
uv_backend-0.1.0.dist-info built_by_uv-0.1.0.dist-info
uv_backend-0.1.0.dist-info/METADATA built_by_uv-0.1.0.dist-info/METADATA
uv_backend-0.1.0.dist-info/RECORD built_by_uv-0.1.0.dist-info/RECORD
uv_backend-0.1.0.dist-info/WHEEL built_by_uv-0.1.0.dist-info/WHEEL
"); ");
let metadata_file = metadata_dir let metadata_file = metadata_dir
.path() .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###" assert_snapshot!(fs_err::read_to_string(metadata_file).unwrap(), @r###"
Metadata-Version: 2.3 Metadata-Version: 2.3
Name: uv-backend Name: built-by-uv
Version: 0.1.0 Version: 0.1.0
Summary: Add your description here Summary: A package to be built with the uv build backend that uses all features exposed by the build backend
Requires-Python: >=3.12 Requires-Dist: anyio>=4,<5
Description-Content-Type: text/markdown Requires-Python: >=3.12
"###);
# uv_backend
A simple package to be built with the uv build backend.
"###);
let record_file = metadata_dir let record_file = metadata_dir
.path() .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###" assert_snapshot!(fs_err::read_to_string(record_file).unwrap(), @r###"
uv_backend-0.1.0.dist-info/WHEEL,sha256=3da1bfa0e8fd1b6cc246aa0b2b44a35815596c600cb485c39a6f8c106c3d5a8d,83 built_by_uv-0.1.0.dist-info/WHEEL,sha256=3da1bfa0e8fd1b6cc246aa0b2b44a35815596c600cb485c39a6f8c106c3d5a8d,83
uv_backend-0.1.0.dist-info/METADATA,sha256=e4a0d390317d7182f65ea978254c71ed283e0a4242150cf1c99a694b113ff68d,224 built_by_uv-0.1.0.dist-info/METADATA,sha256=ec36b5ae8830bdd248e90aaf581483ffb057f9a2d0f41e19e585531e7d07c9dc,215
uv_backend-0.1.0.dist-info/RECORD,, 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###" assert_snapshot!(fs_err::read_to_string(wheel_file).unwrap(), @r###"
Wheel-Version: 1.0 Wheel-Version: 1.0
Generator: uv 1.0.0+test Generator: uv 1.0.0+test

View file

@ -238,10 +238,9 @@ mod tests {
let relative = entry let relative = entry
.path() .path()
.strip_prefix(walkdir_root) .strip_prefix(walkdir_root)
.expect("walkdir starts with root") .expect("walkdir starts with root");
.to_path_buf();
include_matcher.match_directory(&relative) include_matcher.match_directory(relative)
}) })
.filter_map(|entry| { .filter_map(|entry| {
let entry = entry.as_ref().unwrap(); let entry = entry.as_ref().unwrap();
@ -249,9 +248,8 @@ mod tests {
let relative = entry let relative = entry
.path() .path()
.strip_prefix(walkdir_root) .strip_prefix(walkdir_root)
.expect("walkdir starts with root") .expect("walkdir starts with root");
.to_path_buf(); if include_matcher.match_path(relative) {
if include_matcher.match_path(&relative) {
// Translate windows paths back to the unix fixture // Translate windows paths back to the unix fixture
Some(relative.to_str().unwrap().replace(MAIN_SEPARATOR, "/")) Some(relative.to_str().unwrap().replace(MAIN_SEPARATOR, "/"))
} else { } else {

View file

@ -105,6 +105,7 @@ base64 = { version = "0.22.1" }
byteorder = { version = "1.5.0" } byteorder = { version = "1.5.0" }
etcetera = { workspace = true } etcetera = { workspace = true }
filetime = { version = "0.2.25" } filetime = { version = "0.2.25" }
flate2 = { workspace = true }
ignore = { version = "0.4.23" } ignore = { version = "0.4.23" }
indoc = { version = "2.0.5" } indoc = { version = "2.0.5" }
insta = { version = "1.40.0", features = ["filters", "json"] } insta = { version = "1.40.0", features = ["filters", "json"] }
@ -112,6 +113,7 @@ predicates = { version = "3.1.2" }
regex = { workspace = true } regex = { workspace = true }
reqwest = { workspace = true, features = ["blocking"], default-features = false } reqwest = { workspace = true, features = ["blocking"], default-features = false }
similar = { version = "2.6.0" } similar = { version = "2.6.0" }
tar = { workspace = true }
tempfile = { workspace = true } tempfile = { workspace = true }
zip = { workspace = true } zip = { workspace = true }

View file

@ -1,18 +1,30 @@
use crate::common::{uv_snapshot, TestContext}; use crate::common::{uv_snapshot, TestContext};
use anyhow::Result; use anyhow::Result;
use assert_cmd::assert::OutputAssertExt; use assert_cmd::assert::OutputAssertExt;
use flate2::bufread::GzDecoder;
use fs_err::File;
use indoc::indoc;
use std::env; use std::env;
use std::io::BufReader;
use std::path::Path; use std::path::Path;
use tempfile::TempDir; use tempfile::TempDir;
use uv_static::EnvVars; 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. /// 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. /// We can't test end-to-end here including the PEP 517 bridge code since we don't have a uv wheel.
#[test] #[test]
fn uv_backend_direct() -> Result<()> { fn built_by_uv_direct_wheel() -> Result<()> {
let context = TestContext::new("3.12"); 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()?; let temp_dir = TempDir::new()?;
@ -20,18 +32,18 @@ fn uv_backend_direct() -> Result<()> {
.build_backend() .build_backend()
.arg("build-wheel") .arg("build-wheel")
.arg(temp_dir.path()) .arg(temp_dir.path())
.current_dir(uv_backend), @r###" .current_dir(built_by_uv), @r###"
success: true success: true
exit_code: 0 exit_code: 0
----- stdout ----- ----- stdout -----
uv_backend-0.1.0-py3-none-any.whl built_by_uv-0.1.0-py3-none-any.whl
----- stderr ----- ----- stderr -----
"###); "###);
context context
.pip_install() .pip_install()
.arg(temp_dir.path().join("uv_backend-0.1.0-py3-none-any.whl")) .arg(temp_dir.path().join("built_by_uv-0.1.0-py3-none-any.whl"))
.assert() .assert()
.success(); .success();
@ -39,13 +51,91 @@ fn uv_backend_direct() -> Result<()> {
.run() .run()
.arg("python") .arg("python")
.arg("-c") .arg("-c")
.arg("import uv_backend\nuv_backend.greet()") .arg(BUILT_BY_UV_TEST_SCRIPT)
// Python on windows // Python on windows
.env(EnvVars::PYTHONUTF8, "1"), @r###" .env(EnvVars::PYTHONUTF8, "1"), @r###"
success: true success: true
exit_code: 0 exit_code: 0
----- stdout ----- ----- stdout -----
Hello 👋 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 -----
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(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(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 ----- ----- stderr -----
"###); "###);

View file

@ -0,0 +1,2 @@
/dist/
/build-root/

View file

@ -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.

View file

@ -0,0 +1 @@
We don't want this file in the source dist or wheel.

View file

@ -0,0 +1,11 @@
[project]
name = "built-by-uv"
version = "0.1.0"
description = "A package to be built with the uv build backend that uses all features exposed by the build backend"
#rreadme = "README.md"
requires-python = ">=3.12"
dependencies = ["anyio>=4,<5"]
[build-system]
requires = ["uv>=0.4.15,<5"]
build-backend = "uv"

View file

@ -0,0 +1,2 @@
def greet() -> str:
return "Hello 👋"

View file

@ -0,0 +1,3 @@
from .circle import area as circle_area
__all__ = ["circle_area"]

View file

@ -0,0 +1,12 @@
from functools import lru_cache
from pathlib import Path
@lru_cache(maxsize=1)
def pi() -> float:
return float(Path(__file__).parent.joinpath("pi.txt").read_text().strip())
def area(radius: float) -> float:
"""Use a non-Python file (`pi.txt`)."""
return radius**2 * pi()

View file

@ -0,0 +1 @@
3.14159

View file

@ -0,0 +1,23 @@
#!/bin/bash
# Test source tree -> source dist -> wheel and run pytest after.
# We can't test this through the build backend setting directly since we want
# to use the debug build of uv, so we use the internal API instead.
set -e
cargo build
uv venv -p 3.12 -q
mkdir -p dist
rm -f dist/*
../../../target/debug/uv build-backend build-sdist dist/
rm -rf build-root
mkdir build-root
cd build-root
tar -tvf ../dist/built_by_uv-0.1.0.tar.gz
tar xf ../dist/built_by_uv-0.1.0.tar.gz
cd built-by-uv-0.1.0
../../../../../target/debug/uv build-backend build-wheel ../../dist
unzip -l ../../dist/built_by_uv-0.1.0-py3-none-any.whl
cd ../..
uv pip install -q pytest dist/built_by_uv-0.1.0-py3-none-any.whl
pytest

View file

@ -0,0 +1,11 @@
import pytest
from built_by_uv import greet
from built_by_uv.arithmetic.circle import area
def test_circle():
assert area(2) == pytest.approx(12.56636)
def test_greet():
assert greet() == "Hello 👋"

103
scripts/packages/built-by-uv/uv.lock generated Normal file
View file

@ -0,0 +1,103 @@
version = 1
requires-python = ">=3.12"
[[package]]
name = "anyio"
version = "4.6.2.post1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "idna" },
{ name = "sniffio" },
]
sdist = { url = "https://files.pythonhosted.org/packages/9f/09/45b9b7a6d4e45c6bcb5bf61d19e3ab87df68e0601fa8c5293de3542546cc/anyio-4.6.2.post1.tar.gz", hash = "sha256:4c8bc31ccdb51c7f7bd251f51c609e038d63e34219b44aa86e47576389880b4c", size = 173422 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e4/f5/f2b75d2fc6f1a260f340f0e7c6a060f4dd2961cc16884ed851b0d18da06a/anyio-4.6.2.post1-py3-none-any.whl", hash = "sha256:6d170c36fba3bdd840c73d3868c1e777e33676a69c3a72cf0a0d5d6d8009b61d", size = 90377 },
]
[[package]]
name = "built-by-uv"
version = "0.1.0"
source = { editable = "." }
dependencies = [
{ name = "anyio" },
]
[package.dev-dependencies]
dev = [
{ name = "pytest" },
]
[package.metadata]
requires-dist = [{ name = "anyio", specifier = ">=4,<5" }]
[package.metadata.requires-dev]
dev = [{ name = "pytest", specifier = ">=8.3.3" }]
[[package]]
name = "colorama"
version = "0.4.6"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 },
]
[[package]]
name = "idna"
version = "3.10"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 },
]
[[package]]
name = "iniconfig"
version = "2.0.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 },
]
[[package]]
name = "packaging"
version = "24.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 },
]
[[package]]
name = "pluggy"
version = "1.5.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 },
]
[[package]]
name = "pytest"
version = "8.3.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
{ name = "iniconfig" },
{ name = "packaging" },
{ name = "pluggy" },
]
sdist = { url = "https://files.pythonhosted.org/packages/8b/6c/62bbd536103af674e227c41a8f3dcd022d591f6eed5facb5a0f31ee33bbc/pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181", size = 1442487 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/6b/77/7440a06a8ead44c7757a64362dd22df5760f9b12dc5f11b6188cd2fc27a0/pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2", size = 342341 },
]
[[package]]
name = "sniffio"
version = "1.3.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 },
]

View file

@ -1 +0,0 @@
dist/

View file

@ -1,3 +0,0 @@
# uv_backend
A simple package to be built with the uv build backend.

View file

@ -1,11 +0,0 @@
[project]
name = "uv-backend"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = []
[build-system]
requires = ["uv>=0.4.15,<5"]
build-backend = "uv"

View file

@ -1,2 +0,0 @@
def greet():
print("Hello 👋")