Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 25 additions & 11 deletions src/usethis/_integrations/uv/deps.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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(
Expand All @@ -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_)
Expand Down
61 changes: 53 additions & 8 deletions tests/usethis/_integrations/uv/test_deps.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -627,3 +630,45 @@ 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"}


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 == []