Skip to content

Commit ed4bb56

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 91f06ba commit ed4bb56

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
@@ -290,10 +290,16 @@ def _configure_package_dependencies(
290290

291291
dependencies = project.get("dependencies", {})
292292
optional_dependencies = project.get("optional-dependencies", {})
293+
dynamic = project.get("dynamic", [])
293294

294295
package_extras: dict[NormalizedName, list[Dependency]]
295296
if dependencies or optional_dependencies:
296-
group = DependencyGroup(MAIN_GROUP)
297+
group = DependencyGroup(
298+
MAIN_GROUP,
299+
mixed_dynamic=(
300+
"dependencies" in dynamic or "optional-dependencies" in dynamic
301+
),
302+
)
297303
package.add_dependency_group(group)
298304

299305
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)