From 987d77863147f4347b15dc5a672233e5cebd9e7e Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Wed, 6 Nov 2024 11:40:23 -0500 Subject: [PATCH] Respect dynamic version updates in `uv lock` (#8867) ## Summary Closes https://github.com/astral-sh/uv/issues/8866. --- crates/uv-resolver/src/lock/mod.rs | 9 +++ crates/uv/tests/it/lock.rs | 122 +++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+) diff --git a/crates/uv-resolver/src/lock/mod.rs b/crates/uv-resolver/src/lock/mod.rs index 61ddbd508..08bed6149 100644 --- a/crates/uv-resolver/src/lock/mod.rs +++ b/crates/uv-resolver/src/lock/mod.rs @@ -1224,6 +1224,15 @@ impl Lock { } }; + // Validate the `version` metadata. + if metadata.version != package.id.version { + return Ok(SatisfiesResult::MismatchedVersion( + package.id.name.clone(), + package.id.version.clone(), + Some(metadata.version.clone()), + )); + } + // Validate the `requires-dist` metadata. { let expected: BTreeSet<_> = metadata diff --git a/crates/uv/tests/it/lock.rs b/crates/uv/tests/it/lock.rs index 938a4ebb9..2fd30cf16 100644 --- a/crates/uv/tests/it/lock.rs +++ b/crates/uv/tests/it/lock.rs @@ -17750,3 +17750,125 @@ fn lock_transitive_git() -> Result<()> { Ok(()) } + +/// Lock a package that's excluded from the parent workspace, but depends on that parent. +#[test] +fn lock_dynamic_version() -> Result<()> { + let context = TestContext::new("3.12"); + + let pyproject_toml = context.temp_dir.child("pyproject.toml"); + pyproject_toml.write_str( + r#" + [project] + name = "project" + requires-python = ">=3.12" + dynamic = ["version"] + + [build-system] + requires = ["setuptools"] + build-backend = "setuptools.build_meta" + + [tool.uv] + cache-keys = [{ file = "pyproject.toml" }, { file = "src/project/__init__.py" }] + + [tool.setuptools.dynamic] + version = { attr = "project.__version__" } + + [tool.setuptools] + package-dir = { "" = "src" } + + [tool.setuptools.packages.find] + where = ["src"] + "#, + )?; + + context + .temp_dir + .child("src") + .child("project") + .child("__init__.py") + .write_str("__version__ = '0.1.0'")?; + + uv_snapshot!(context.filters(), context.lock(), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 1 package in [TIME] + "###); + + let lock = fs_err::read_to_string(context.temp_dir.join("uv.lock")).unwrap(); + + insta::with_settings!({ + filters => context.filters(), + }, { + assert_snapshot!( + lock, @r###" + version = 1 + requires-python = ">=3.12" + + [options] + exclude-newer = "2024-03-25T00:00:00Z" + + [[package]] + name = "project" + version = "0.1.0" + source = { editable = "." } + "### + ); + }); + + // Bump the version. + context + .temp_dir + .child("src") + .child("project") + .child("__init__.py") + .write_str("__version__ = '0.1.1'")?; + + // Re-run with `--locked`. + uv_snapshot!(context.filters(), context.lock().arg("--locked"), @r###" + success: false + exit_code: 2 + ----- stdout ----- + + ----- stderr ----- + Resolved 1 package in [TIME] + error: The lockfile at `uv.lock` needs to be updated, but `--locked` was provided. To update the lockfile, run `uv lock`. + "###); + + // Re-lock. + uv_snapshot!(context.filters(), context.lock(), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 1 package in [TIME] + Updated project v0.1.0 -> v0.1.1 + "###); + + let lock = fs_err::read_to_string(context.temp_dir.join("uv.lock")).unwrap(); + + insta::with_settings!({ + filters => context.filters(), + }, { + assert_snapshot!( + lock, @r###" + version = 1 + requires-python = ">=3.12" + + [options] + exclude-newer = "2024-03-25T00:00:00Z" + + [[package]] + name = "project" + version = "0.1.1" + source = { editable = "." } + "### + ); + }); + + Ok(()) +}