Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
14 changes: 13 additions & 1 deletion src/poetry/console/commands/group_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,16 @@ def activated_groups(self) -> set[NormalizedName]:
key: {canonicalize_name(group) for group in key_groups}
for key, key_groups in groups.items()
}
# Warn if any group name was normalized
for key_groups in groups.values():
for group in key_groups:
normalized = canonicalize_name(group)
if normalized != group:
self.line_error(
f"<warning>Group '{group}' was normalized to '{normalized}'."
" Consider updating your command to use the normalized name.</warning>"
)

norm_default_groups = {canonicalize_name(name) for name in self.default_groups}

return norm_groups["only"] or norm_default_groups.union(
Expand All @@ -121,7 +131,9 @@ def _validate_group_options(self, group_options: dict[str, set[str]]) -> None:
invalid_options = defaultdict(set)
for opt, groups in group_options.items():
for group in groups:
if not self.poetry.package.has_dependency_group(group):
if not self.poetry.package.has_dependency_group(
canonicalize_name(group)
):
invalid_options[group].add(opt)
if invalid_options:
message_parts = []
Expand Down
41 changes: 41 additions & 0 deletions tests/console/commands/test_install.py
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,47 @@ def test_invalid_groups_with_without_only(
)


@pytest.mark.parametrize(
"option",
["--with", "--without", "--only"],
)
def test_non_normalized_group_name_emits_warning(
tester: CommandTester,
mocker: MockerFixture,
option: str,
) -> None:
"""
A warning is emitted when a group name passed via --with/--without/--only
differs from its normalized form (e.g. 'Foo' instead of 'foo').
"""
assert isinstance(tester.command, InstallerCommand)
mocker.patch.object(tester.command.installer, "run", return_value=0)

# 'Foo' normalizes to 'foo', which exists in the fixture pyproject
tester.execute(f"{option} Foo")

error_output = tester.io.fetch_error()
assert "was normalized to" in error_output
assert "'Foo'" in error_output
assert "'foo'" in error_output


def test_normalized_group_name_does_not_raise(
tester: CommandTester,
mocker: MockerFixture,
) -> None:
"""
Passing a non-normalized but valid group name (e.g. 'Foo' for group 'foo')
should not raise a GroupNotFoundError.
"""
assert isinstance(tester.command, InstallerCommand)
mocker.patch.object(tester.command.installer, "run", return_value=0)

# Should not raise even though 'Foo' != 'foo', because they normalize to the same name
tester.execute("--with Foo")
assert tester.status_code != 1 or "not found" not in tester.io.fetch_error()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Assertion is too weak and can pass even when the command fails or reports a group-not-found error.

This assertion lets failures pass: e.g., if status_code == 1 but the error text changes and no longer includes "not found", the test still passes. It also risks flakiness if the wording changes.

Please assert the concrete success conditions instead, for example:

tester.execute("--with Foo")
error_output = tester.io.fetch_error()

assert tester.status_code == 0
assert "GroupNotFoundError" not in error_output
assert "not found" not in error_output

(or whatever status code and error-output guarantees are actually expected), so the test clearly proves that a normalized but valid group name neither fails nor emits a group-not-found error.



def test_dry_run_populates_installer(
tester: CommandTester, mocker: MockerFixture
) -> None:
Expand Down
Loading