From acb40b0713c30466dcbab73e6b98d1add8e5dd98 Mon Sep 17 00:00:00 2001 From: Frost Ming Date: Mon, 4 Nov 2024 16:36:11 +0800 Subject: [PATCH] fix: dependency group with non-normalized name does not work in pdm 2.20.0 (#3250) * fix: dependency group with non-normalized name does not work in pdm 2.20.0 Fixes #3247 Signed-off-by: Frost Ming * fix tests Signed-off-by: Frost Ming --- news/3247.bugfix.md | 1 + src/pdm/cli/commands/add.py | 2 +- src/pdm/cli/filters.py | 9 +++++---- src/pdm/project/core.py | 11 +++++++++-- tests/cli/test_add.py | 12 ++++++++++++ 5 files changed, 28 insertions(+), 7 deletions(-) create mode 100644 news/3247.bugfix.md diff --git a/news/3247.bugfix.md b/news/3247.bugfix.md new file mode 100644 index 0000000000..076672b5f6 --- /dev/null +++ b/news/3247.bugfix.md @@ -0,0 +1 @@ +Fix group name normalization when comparing groups. diff --git a/src/pdm/cli/commands/add.py b/src/pdm/cli/commands/add.py index cef31b9c35..b90d7c168c 100644 --- a/src/pdm/cli/commands/add.py +++ b/src/pdm/cli/commands/add.py @@ -122,7 +122,7 @@ def do_add( if project.enable_write_lockfile: project.core.ui.info(f"Adding group [success]{group}[/] to lockfile") lock_groups.append(group) - if group == "default" or not selection.dev and group not in project.pyproject.dev_dependencies: + if group == "default" or not selection.dev and normalize_name(group) not in project.pyproject.dev_dependencies: if editables: raise PdmUsageError("Cannot add editables to the default or optional dependency group") for r in [parse_requirement(line, True) for line in editables] + [parse_requirement(line) for line in packages]: diff --git a/src/pdm/cli/filters.py b/src/pdm/cli/filters.py index b8e6f217bf..d90c78ba32 100644 --- a/src/pdm/cli/filters.py +++ b/src/pdm/cli/filters.py @@ -5,6 +5,7 @@ from typing import TYPE_CHECKING from pdm.exceptions import PdmUsageError +from pdm.utils import normalize_name if TYPE_CHECKING: from typing import Iterator, Sequence @@ -78,9 +79,9 @@ def _translated_groups(self) -> list[str]: if dev is None: # --prod is not set, include dev-dependencies dev = True project = self.project - optional_groups = set(project.pyproject.metadata.get("optional-dependencies", {})) + optional_groups = {normalize_name(g) for g in project.pyproject.metadata.get("optional-dependencies", {})} dev_groups = set(project.pyproject.dev_dependencies) - groups_set = set(groups) + groups_set = {normalize_name(g) if g != ":all" else g for g in groups} if groups_set & dev_groups: if not dev: raise PdmUsageError("--prod is not allowed with dev groups and should be left") @@ -91,9 +92,9 @@ def _translated_groups(self) -> list[str]: groups_set.update(optional_groups) if default: groups_set.add("default") - groups_set -= set(self.excluded_groups) + groups_set -= {normalize_name(g) for g in self.excluded_groups} - invalid_groups = groups_set - set(project.iter_groups()) + invalid_groups = groups_set - {normalize_name(g) for g in project.iter_groups()} if invalid_groups: project.core.ui.echo( "[d]Ignoring non-existing groups: [success]" f"{', '.join(invalid_groups)}[/]", diff --git a/src/pdm/project/core.py b/src/pdm/project/core.py index 870d82e4e3..97596dc08e 100644 --- a/src/pdm/project/core.py +++ b/src/pdm/project/core.py @@ -442,7 +442,8 @@ def iter_groups(self) -> Iterable[str]: groups = {"default"} if self.pyproject.metadata.get("optional-dependencies"): groups.update(self.pyproject.metadata["optional-dependencies"].keys()) - groups.update(self.pyproject.dev_dependencies.keys()) + groups.update(self.pyproject._data.get("dependency-groups", {}).keys()) + groups.update(self.pyproject.settings.get("dev-dependencies", {}).keys()) return groups @property @@ -662,6 +663,8 @@ def update_dev_dependencies(deps: list[str]) -> None: metadata, settings = self.pyproject.metadata, self.pyproject.settings if group == "default": return metadata.get("dependencies", tomlkit.array()), lambda x: metadata.__setitem__("dependencies", x) + dev_dependencies = self.pyproject._data.get("dependency-groups", {}) + dev_dependencies.update(self.pyproject.settings.get("dev-dependencies", {})) deps_setter = [ ( metadata.get("optional-dependencies", {}), @@ -669,11 +672,15 @@ def update_dev_dependencies(deps: list[str]) -> None: if x else metadata.setdefault("optional-dependencies", {}).pop(group, None), ), - (self.pyproject.dev_dependencies, update_dev_dependencies), + (dev_dependencies, update_dev_dependencies), ] + normalized_group = normalize_name(group) for deps, setter in deps_setter: + normalized_groups = {normalize_name(g) for g in deps} if group in deps: return make_array(deps[group], True), setter + if normalized_group in normalized_groups: + raise PdmUsageError(f"Group {group} already exists in another non-normalized form") # If not found, return an empty list and a setter to add the group return tomlkit.array(), deps_setter[int(dev)][1] diff --git a/tests/cli/test_add.py b/tests/cli/test_add.py index 8da5d9610a..9176c418ac 100644 --- a/tests/cli/test_add.py +++ b/tests/cli/test_add.py @@ -327,3 +327,15 @@ def test_add_dependency_with_direct_minimal_versions(project, pdm, repository): assert "django>=1.11.8" in project.pyproject.metadata["dependencies"] assert all_candidates["django"].version == "1.11.8" assert all_candidates["pytz"].version == "2019.6" + + +def test_add_group_with_normalized_name(project, pdm, working_set): + project.pyproject.dependency_groups.update({"foo_bar": ["requests"]}) + project.pyproject.write() + pdm(["lock"], obj=project, strict=True) + assert "foo-bar" in project.lockfile.groups + pdm(["sync", "-G", "foo.bar"], obj=project, strict=True) + assert "requests" in working_set + result = pdm(["add", "-G", "foo-bar", "pytz"], obj=project) + assert result.exit_code != 0 + assert "Group foo-bar already exists in another non-normalized form" in result.stderr