Skip to content

Commit

Permalink
Mark metadata as dynamic when reading from built wheel cache
Browse files Browse the repository at this point in the history
  • Loading branch information
charliermarsh committed Jan 29, 2025
1 parent a9f3552 commit e6a1723
Show file tree
Hide file tree
Showing 2 changed files with 143 additions and 15 deletions.
39 changes: 24 additions & 15 deletions crates/uv-distribution/src/source/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1187,10 +1187,19 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
.await?
.filter(|metadata| metadata.matches(source.name(), source.version()))
{
debug!("Using cached metadata for: {source}");
// If necessary, mark the metadata as dynamic.
let metadata = if dynamic {
ResolutionMetadata {
dynamic: true,
..metadata.into()
}
} else {
metadata.into()
};

return Ok(ArchiveMetadata::from(
Metadata::from_workspace(
metadata.into(),
metadata,
resource.install_path.as_ref(),
None,
self.build_context.locations(),
Expand All @@ -1212,6 +1221,14 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
.boxed_local()
.await?
{
// Store the metadata.
fs::create_dir_all(metadata_entry.dir())
.await
.map_err(Error::CacheWrite)?;
write_atomic(metadata_entry.path(), rmp_serde::to_vec(&metadata)?)
.await
.map_err(Error::CacheWrite)?;

// If necessary, mark the metadata as dynamic.
let metadata = if dynamic {
ResolutionMetadata {
Expand All @@ -1222,14 +1239,6 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
metadata
};

// Store the metadata.
fs::create_dir_all(metadata_entry.dir())
.await
.map_err(Error::CacheWrite)?;
write_atomic(metadata_entry.path(), rmp_serde::to_vec(&metadata)?)
.await
.map_err(Error::CacheWrite)?;

return Ok(ArchiveMetadata::from(
Metadata::from_workspace(
metadata,
Expand Down Expand Up @@ -1273,6 +1282,11 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
}
}

// Store the metadata.
write_atomic(metadata_entry.path(), rmp_serde::to_vec(&metadata)?)
.await
.map_err(Error::CacheWrite)?;

// If necessary, mark the metadata as dynamic.
let metadata = if dynamic {
ResolutionMetadata {
Expand All @@ -1283,11 +1297,6 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
metadata
};

// Store the metadata.
write_atomic(metadata_entry.path(), rmp_serde::to_vec(&metadata)?)
.await
.map_err(Error::CacheWrite)?;

Ok(ArchiveMetadata::from(
Metadata::from_workspace(
metadata,
Expand Down
119 changes: 119 additions & 0 deletions crates/uv/tests/it/lock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20363,6 +20363,125 @@ fn lock_dynamic_version_self_extra_setuptools() -> Result<()> {
Ok(())
}

/// See: <https://github.com/astral-sh/uv/issues/11047>
#[test]
fn lock_dynamic_built_cache() -> 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"]
dependencies = []

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[tool.uv]
cache-keys = [{ file = "pyproject.toml" }, { file = "src/__about__.py" }]

[tool.hatch.version]
path = "src/__about__.py"
scheme = "standard"
"#,
)?;

context
.temp_dir
.child("src")
.child("__about__.py")
.write_str("__version__ = '0.1.0'")?;

context
.temp_dir
.child("src")
.child("project")
.child("__init__.py")
.touch()?;

// Lock the project, which should omit the dynamic version.
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"
source = { editable = "." }
"###
);
});

// Install the project, to force a build.
uv_snapshot!(context.filters(), context.sync(), @r###"
success: true
exit_code: 0
----- stdout -----

----- stderr -----
Resolved 1 package in [TIME]
Prepared 1 package in [TIME]
Installed 1 package in [TIME]
+ project==0.1.0 (from file://[TEMP_DIR]/)
"###);

// Remove the lockfile.
fs_err::remove_file(context.temp_dir.join("uv.lock"))?;

// Lock the project, which should omit the dynamic version.
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"
source = { editable = "." }
"###
);
});

Ok(())
}

/// Re-lock after converting a package from dynamic to static.
#[test]
fn lock_dynamic_to_static() -> Result<()> {
Expand Down

0 comments on commit e6a1723

Please sign in to comment.