Skip to content
Open
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
3 changes: 3 additions & 0 deletions docs/changelog/4061.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
A ``dependency-groups`` self-reference such as ``demo_pkg[extra_1]`` now matches the project name and the
``project.optional-dependencies`` keys after PEP 503 normalization, so a non-canonical spelling no longer fails with
``extra not found`` nor reaches pip unresolved - by :user:`MohammedAlkindi`.
8 changes: 6 additions & 2 deletions src/tox/tox_env/python/dependency_groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,10 @@ def unwrap_nested_extras(
if not project_name:
return dependencies

normed_project_name = canonicalize_name(project_name)
extras_to_unwrap: set[Requirement] = set()
for dependency in dependencies:
if dependency.name == project_name:
if canonicalize_name(dependency.name) == normed_project_name:
extras_to_unwrap.add(dependency)
if not extras_to_unwrap:
return dependencies
Expand Down Expand Up @@ -86,7 +87,10 @@ def resolve(root: Path, groups: set[str]) -> set[Requirement]:
result = result.union(_resolve_dependency_group(dependency_groups, group, original_names_lookup))

project_name = pyproject.get("project", {}).get("name")
optional_dependencies = pyproject.get("project", {}).get("optional-dependencies", {})
optional_dependencies: dict[str, list[str]] = {
canonicalize_name(name): deps
for name, deps in pyproject.get("project", {}).get("optional-dependencies", {}).items()
}

return unwrap_nested_extras(optional_dependencies, project_name, result, set())

Expand Down
30 changes: 30 additions & 0 deletions tests/tox_env/python/test_dependency_groups.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from __future__ import annotations

from typing import TYPE_CHECKING

from tox.tox_env.python.dependency_groups import resolve

if TYPE_CHECKING:
from pathlib import Path


def test_extra_key_not_canonical(tmp_path: Path) -> None:
(tmp_path / "pyproject.toml").write_text(
'[project]\nname = "demo-pkg"\n'
'[project.optional-dependencies]\nextra_1 = ["extra-pkg>=1.0"]\n'
'[dependency-groups]\ntest = ["demo-pkg[extra_1]"]\n',
encoding="utf-8",
)

assert sorted(str(i) for i in resolve(tmp_path, {"test"})) == ["extra-pkg>=1.0"]


def test_project_name_not_canonical(tmp_path: Path) -> None:
(tmp_path / "pyproject.toml").write_text(
'[project]\nname = "demo-pkg"\n'
'[project.optional-dependencies]\nextra1 = ["extra-pkg>=1.0"]\n'
'[dependency-groups]\ntest = ["demo_pkg[extra1]"]\n',
encoding="utf-8",
)

assert sorted(str(i) for i in resolve(tmp_path, {"test"})) == ["extra-pkg>=1.0"]
Loading