From 640ab6e6330a30dd1977111712263015c70c831a Mon Sep 17 00:00:00 2001 From: Nathan McDougall Date: Wed, 5 Mar 2025 15:28:22 +1300 Subject: [PATCH 1/2] Don't register dependency groups until they're succesfully added --- src/usethis/_integrations/uv/deps.py | 36 ++++++++++++++------- tests/usethis/_integrations/uv/test_deps.py | 34 ++++++++++++++----- 2 files changed, 51 insertions(+), 19 deletions(-) diff --git a/src/usethis/_integrations/uv/deps.py b/src/usethis/_integrations/uv/deps.py index bdbe4c2e..8b0059a7 100644 --- a/src/usethis/_integrations/uv/deps.py +++ b/src/usethis/_integrations/uv/deps.py @@ -65,26 +65,39 @@ def register_default_group(group: str) -> None: This ensures that dependencies in the group will be installed by default. """ if group == "dev": + # Note, if the "dev" group is missing already then then we'll respect the + # user's choice since they presumably would have added it themselves. So, we + # won't register in that case. return - ensure_dev_group_is_defined() - - try: - default_groups = get_pyproject_value(["tool", "uv", "default-groups"]) - if not isinstance(default_groups, list): - default_groups = [] - except KeyError: - default_groups = [] + default_groups = get_default_groups() + # Choose which groups we want to add groups_to_add = [] if group not in default_groups: groups_to_add.append(group) # Add "dev" if section is empty or if we're adding a new group and "dev" isn't present if (not default_groups or group != "dev") and "dev" not in default_groups: + ensure_dev_group_is_defined() groups_to_add.append("dev") if groups_to_add: - extend_pyproject_list(["tool", "uv", "default-groups"], groups_to_add) + add_default_groups(groups=groups_to_add) + + +def add_default_groups(groups: list[str]) -> None: + extend_pyproject_list(["tool", "uv", "default-groups"], groups) + + +def get_default_groups() -> list[str]: + try: + default_groups = get_pyproject_value(["tool", "uv", "default-groups"]) + if not isinstance(default_groups, list): + default_groups = [] + except KeyError: + default_groups = [] + + return default_groups def ensure_dev_group_is_defined() -> None: @@ -112,8 +125,6 @@ def add_deps_to_group(deps: list[Dependency], group: str) -> None: if usethis_config.frozen: box_print(f"Install the dependenc{ies} {deps_str}.") - register_default_group(group) # Register the group before adding dependencies - for dep in to_add_deps: try: call_uv_subprocess( @@ -125,6 +136,9 @@ def add_deps_to_group(deps: list[Dependency], group: str) -> None: msg += (Path.cwd() / "pyproject.toml").read_text() raise UVDepGroupError(msg) from None + # Register the group - don't do this before adding the deps in case that step fails + register_default_group(group) + def is_dep_satisfied_in(dep: Dependency, *, in_: list[Dependency]) -> bool: return any(_is_dep_satisfied_by(dep, by=by) for by in in_) diff --git a/tests/usethis/_integrations/uv/test_deps.py b/tests/usethis/_integrations/uv/test_deps.py index 2a8ee16f..b0919466 100644 --- a/tests/usethis/_integrations/uv/test_deps.py +++ b/tests/usethis/_integrations/uv/test_deps.py @@ -15,6 +15,7 @@ from usethis._integrations.uv.deps import ( Dependency, add_deps_to_group, + get_default_groups, get_dep_groups, get_deps_from_group, is_dep_in_any_group, @@ -290,16 +291,18 @@ def mock_call_uv_subprocess(*_, **__): usethis._integrations.uv.deps, "call_uv_subprocess", mock_call_uv_subprocess ) - with ( - change_cwd(uv_init_dir), - PyprojectTOMLManager(), - pytest.raises( + # Act, Assert + with change_cwd(uv_init_dir), PyprojectTOMLManager(): + with pytest.raises( UVDepGroupError, match="Failed to add 'pytest' to the 'test' dependency group", - ), - ): - # Act - add_deps_to_group([Dependency(name="pytest")], "test") + ): + add_deps_to_group([Dependency(name="pytest")], "test") + + # Assert contd + # We want to check that registration hasn't taken place + default_groups = get_default_groups() + assert "test" not in default_groups class TestRemoveDepsFromGroup: @@ -627,3 +630,18 @@ def test_existing_section_adds_dev_with_new_group(self, tmp_path: Path): # Assert default_groups = get_pyproject_value(["tool", "uv", "default-groups"]) assert set(default_groups) == {"test", "docs", "dev"} + + def test_dev_not_added_if_missing(self, tmp_path: Path): + # Arrange + (tmp_path / "pyproject.toml").write_text("""\ +[tool.uv] +default-groups = ["test"] +""") + + with change_cwd(tmp_path), PyprojectTOMLManager(): + # Act + register_default_group("test") + + # Assert + default_groups = get_pyproject_value(["tool", "uv", "default-groups"]) + assert set(default_groups) == {"test"} From e8afc9dbfe50dcfd0d26b84de2b5fa34ab9f7cdb Mon Sep 17 00:00:00 2001 From: Nathan McDougall Date: Wed, 5 Mar 2025 16:10:16 +1300 Subject: [PATCH 2/2] Add tests for `get_default_groups` --- tests/usethis/_integrations/uv/test_deps.py | 27 +++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/usethis/_integrations/uv/test_deps.py b/tests/usethis/_integrations/uv/test_deps.py index b0919466..7aa8780d 100644 --- a/tests/usethis/_integrations/uv/test_deps.py +++ b/tests/usethis/_integrations/uv/test_deps.py @@ -645,3 +645,30 @@ def test_dev_not_added_if_missing(self, tmp_path: Path): # Assert default_groups = get_pyproject_value(["tool", "uv", "default-groups"]) assert set(default_groups) == {"test"} + + +class TestGetDefaultGroups: + def test_empty_pyproject_toml(self, tmp_path: Path): + # Arrange + (tmp_path / "pyproject.toml").touch() + + with change_cwd(tmp_path), PyprojectTOMLManager(): + # Act + result = get_default_groups() + + # Assert + assert result == [] + + def test_invalid_default_groups(self, tmp_path: Path): + # Arrange + (tmp_path / "pyproject.toml").write_text("""\ +[tool.uv] +default-groups = "not a list" +""") + + with change_cwd(tmp_path), PyprojectTOMLManager(): + # Act + result = get_default_groups() + + # Assert + assert result == []