Skip to content

Commit

Permalink
Sort dependency group keys when adding new group
Browse files Browse the repository at this point in the history
However, if earlier keys were not sorted, we just append to avoid churn.
  • Loading branch information
jtfmumm committed Feb 18, 2025
1 parent 929e7c3 commit 58fa23f
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 0 deletions.
13 changes: 13 additions & 0 deletions crates/uv-workspace/src/pyproject_mut.rs
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,13 @@ impl PyProjectTomlMut {
.as_table_like_mut()
.ok_or(Error::MalformedDependencies)?;

let was_sorted = dependency_groups
.get_values()
.iter()
.filter_map(|(dotted_ks, _)| dotted_ks.first())
.map(|k| k.get())
.is_sorted();

let group = dependency_groups
.entry(group.as_ref())
.or_insert(Item::Value(Value::Array(Array::new())))
Expand All @@ -459,6 +466,12 @@ impl PyProjectTomlMut {
let name = req.name.clone();
let added = add_dependency(req, group, source.is_some())?;

// To avoid churn in pyproject.toml, we only sort new group keys if the
// existing keys were sorted.
if was_sorted {
dependency_groups.sort_values();
}

// If `dependency-groups` is an inline table, reformat it.
//
// Reformatting can drop comments between keys, but you can't put comments
Expand Down
109 changes: 109 additions & 0 deletions crates/uv/tests/it/edit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4826,6 +4826,112 @@ fn add_group() -> Result<()> {

let pyproject_toml = context.read("pyproject.toml");

insta::with_settings!({
filters => context.filters(),
}, {
assert_snapshot!(
pyproject_toml, @r#"
[project]
name = "project"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = []
[dependency-groups]
second = [
"anyio==3.7.0",
]
test = [
"anyio==3.7.0",
"requests>=2.31.0",
]
"#
);
});

uv_snapshot!(context.filters(), context.add().arg("anyio==3.7.0").arg("--group").arg("alpha"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 8 packages in [TIME]
Audited 3 packages in [TIME]
"###);

let pyproject_toml = context.read("pyproject.toml");

insta::with_settings!({
filters => context.filters(),
}, {
assert_snapshot!(
pyproject_toml, @r#"
[project]
name = "project"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = []
[dependency-groups]
alpha = [
"anyio==3.7.0",
]
second = [
"anyio==3.7.0",
]
test = [
"anyio==3.7.0",
"requests>=2.31.0",
]
"#
);
});

assert!(context.temp_dir.join("uv.lock").exists());

Ok(())
}

/// Add a requirement to a dependency group when existing dependency group
/// keys are not sorted.
#[test]
fn add_group_to_unsorted() -> Result<()> {
let context = TestContext::new("3.12");

let pyproject_toml = context.temp_dir.child("pyproject.toml");
pyproject_toml.write_str(indoc! {r#"
[project]
name = "project"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = []
[dependency-groups]
test = [
"anyio==3.7.0",
"requests>=2.31.0",
]
second = [
"anyio==3.7.0",
]
"#})?;

uv_snapshot!(context.filters(), context.add().arg("anyio==3.7.0").arg("--group").arg("alpha"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 8 packages in [TIME]
Prepared 3 packages in [TIME]
Installed 3 packages in [TIME]
+ anyio==3.7.0
+ idna==3.6
+ sniffio==1.3.1
"###);

let pyproject_toml = context.read("pyproject.toml");

insta::with_settings!({
filters => context.filters(),
}, {
Expand All @@ -4845,6 +4951,9 @@ fn add_group() -> Result<()> {
second = [
"anyio==3.7.0",
]
alpha = [
"anyio==3.7.0",
]
"###
);
});
Expand Down

0 comments on commit 58fa23f

Please sign in to comment.