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`. /// The type of a dependency in a `pyproject.toml`.
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq, Eq)]
pub enum DependencyType { pub enum DependencyType {
/// A dependency in `project.dependencies`. /// A dependency in `project.dependencies`.
Production, Production,

View file

@ -48,6 +48,14 @@ pub enum ArrayEdit {
Add(usize), 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. /// Specifies whether dependencies are added to a script file or a `pyproject.toml` file.
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum DependencyTarget { pub enum DependencyTarget {

View file

@ -328,7 +328,7 @@ pub(crate) async fn add(
DependencyTarget::PyProjectToml, DependencyTarget::PyProjectToml,
), ),
}?; }?;
let mut edits = Vec::with_capacity(requirements.len()); let mut edits = Vec::<DependencyEdit>::with_capacity(requirements.len());
for mut requirement in requirements { for mut requirement in requirements {
// Add the specified extras. // Add the specified extras.
requirement.extras.extend(extras.iter().cloned()); requirement.extras.extend(extras.iter().cloned());
@ -407,6 +407,26 @@ pub(crate) async fn add(
}; };
// Keep track of the exact location of the edit. // 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 { edits.push(DependencyEdit {
dependency_type: &dependency_type, dependency_type: &dependency_type,
requirement, requirement,

View file

@ -4411,8 +4411,8 @@ fn fail_to_add_revert_project() -> Result<()> {
Ok(()) Ok(())
} }
/// Ensure that the added dependencies are sorted /// Ensure that the added dependencies are sorted if the dependency list was already sorted prior
/// if the dependency list was already sorted prior to adding the new one. /// to the operation.
#[test] #[test]
fn sorted_dependencies() -> Result<()> { fn sorted_dependencies() -> Result<()> {
let context = TestContext::new("3.12"); let context = TestContext::new("3.12");
@ -4426,19 +4426,31 @@ fn sorted_dependencies() -> Result<()> {
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [ dependencies = [
"CacheControl[filecache]>=0.14,<0.15", "CacheControl[filecache]>=0.14,<0.15",
"mwparserfromhell", "iniconfig",
"pywikibot",
"sentry-sdk",
"yarl",
] ]
"#})?; "#})?;
uv_snapshot!(context.filters(), context.add(&["pydantic"]).arg("--frozen"), @r###" uv_snapshot!(context.filters(), context.add(&["typing-extensions", "anyio"]), @r###"
success: true success: true
exit_code: 0 exit_code: 0
----- stdout ----- ----- stdout -----
----- stderr ----- ----- 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"))?; 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" requires-python = ">=3.12"
dependencies = [ dependencies = [
"CacheControl[filecache]>=0.14,<0.15", "CacheControl[filecache]>=0.14,<0.15",
"mwparserfromhell", "anyio>=4.3.0",
"pydantic", "iniconfig",
"pywikibot", "typing-extensions>=4.10.0",
"sentry-sdk",
"yarl",
] ]
"### "###
); );