Skip to content
Open
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
8 changes: 8 additions & 0 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ uv-small-str = { version = "0.0.40", path = "crates/uv-small-str" }
uv-state = { version = "0.0.40", path = "crates/uv-state" }
uv-static = { version = "0.0.40", path = "crates/uv-static" }
uv-test = { version = "0.0.40", path = "crates/uv-test" }
uv-toml = { version = "0.0.40", path = "crates/uv-toml" }
uv-tool = { version = "0.0.40", path = "crates/uv-tool" }
uv-torch = { version = "0.0.40", path = "crates/uv-torch" }
uv-trampoline-builder = { version = "0.0.40", path = "crates/uv-trampoline-builder" }
Expand Down Expand Up @@ -269,6 +270,7 @@ tokio-stream = { version = "0.1.16" }
tokio-util = { version = "0.7.12", features = ["compat", "io"] }
toml = { version = "1.1.0", features = ["fast_hash"] }
toml_edit = { version = "0.25.8", features = ["serde"] }
toml_parser = { version = "1.1.0" }
tracing = { version = "0.1.40" }
tracing-durations-export = { version = "0.3.0", features = ["plot"] }
tracing-subscriber = { version = "0.3.18" } # Default feature set for uv_build, uv activates extra features
Expand Down
1 change: 1 addition & 0 deletions crates/uv-build-backend/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ uv-pep508 = { workspace = true }
uv-platform-tags = { workspace = true }
uv-preview = { workspace = true }
uv-pypi-types = { workspace = true }
uv-toml = { workspace = true }
uv-warnings = { workspace = true }

base64 = { workspace = true }
Expand Down
91 changes: 91 additions & 0 deletions crates/uv-build-backend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1955,4 +1955,95 @@ mod tests {
build-backend = "uv_build"
"#);
}

