Skip to content

Commit 3d67a46

Browse files
committed
fix: take project.dynamic into account to decide if poetry dependencies are used to define or only to enrich project dependencies
This is especially relevant for projects that do not have any mandatory dependencies but optional dependencies and use `tool.poetry.dependencies` to define sources for these optional dependencies. Without this change the dependencies in `tool.poetry.dependencies` would have been considered mandatory dependencies in this case.
1 parent 8b66b86 commit 3d67a46

File tree

3 files changed

+66
-14
lines changed

3 files changed

+66
-14
lines changed

src/poetry/core/factory.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,10 +284,16 @@ def _configure_package_dependencies(
284284

285285
dependencies = project.get("dependencies", {})
286286
optional_dependencies = project.get("optional-dependencies", {})
287+
dynamic = project.get("dynamic", [])
287288

288289
package_extras: dict[NormalizedName, list[Dependency]]
289290
if dependencies or optional_dependencies:
290-
group = DependencyGroup(MAIN_GROUP)
291+
group = DependencyGroup(
292+
MAIN_GROUP,
293+
mixed_dynamic=(
294+
"dependencies" in dynamic or "optional-dependencies" in dynamic
295+
),
296+
)
291297
package.add_dependency_group(group)
292298

293299
for constraint in dependencies:

src/poetry/core/packages/dependency_group.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,12 @@
1414

1515

1616
class DependencyGroup:
17-
def __init__(self, name: str, optional: bool = False) -> None:
17+
def __init__(
18+
self, name: str, *, optional: bool = False, mixed_dynamic: bool = False
19+
) -> None:
1820
self._name: str = name
1921
self._optional: bool = optional
22+
self._mixed_dynamic = mixed_dynamic
2023
self._dependencies: list[Dependency] = []
2124
self._poetry_dependencies: list[Dependency] = []
2225

@@ -27,8 +30,9 @@ def name(self) -> str:
2730
@property
2831
def dependencies(self) -> list[Dependency]:
2932
if not self._dependencies:
33+
# legacy mode
3034
return self._poetry_dependencies
31-
if self._poetry_dependencies:
35+
if self._mixed_dynamic and self._poetry_dependencies:
3236
if all(dep.is_optional() for dep in self._dependencies):
3337
return [
3438
*self._dependencies,

tests/packages/test_dependency_group.py

Lines changed: 53 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ def create_dependency(
4343
return dep
4444

4545

46+
@pytest.mark.parametrize("mixed_dynamic", [False, True])
4647
@pytest.mark.parametrize(
4748
(
4849
"dependencies",
@@ -57,20 +58,20 @@ def create_dependency(
5758
(
5859
{Dependency("foo", "*", optional=True)},
5960
{Dependency("bar", "*")},
60-
{"foo", "bar"},
61+
({"foo"}, {"foo", "bar"}),
6162
),
6263
(
6364
{Dependency("foo", "*")},
6465
{Dependency("bar", "*", optional=True)},
65-
{"foo", "bar"},
66+
({"foo"}, {"foo", "bar"}),
6667
),
6768
(
6869
{
6970
Dependency("foo", "*", optional=True),
7071
Dependency("baz", "*", optional=True),
7172
},
7273
{Dependency("bar", "*")},
73-
{"foo", "bar", "baz"},
74+
({"foo", "baz"}, {"foo", "bar", "baz"}),
7475
),
7576
(
7677
{
@@ -83,20 +84,25 @@ def create_dependency(
8384
(
8485
{Dependency("foo", "*", optional=True)},
8586
{Dependency("bar", "*"), Dependency("baz", "*", optional=True)},
86-
{"foo", "bar"},
87+
({"foo"}, {"foo", "bar"}),
8788
),
8889
],
8990
)
9091
def test_dependencies(
9192
dependencies: set[Dependency],
9293
poetry_dependencies: set[Dependency],
93-
expected_dependencies: set[str],
94+
mixed_dynamic: bool,
95+
expected_dependencies: set[str] | tuple[set[str], set[str]],
9496
) -> None:
95-
group = DependencyGroup(name="group")
97+
group = DependencyGroup(name="group", mixed_dynamic=mixed_dynamic)
9698
group._dependencies = list(dependencies)
9799
group._poetry_dependencies = list(poetry_dependencies)
98100

99-
assert {d.name for d in group.dependencies} == set(expected_dependencies)
101+
if isinstance(expected_dependencies, tuple):
102+
expected_dependencies = (
103+
expected_dependencies[1] if mixed_dynamic else expected_dependencies[0]
104+
)
105+
assert {d.name for d in group.dependencies} == expected_dependencies
100106

101107

102108
@pytest.mark.parametrize(
@@ -147,6 +153,7 @@ def test_remove_dependency_removes_from_both_lists() -> None:
147153
assert {d.name for d in group._poetry_dependencies} == {"baz"}
148154

149155

156+
@pytest.mark.parametrize("mixed_dynamic", [False, True])
150157
@pytest.mark.parametrize(
151158
(
152159
"dependencies",
@@ -164,12 +171,24 @@ def test_remove_dependency_removes_from_both_lists() -> None:
164171
(
165172
[create_dependency("foo", in_extras=("extra1",))],
166173
[create_dependency("bar")],
167-
[create_dependency("foo", in_extras=("extra1",)), create_dependency("bar")],
174+
(
175+
[create_dependency("foo", in_extras=("extra1",))],
176+
[
177+
create_dependency("foo", in_extras=("extra1",)),
178+
create_dependency("bar"),
179+
],
180+
),
168181
),
169182
(
170183
[create_dependency("foo")],
171184
[create_dependency("bar", in_extras=("extra1",))],
172-
[create_dependency("foo"), create_dependency("bar", in_extras=("extra1",))],
185+
(
186+
[create_dependency("foo")],
187+
[
188+
create_dependency("foo"),
189+
create_dependency("bar", in_extras=("extra1",)),
190+
],
191+
),
173192
),
174193
# refine constraint
175194
(
@@ -311,6 +330,23 @@ def test_remove_dependency_removes_from_both_lists() -> None:
311330
[create_dependency("foo", source_name="src", optional=True)],
312331
[create_dependency("foo", source_name="src", marker="extra == 'extra1'")],
313332
),
333+
(
334+
[Dependency.create_from_pep_508("foo;extra=='extra1'")],
335+
[create_dependency("foo", source_name="src")],
336+
(
337+
[
338+
create_dependency(
339+
"foo", source_name="src", marker="extra == 'extra1'"
340+
)
341+
],
342+
[
343+
create_dependency(
344+
"foo", source_name="src", marker="extra == 'extra1'"
345+
),
346+
create_dependency("foo", source_name="src"),
347+
],
348+
),
349+
),
314350
# extras - special
315351
# root extras do not have an extra marker, they just have set _in_extras!
316352
(
@@ -398,12 +434,18 @@ def test_remove_dependency_removes_from_both_lists() -> None:
398434
def test_dependencies_for_locking(
399435
dependencies: list[Dependency],
400436
poetry_dependencies: list[Dependency],
401-
expected_dependencies: list[Dependency],
437+
mixed_dynamic: bool,
438+
expected_dependencies: list[Dependency] | tuple[list[Dependency], list[Dependency]],
402439
) -> None:
403-
group = DependencyGroup(name="group")
440+
group = DependencyGroup(name="group", mixed_dynamic=mixed_dynamic)
404441
group._dependencies = dependencies
405442
group._poetry_dependencies = poetry_dependencies
406443

444+
if isinstance(expected_dependencies, tuple):
445+
expected_dependencies = (
446+
expected_dependencies[1] if mixed_dynamic else expected_dependencies[0]
447+
)
448+
407449
assert group.dependencies_for_locking == expected_dependencies
408450
# explicitly check attributes that are not considered in __eq__
409451
assert [d.allows_prereleases() for d in group.dependencies_for_locking] == [

0 commit comments

Comments
 (0)