Avoid updating incorrect dependencies for sorted uv add (#6939)

## Summary

The indexes stored in the edits is wrong now that we add dependencies
out-of-order.

Closes https://github.com/astral-sh/uv/issues/6933.
This commit is contained in:
Charlie Marsh 2024-09-02 14:21:25 -04:00 committed by GitHub
parent 42a4d80a63
commit cbe2827e97
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 52 additions and 14 deletions

View file

@ -486,7 +486,7 @@ impl Source {
}
/// The type of a dependency in a `pyproject.toml`.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DependencyType {
/// A dependency in `project.dependencies`.
Production,

View file

@ -48,6 +48,14 @@ pub enum ArrayEdit {
Add(usize),
}
impl ArrayEdit {
pub fn index(&self) -> usize {
match self {
Self::Update(i) | Self::Add(i) => *i,
}
}
}
/// Specifies whether dependencies are added to a script file or a `pyproject.toml` file.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum DependencyTarget {

View file

@ -328,7 +328,7 @@ pub(crate) async fn add(
DependencyTarget::PyProjectToml,
),
}?;
let mut edits = Vec::with_capacity(requirements.len());
let mut edits = Vec::<DependencyEdit>::with_capacity(requirements.len());
for mut requirement in requirements {
// Add the specified extras.
requirement.extras.extend(extras.iter().cloned());
@ -407,6 +407,26 @@ pub(crate) async fn add(
};
// Keep track of the exact location of the edit.
let index = edit.index();
// If the edit was inserted before the end of the list, update the existing edits.
for edit in &mut edits {
if *edit.dependency_type == dependency_type {
match &mut edit.edit {
ArrayEdit::Add(existing) => {
if *existing >= index {
*existing += 1;
}
}
ArrayEdit::Update(existing) => {
if *existing >= index {
*existing += 1;
}
}
}
}
}
edits.push(DependencyEdit {
dependency_type: &dependency_type,
requirement,

View file

@ -4411,8 +4411,8 @@ fn fail_to_add_revert_project() -> Result<()> {
Ok(())
}
/// Ensure that the added dependencies are sorted
/// if the dependency list was already sorted prior to adding the new one.
/// Ensure that the added dependencies are sorted if the dependency list was already sorted prior
/// to the operation.
#[test]
fn sorted_dependencies() -> Result<()> {
let context = TestContext::new("3.12");
@ -4426,19 +4426,31 @@ fn sorted_dependencies() -> Result<()> {
requires-python = ">=3.12"
dependencies = [
"CacheControl[filecache]>=0.14,<0.15",
"mwparserfromhell",
"pywikibot",
"sentry-sdk",
"yarl",
"iniconfig",
]
"#})?;
uv_snapshot!(context.filters(), context.add(&["pydantic"]).arg("--frozen"), @r###"
uv_snapshot!(context.filters(), context.add(&["typing-extensions", "anyio"]), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 13 packages in [TIME]
Prepared 12 packages in [TIME]
Installed 12 packages in [TIME]
+ anyio==4.3.0
+ cachecontrol==0.14.0
+ certifi==2024.2.2
+ charset-normalizer==3.3.2
+ filelock==3.13.1
+ idna==3.6
+ iniconfig==2.0.0
+ msgpack==1.0.8
+ requests==2.31.0
+ sniffio==1.3.1
+ typing-extensions==4.10.0
+ urllib3==2.2.1
"###);
let pyproject_toml = fs_err::read_to_string(context.temp_dir.join("pyproject.toml"))?;
@ -4455,11 +4467,9 @@ fn sorted_dependencies() -> Result<()> {
requires-python = ">=3.12"
dependencies = [
"CacheControl[filecache]>=0.14,<0.15",
"mwparserfromhell",
"pydantic",
"pywikibot",
"sentry-sdk",
"yarl",
"anyio>=4.3.0",
"iniconfig",
"typing-extensions>=4.10.0",
]
"###
);