Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error when invalid groups are referenced #7529

Merged
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
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