Skip to content
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
10 changes: 5 additions & 5 deletions crates/uv-resolver/src/lock/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1183,12 +1183,12 @@ impl Lock {
match setting {
ExcludeNewerOverride::Enabled(exclude_newer_value) => {
if let Some(span) = exclude_newer_value.span() {
// Serialize as inline table with timestamp and span
// When a relative span is present, write a no-op timestamp
// for backwards compatibility. This matches treatment for
// the global `exclude-newer`.
let mut inline = toml_edit::InlineTable::new();
inline.insert(
"timestamp",
exclude_newer_value.timestamp().to_string().into(),
);
inline
.insert("timestamp", ExcludeNewerValue::PLACEHOLDER.into());
inline.insert("span", span.to_string().into());
package_table.insert(name.as_ref(), Item::Value(inline.into()));
} else {
Expand Down
77 changes: 69 additions & 8 deletions crates/uv/tests/it/lock_exclude_newer_relative.rs
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ fn lock_exclude_newer_package_relative() -> Result<()> {
[options]

[options.exclude-newer-package]
idna = { timestamp = "2024-04-10T00:00:00Z", span = "P3W" }
idna = { timestamp = "0001-01-01T00:00:00Z", span = "P3W" }

[[package]]
name = "idna"
Expand Down Expand Up @@ -442,7 +442,7 @@ fn lock_exclude_newer_package_relative() -> Result<()> {
[options]

[options.exclude-newer-package]
idna = { timestamp = "2024-04-17T00:00:00Z", span = "P2W" }
idna = { timestamp = "0001-01-01T00:00:00Z", span = "P2W" }

[[package]]
name = "idna"
Expand Down Expand Up @@ -492,7 +492,7 @@ fn lock_exclude_newer_package_relative() -> Result<()> {
[options]

[options.exclude-newer-package]
idna = { timestamp = "2024-04-17T00:00:00Z", span = "P2W" }
idna = { timestamp = "0001-01-01T00:00:00Z", span = "P2W" }

[[package]]
name = "idna"
Expand Down Expand Up @@ -635,7 +635,7 @@ fn lock_exclude_newer_package_relative_pyproject() -> Result<()> {
[options]

[options.exclude-newer-package]
idna = { timestamp = "2024-04-10T00:00:00Z", span = "P3W" }
idna = { timestamp = "0001-01-01T00:00:00Z", span = "P3W" }

[[package]]
name = "idna"
Expand Down Expand Up @@ -720,7 +720,7 @@ fn lock_exclude_newer_relative_global_and_package() -> Result<()> {
exclude-newer-span = "P3W"

[options.exclude-newer-package]
typing-extensions = { timestamp = "2024-04-17T00:00:00Z", span = "P2W" }
typing-extensions = { timestamp = "0001-01-01T00:00:00Z", span = "P2W" }

[[package]]
name = "idna"
Expand Down Expand Up @@ -844,7 +844,7 @@ fn lock_exclude_newer_relative_global_and_package() -> Result<()> {
exclude-newer = "2024-05-20T00:00:00Z"

[options.exclude-newer-package]
typing-extensions = { timestamp = "2024-04-17T00:00:00Z", span = "P2W" }
typing-extensions = { timestamp = "0001-01-01T00:00:00Z", span = "P2W" }

[[package]]
name = "idna"
Expand Down Expand Up @@ -1353,7 +1353,7 @@ fn lock_exclude_newer_package_relative_no_timestamp_in_lockfile() -> Result<()>
[options]

[options.exclude-newer-package]
idna = { timestamp = "2024-04-10T00:00:00Z", span = "P3W" }
idna = { timestamp = "0001-01-01T00:00:00Z", span = "P3W" }

[[package]]
name = "idna"
Expand All @@ -1378,7 +1378,7 @@ fn lock_exclude_newer_package_relative_no_timestamp_in_lockfile() -> Result<()>

// Manually remove the per-package exclude-newer timestamp from the lockfile, leaving the span.
let lock = lock.replace(
"idna = { timestamp = \"2024-04-10T00:00:00Z\", span = \"P3W\" }",
"idna = { timestamp = \"0001-01-01T00:00:00Z\", span = \"P3W\" }",
"idna = { span = \"P3W\" }",
);
context.temp_dir.child("uv.lock").write_str(&lock)?;
Expand Down Expand Up @@ -1532,3 +1532,64 @@ fn lock_exclude_newer_relative_values_pyproject() -> Result<()> {

Ok(())
}

/// When a relative span is configured for `exclude-newer-package`, the lockfile
/// should use a fixed no-op sentinel for the stored timestamp.
#[test]
fn lock_exclude_newer_package_relative_noop_timestamp() -> Result<()> {
let context = uv_test::test_context!("3.12");
let pyproject_toml = context.temp_dir.child("pyproject.toml");
pyproject_toml.write_str(
r#"
[project]
name = "project"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["idna"]
"#,
)?;

let current_timestamp = "2024-05-01T00:00:00Z";
uv_snapshot!(context.filters(), context
.lock()
.env_remove(EnvVars::UV_EXCLUDE_NEWER)
.env(EnvVars::UV_TEST_CURRENT_TIMESTAMP, current_timestamp)
.arg("--exclude-newer-package")
.arg("idna=3 weeks"), @"
success: true
exit_code: 0
----- stdout -----

----- stderr -----
Resolved 2 packages in [TIME]
");

// The lockfile stores the no-op sentinel timestamp alongside the span.
let lock = context.read("uv.lock");
assert!(
lock.contains(r#"idna = { timestamp = "0001-01-01T00:00:00Z", span = "P3W" }"#),
"expected no-op sentinel in lockfile, got:\n{lock}"
);

// Locking again at a later time should yield an identical lockfile, even
// without `--locked`, because the span is unchanged and the stored
// timestamp is a fixed sentinel.
let later_timestamp = "2024-06-01T00:00:00Z";
uv_snapshot!(context.filters(), context
.lock()
.env_remove(EnvVars::UV_EXCLUDE_NEWER)
.env(EnvVars::UV_TEST_CURRENT_TIMESTAMP, later_timestamp)
.arg("--exclude-newer-package")
.arg("idna=3 weeks"), @"
success: true
exit_code: 0
----- stdout -----

----- stderr -----
Resolved 2 packages in [TIME]
");

assert_eq!(context.read("uv.lock"), lock);

Ok(())
}
Loading