/// Test that TOML 1.1 features in pyproject.toml trigger auto-detection and rewrite to TOML
/// 1.0, even without explicitly enabling `PreviewFeature::TomlBackwardsCompatibility`.
#[test]
fn toml_1_1_backward_compatibility_auto_detection() {
let _preview = uv_preview::test::with_features(&[]);
let src = TempDir::new().unwrap();

// A `pyproject.toml` with a TOML 1.1 feature, trailing commas in inline tables.
let pyproject_toml = indoc! {r#"
[project]
name = "toml11-project"
version = "0.1.0"
description = "A test package using TOML 1.1 features"
requires-python = ">=3.12"
# TOML 1.1 feature: Trailing comma in inline table
authors = [
{ name = "Ferris", email = "ferris@example.com", },
{ name = "Platypus", email = "platypus@example.com", },
]

[build-system]
requires = ["uv_build>=0.5.15,<0.6.0"]
build-backend = "uv_build"
"#};

fs_err::write(src.path().join("pyproject.toml"), pyproject_toml).unwrap();
fs_err::create_dir_all(src.path().join("src").join("toml11_project")).unwrap();
File::create(
src.path()
.join("src")
.join("toml11_project")
.join("__init__.py"),
)
.unwrap();

let dist = TempDir::new().unwrap();
let build = build(src.path(), dist.path()).unwrap();

// Check that both `pyproject.toml` and `pyproject.toml.orig` are in the sdist.
assert_snapshot!(build.source_dist_contents.join("\n"), @"
toml11_project-0.1.0/
toml11_project-0.1.0/PKG-INFO
toml11_project-0.1.0/pyproject.toml
toml11_project-0.1.0/pyproject.toml.orig
toml11_project-0.1.0/src
toml11_project-0.1.0/src/toml11_project
toml11_project-0.1.0/src/toml11_project/__init__.py
");

// Extract the sdist to verify the contents of both files.
let source_dist_path = dist.path().join(build.source_dist_filename.to_string());
let sdist_reader = BufReader::new(File::open(&source_dist_path).unwrap());
let mut source_dist = tar::Archive::new(GzDecoder::new(sdist_reader));

let mut pyproject_toml_content = String::new();
let mut pyproject_toml_orig_content = String::new();
for entry in source_dist.entries().unwrap() {
let mut entry = entry.unwrap();
let path = entry.path().unwrap().to_string_lossy().to_string();

if path.ends_with("pyproject.toml") && !path.eq_ignore_ascii_case(".orig") {
entry.read_to_string(&mut pyproject_toml_content).unwrap();
} else if path.ends_with("pyproject.toml.orig") {
entry
.read_to_string(&mut pyproject_toml_orig_content)
.unwrap();
}
}

assert_eq!(pyproject_toml_orig_content, pyproject_toml);
assert_snapshot!(pyproject_toml_content, @r#"
[project]
name = "toml11-project"
version = "0.1.0"
description = "A test package using TOML 1.1 features"
requires-python = ">=3.12"

[[project.authors]]
name = "Ferris"
email = "ferris@example.com"

[[project.authors]]
name = "Platypus"
email = "platypus@example.com"

[build-system]
requires = ["uv_build>=0.5.15,<0.6.0"]
build-backend = "uv_build"
"#);
}
}
22 changes: 19 additions & 3 deletions crates/uv-build-backend/src/source_dist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use uv_distribution_filename::{SourceDistExtension, SourceDistFilename};
use uv_fs::{Simplified, normalize_path};
use uv_globfilter::{GlobDirFilter, PortableGlobParser};
use uv_preview::PreviewFeature;
use uv_toml::has_toml11_features;
use uv_warnings::warn_user_once;
use walkdir::WalkDir;

Expand Down Expand Up @@ -236,11 +237,26 @@ fn write_source_dist(
//
// To work around this, we do a best-effort rewrite of `pyproject.toml` to TOML 1.0. We also
// add the original `pyproject.toml` as `pyproject.toml.orig` for reference.
//
// The feature is enabled either explicitly via the preview flag, or automatically when the
// `pyproject.toml` is detected to contain TOML 1.1-only syntax.
let pyproject_path = source_tree.join("pyproject.toml");
let pyproject_contents = fs_err::read_to_string(&pyproject_path)?;
let toml_backwards_compatibility =
uv_preview::is_enabled(PreviewFeature::TomlBackwardsCompatibility);
if uv_preview::is_enabled(PreviewFeature::TomlBackwardsCompatibility) {
true
} else if has_toml11_features(&pyproject_contents) {
warn_user_once!(
"`pyproject.toml` uses TOML 1.1 features; rewriting to TOML 1.0 for \
compatibility with older build tools. Use `--preview-feature \
{feature}` to suppress this warning.",
feature = PreviewFeature::TomlBackwardsCompatibility
);
true
} else {
false
};
if toml_backwards_compatibility {
let pyproject_path = source_tree.join("pyproject.toml");
let pyproject_contents = fs_err::read_to_string(&pyproject_path)?;
let pyproject_value: toml::Value = toml::from_str(&pyproject_contents)
.map_err(|err| Error::Toml(pyproject_path.clone(), err))?;
// See https://github.com/toml-rs/toml/issues/1088 for `to_string_pretty`.
Expand Down
19 changes: 19 additions & 0 deletions crates/uv-toml/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[package]
name = "uv-toml"
version = "0.0.40"
description = "This is an internal component crate of uv"
edition = { workspace = true }
rust-version = { workspace = true }
homepage = { workspace = true }
repository = { workspace = true }
authors = { workspace = true }
license = { workspace = true }

[lib]
doctest = false

[lints]
workspace = true

[dependencies]
toml_parser = { workspace = true }
13 changes: 13 additions & 0 deletions crates/uv-toml/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!-- This file is generated. DO NOT EDIT -->

# uv-fs

This crate is an internal component of [uv](https://crates.io/crates/uv). The Rust API exposed here
is unstable and will have frequent breaking changes.

This version (0.0.40) is a component of [uv 0.11.7](https://crates.io/crates/uv/0.11.7). The source
can be found [here](https://github.com/astral-sh/uv/blob/0.11.7/crates/uv-fs).

See uv's
[crate versioning policy](https://docs.astral.sh/uv/reference/policies/versioning/#crate-versioning)
for details on versioning.
Loading