Skip to content

Commit

Permalink
Error when invalid groups are referenced (#7529)
Browse files Browse the repository at this point in the history
  • Loading branch information
samypr100 authored Apr 5, 2023
1 parent 6b3a616 commit 7585c37
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 1 deletion.
2 changes: 1 addition & 1 deletion docs/managing-dependencies.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ poetry install --only docs
```

{{% note %}}
If you only want to install the project's runtime dependencies, you can do so with the
If you only want to install the project's runtime dependencies, you can do so with the
`--only main` notation:

```bash
Expand Down
22 changes: 22 additions & 0 deletions src/poetry/console/commands/group_command.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
from __future__ import annotations

from collections import defaultdict
from typing import TYPE_CHECKING

from cleo.helpers import option
from poetry.core.packages.dependency_group import MAIN_GROUP

from poetry.console.commands.command import Command
from poetry.console.exceptions import GroupNotFound


if TYPE_CHECKING:
Expand Down Expand Up @@ -78,6 +80,7 @@ def activated_groups(self) -> set[str]:
for groups in self.option(key, "")
for group in groups.split(",")
}
self._validate_group_options(groups)

for opt, new, group in [
("no-dev", "only", MAIN_GROUP),
Expand Down Expand Up @@ -107,3 +110,22 @@ def project_with_activated_groups_only(self) -> ProjectPackage:
return self.poetry.package.with_dependency_groups(
list(self.activated_groups), only=True
)

def _validate_group_options(self, group_options: dict[str, set[str]]) -> None:
"""
Raises en error if it detects that a group is not part of pyproject.toml
"""
invalid_options = defaultdict(set)
for opt, groups in group_options.items():
for group in groups:
if not self.poetry.package.has_dependency_group(group):
invalid_options[group].add(opt)
if invalid_options:
message_parts = []
for group in sorted(invalid_options):
opts = ", ".join(
f"<fg=yellow;options=bold>--{opt}</>"
for opt in sorted(invalid_options[group])
)
message_parts.append(f"{group} (via {opts})")
raise GroupNotFound(f"Group(s) not found: {', '.join(message_parts)}")
4 changes: 4 additions & 0 deletions src/poetry/console/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,7 @@

class PoetryConsoleError(CleoError):
pass


class GroupNotFound(PoetryConsoleError):
pass
46 changes: 46 additions & 0 deletions tests/console/commands/test_install.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
from __future__ import annotations

import re

from typing import TYPE_CHECKING

import pytest

from poetry.core.masonry.utils.module import ModuleOrPackageNotFound
from poetry.core.packages.dependency_group import MAIN_GROUP

from poetry.console.exceptions import GroupNotFound


if TYPE_CHECKING:
from cleo.testers.command_tester import CommandTester
Expand Down Expand Up @@ -257,6 +261,48 @@ def test_only_root_conflicts_with_without_only(
)


@pytest.mark.parametrize(
("options", "valid_groups", "should_raise"),
[
({"--with": MAIN_GROUP}, {MAIN_GROUP}, False),
({"--with": "spam"}, set(), True),
({"--with": "spam,foo"}, {"foo"}, True),
({"--without": "spam"}, set(), True),
({"--without": "spam,bar"}, {"bar"}, True),
({"--with": "eggs,ham", "--without": "spam"}, set(), True),
({"--with": "eggs,ham", "--without": "spam,baz"}, {"baz"}, True),
({"--only": "spam"}, set(), True),
({"--only": "bim"}, {"bim"}, False),
({"--only": MAIN_GROUP}, {MAIN_GROUP}, False),
],
)
def test_invalid_groups_with_without_only(
tester: CommandTester,
mocker: MockerFixture,
options: dict[str, str],
valid_groups: set[str],
should_raise: bool,
):
mocker.patch.object(tester.command.installer, "run", return_value=0)

cmd_args = " ".join(f"{flag} {groups}" for (flag, groups) in options.items())

if not should_raise:
tester.execute(cmd_args)
assert tester.status_code == 0
else:
with pytest.raises(GroupNotFound, match=r"^Group\(s\) not found:") as e:
tester.execute(cmd_args)
assert tester.status_code is None
for opt, groups in options.items():
group_list = groups.split(",")
invalid_groups = sorted(set(group_list) - valid_groups)
for group in invalid_groups:
assert (
re.search(rf"{group} \(via .*{opt}.*\)", str(e.value)) is not None
)


def test_remove_untracked_outputs_deprecation_warning(
tester: CommandTester,
mocker: MockerFixture,
Expand Down

0 comments on commit 7585c37

Please sign in to comment.