From cbe2827e97639eeef7d127e96f0aeb971eab3ce1 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Mon, 2 Sep 2024 14:21:25 -0400 Subject: [PATCH] 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. --- crates/uv-workspace/src/pyproject.rs | 2 +- crates/uv-workspace/src/pyproject_mut.rs | 8 ++++++ crates/uv/src/commands/project/add.rs | 22 ++++++++++++++- crates/uv/tests/edit.rs | 34 +++++++++++++++--------- 4 files changed, 52 insertions(+), 14 deletions(-) diff --git a/crates/uv-workspace/src/pyproject.rs b/crates/uv-workspace/src/pyproject.rs index eafc6d78d..76ba81d44 100644 --- a/crates/uv-workspace/src/pyproject.rs +++ b/crates/uv-workspace/src/pyproject.rs @@ -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, diff --git a/crates/uv-workspace/src/pyproject_mut.rs b/crates/uv-workspace/src/pyproject_mut.rs index 38e7fa81d..8e31d9d92 100644 --- a/crates/uv-workspace/src/pyproject_mut.rs +++ b/crates/uv-workspace/src/pyproject_mut.rs @@ -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 { diff --git a/crates/uv/src/commands/project/add.rs b/crates/uv/src/commands/project/add.rs index 02018d14c..04fde9418 100644 --- a/crates/uv/src/commands/project/add.rs +++ b/crates/uv/src/commands/project/add.rs @@ -328,7 +328,7 @@ pub(crate) async fn add( DependencyTarget::PyProjectToml, ), }?; - let mut edits = Vec::with_capacity(requirements.len()); + let mut edits = Vec::::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, diff --git a/crates/uv/tests/edit.rs b/crates/uv/tests/edit.rs index 3d3235ba2..2fb9e1a39 100644 --- a/crates/uv/tests/edit.rs +++ b/crates/uv/tests/edit.rs @@ -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", ] "### );