mirror of
https://github.com/astral-sh/uv.git
synced 2025-12-15 22:14:06 +00:00
Invalidate dependencies when editables are updated (#1955)
## Summary If a `pyproject.toml` or similar is changed within an editable, we should avoid passing our audit check (and thus re-install the package). Closes https://github.com/astral-sh/uv/issues/1913.
This commit is contained in:
parent
a1f50418fd
commit
db53486308
3 changed files with 118 additions and 0 deletions
|
|
@ -645,6 +645,7 @@ impl ArchiveTimestamp {
|
|||
if metadata.is_file() {
|
||||
Ok(Some(Self::Exact(Timestamp::from_metadata(&metadata))))
|
||||
} else {
|
||||
// TODO(charlie): Take the maximum of `pyproject.toml`, `setup.py`, and `setup.cfg`.
|
||||
if let Some(metadata) = path
|
||||
.as_ref()
|
||||
.join("pyproject.toml")
|
||||
|
|
@ -676,6 +677,18 @@ impl ArchiveTimestamp {
|
|||
}
|
||||
}
|
||||
|
||||
impl std::cmp::PartialOrd for ArchiveTimestamp {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.timestamp().cmp(&other.timestamp()))
|
||||
}
|
||||
}
|
||||
|
||||
impl std::cmp::Ord for ArchiveTimestamp {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.timestamp().cmp(&other.timestamp())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Freshness {
|
||||
/// The cache entry is fresh according to the [`Refresh`] policy.
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ use distribution_types::{InstalledDist, InstalledMetadata, InstalledVersion, Nam
|
|||
use pep440_rs::{Version, VersionSpecifiers};
|
||||
use pep508_rs::{Requirement, VerbatimUrl};
|
||||
use requirements_txt::EditableRequirement;
|
||||
use uv_cache::ArchiveTimestamp;
|
||||
use uv_interpreter::Virtualenv;
|
||||
use uv_normalize::PackageName;
|
||||
|
||||
|
|
@ -273,6 +274,20 @@ impl<'a> SitePackages<'a> {
|
|||
return Ok(false);
|
||||
}
|
||||
[distribution] => {
|
||||
// Is the editable out-of-date?
|
||||
let Ok(Some(installed_at)) =
|
||||
ArchiveTimestamp::from_path(distribution.path().join("METADATA"))
|
||||
else {
|
||||
return Ok(false);
|
||||
};
|
||||
let Ok(Some(modified_at)) = ArchiveTimestamp::from_path(&requirement.path)
|
||||
else {
|
||||
return Ok(false);
|
||||
};
|
||||
if modified_at > installed_at {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
// Recurse into the dependencies.
|
||||
let metadata = distribution
|
||||
.metadata()
|
||||
|
|
|
|||
|
|
@ -1818,3 +1818,93 @@ fn install_symlink() {
|
|||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalidate_on_change() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
// Create an editable package.
|
||||
let editable_dir = assert_fs::TempDir::new()?;
|
||||
let pyproject_toml = editable_dir.child("pyproject.toml");
|
||||
pyproject_toml.write_str(
|
||||
r#"[project]
|
||||
name = "example"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"anyio==4.0.0"
|
||||
]
|
||||
requires-python = ">=3.8"
|
||||
"#,
|
||||
)?;
|
||||
|
||||
let filters = [(r"\(from file://.*\)", "(from [WORKSPACE_DIR])")]
|
||||
.into_iter()
|
||||
.chain(INSTA_FILTERS.to_vec())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
uv_snapshot!(filters, command(&context)
|
||||
.arg("--editable")
|
||||
.arg(editable_dir.path()), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Built 1 editable in [TIME]
|
||||
Resolved 4 packages in [TIME]
|
||||
Downloaded 3 packages in [TIME]
|
||||
Installed 4 packages in [TIME]
|
||||
+ anyio==4.0.0
|
||||
+ example==0.0.0 (from [WORKSPACE_DIR])
|
||||
+ idna==3.4
|
||||
+ sniffio==1.3.0
|
||||
"###
|
||||
);
|
||||
|
||||
// Re-installing should be a no-op.
|
||||
uv_snapshot!(filters, command(&context)
|
||||
.arg("--editable")
|
||||
.arg(editable_dir.path()), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Audited 1 package in [TIME]
|
||||
"###
|
||||
);
|
||||
|
||||
// Modify the editable package.
|
||||
pyproject_toml.write_str(
|
||||
r#"[project]
|
||||
name = "example"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"anyio==3.7.1"
|
||||
]
|
||||
requires-python = ">=3.8"
|
||||
"#,
|
||||
)?;
|
||||
|
||||
// Re-installing should update the package.
|
||||
uv_snapshot!(filters, command(&context)
|
||||
.arg("--editable")
|
||||
.arg(editable_dir.path()), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Built 1 editable in [TIME]
|
||||
Resolved 4 packages in [TIME]
|
||||
Downloaded 1 package in [TIME]
|
||||
Installed 2 packages in [TIME]
|
||||
- anyio==4.0.0
|
||||
+ anyio==3.7.1
|
||||
- example==0.0.0 (from [WORKSPACE_DIR])
|
||||
+ example==0.0.0 (from [WORKSPACE_DIR])
|
||||
"###
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue