Skip to content

Commit c6d741d

Browse files
finswimmerradoering
authored andcommitted
feat(cli): support pep 735 on add (#10130)
1 parent 5d4d6a2 commit c6d741d

File tree

6 files changed

+283
-70
lines changed

6 files changed

+283
-70
lines changed

poetry.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/poetry/console/commands/add.py

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -152,14 +152,18 @@ def handle(self) -> int:
152152
content: dict[str, Any] = self.poetry.file.read()
153153
project_content = content.get("project", table())
154154
poetry_content = content.get("tool", {}).get("poetry", table())
155+
groups_content = content.get("dependency-groups", {})
155156
project_name = (
156157
canonicalize_name(name)
157158
if (name := project_content.get("name", poetry_content.get("name")))
158159
else None
159160
)
160161

161162
use_project_section = False
163+
use_groups_section = False
162164
project_dependency_names = []
165+
166+
# Run-Time Deps incl. extras
163167
if group == MAIN_GROUP:
164168
if (
165169
"dependencies" in project_content
@@ -179,7 +183,21 @@ def handle(self) -> int:
179183
project_section = array()
180184

181185
poetry_section = poetry_content.get("dependencies", table())
186+
187+
# Dependency Groups
182188
else:
189+
if groups_content or "group" not in poetry_content:
190+
use_groups_section = True
191+
if not groups_content:
192+
groups_content = table(is_super_table=True)
193+
if group not in groups_content:
194+
groups_content[group] = array("[\n]")
195+
196+
project_dependency_names = [
197+
Dependency.create_from_pep_508(dep).name
198+
for dep in groups_content[group]
199+
]
200+
183201
if "group" not in poetry_content:
184202
poetry_content["group"] = table(is_super_table=True)
185203

@@ -263,17 +281,17 @@ def handle(self) -> int:
263281
self.line_error("\nNo changes were applied.")
264282
return 1
265283

266-
if self.option("python"):
267-
constraint["python"] = self.option("python")
284+
if python := self.option("python"):
285+
constraint["python"] = python
268286

269-
if self.option("platform"):
270-
constraint["platform"] = self.option("platform")
287+
if platform := self.option("platform"):
288+
constraint["platform"] = platform
271289

272-
if self.option("markers"):
273-
constraint["markers"] = self.option("markers")
290+
if markers := self.option("markers"):
291+
constraint["markers"] = markers
274292

275-
if self.option("source"):
276-
constraint["source"] = self.option("source")
293+
if source := self.option("source"):
294+
constraint["source"] = source
277295

278296
if len(constraint) == 1 and "version" in constraint:
279297
constraint = constraint["version"]
@@ -304,13 +322,16 @@ def handle(self) -> int:
304322
)
305323
self.poetry.package.add_dependency(dependency)
306324

307-
if use_project_section:
325+
if use_project_section or use_groups_section:
326+
pep_section = (
327+
project_section if use_project_section else groups_content[group]
328+
)
308329
try:
309330
index = project_dependency_names.index(canonical_constraint_name)
310331
except ValueError:
311-
project_section.append(dependency.to_pep_508())
332+
pep_section.append(dependency.to_pep_508())
312333
else:
313-
project_section[index] = dependency.to_pep_508()
334+
pep_section[index] = dependency.to_pep_508()
314335

315336
# create a second constraint for tool.poetry.dependencies with keys
316337
# that cannot be stored in the project section
@@ -352,13 +373,20 @@ def handle(self) -> int:
352373
project_content["optional-dependencies"][optional] = project_section
353374
elif "dependencies" not in project_content:
354375
project_content["dependencies"] = project_section
376+
355377
if poetry_section:
356378
if "tool" not in content:
357379
content["tool"] = table()
358380
if "poetry" not in content["tool"]:
359381
content["tool"]["poetry"] = poetry_content
360382
if group == MAIN_GROUP and "dependencies" not in poetry_content:
361383
poetry_content["dependencies"] = poetry_section
384+
385+
if groups_content and group != MAIN_GROUP:
386+
if "dependency-groups" not in content:
387+
content["dependency-groups"] = table()
388+
content["dependency-groups"][group] = groups_content[group]
389+
362390
self.poetry.locker.set_pyproject_data(content)
363391
self.installer.set_locker(self.poetry.locker)
364392

tests/console/commands/self/test_add_plugins.py

Lines changed: 19 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
11
from __future__ import annotations
22

33
from typing import TYPE_CHECKING
4-
from typing import Any
5-
6-
7-
if TYPE_CHECKING:
8-
from collections.abc import Mapping
94

105
import pytest
116

@@ -32,13 +27,13 @@ def tester(command_tester_factory: CommandTesterFactory) -> CommandTester:
3227
def assert_plugin_add_result(
3328
tester: CommandTester,
3429
expected: str,
35-
constraint: str | Mapping[str, str | list[str]],
30+
constraint: str,
3631
) -> None:
3732
assert tester.io.fetch_output() == expected
38-
dependencies: dict[str, Any] = get_self_command_dependencies()
33+
dependencies: list[str] = get_self_command_dependencies()
3934

40-
assert "poetry-plugin" in dependencies
41-
assert dependencies["poetry-plugin"] == constraint
35+
assert "poetry-plugin" in dependencies[0]
36+
assert constraint in dependencies[0]
4237

4338

4439
def test_add_no_constraint(
@@ -61,7 +56,7 @@ def test_add_no_constraint(
6156
6257
Writing lock file
6358
"""
64-
assert_plugin_add_result(tester, expected, "^0.1.0")
59+
assert_plugin_add_result(tester, expected, "(>=0.1.0,<0.2.0)")
6560

6661

6762
def test_add_with_constraint(
@@ -84,7 +79,7 @@ def test_add_with_constraint(
8479
Writing lock file
8580
"""
8681

87-
assert_plugin_add_result(tester, expected, "^0.2.0")
82+
assert_plugin_add_result(tester, expected, "(>=0.2.0,<0.3.0)")
8883

8984

9085
def test_add_with_git_constraint(
@@ -108,7 +103,7 @@ def test_add_with_git_constraint(
108103
"""
109104

110105
assert_plugin_add_result(
111-
tester, expected, {"git": "https://github.com/demo/poetry-plugin.git"}
106+
tester, expected, "https://github.com/demo/poetry-plugin.git"
112107
)
113108

114109

@@ -134,11 +129,11 @@ def test_add_with_git_constraint_with_extras(
134129
Writing lock file
135130
"""
136131

137-
constraint: dict[str, str | list[str]] = {
138-
"git": "https://github.com/demo/poetry-plugin.git",
139-
"extras": ["foo"],
140-
}
141-
assert_plugin_add_result(tester, expected, constraint)
132+
assert_plugin_add_result(
133+
tester,
134+
expected,
135+
"poetry-plugin[foo] @ git+https://github.com/demo/poetry-plugin.git",
136+
)
142137

143138

144139
@pytest.mark.parametrize(
@@ -173,19 +168,7 @@ def test_add_with_git_constraint_with_subdirectory(
173168
Writing lock file
174169
"""
175170

176-
constraint = {
177-
"git": "https://github.com/demo/poetry-plugin2.git",
178-
"subdirectory": "subdir",
179-
}
180-
181-
if rev:
182-
constraint["rev"] = rev
183-
184-
assert_plugin_add_result(
185-
tester,
186-
expected,
187-
constraint,
188-
)
171+
assert_plugin_add_result(tester, expected, url)
189172

190173

191174
def test_add_existing_plugin_warns_about_no_operation(
@@ -248,8 +231,10 @@ def test_add_existing_plugin_updates_if_requested(
248231
[tool.poetry.dependencies]
249232
python = "^3.6"
250233
251-
[tool.poetry.group.{SelfCommand.ADDITIONAL_PACKAGE_GROUP}.dependencies]
252-
poetry-plugin = "^1.2.3"
234+
[dependency-groups]
235+
{SelfCommand.ADDITIONAL_PACKAGE_GROUP} = [
236+
"poetry-plugin (>=1.2.3,<2.0.0)"
237+
]
253238
"""
254239
)
255240

@@ -273,7 +258,7 @@ def test_add_existing_plugin_updates_if_requested(
273258
Writing lock file
274259
"""
275260

276-
assert_plugin_add_result(tester, expected, "^2.3.4")
261+
assert_plugin_add_result(tester, expected, "(>=2.3.4,<3.0.0)")
277262

278263

279264
def test_adding_a_plugin_can_update_poetry_dependencies_if_needed(
@@ -310,4 +295,4 @@ def test_adding_a_plugin_can_update_poetry_dependencies_if_needed(
310295
Writing lock file
311296
"""
312297

313-
assert_plugin_add_result(tester, expected, "^1.2.3")
298+
assert_plugin_add_result(tester, expected, "(>=1.2.3,<2.0.0)")

tests/console/commands/self/test_remove_plugins.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ def install_plugin(installed: Repository) -> None:
6464
installed.add_package(plugin)
6565

6666

67+
@pytest.mark.xfail(reason="remove command does not support dependency-groups yet")
6768
def test_remove_installed_package(tester: CommandTester) -> None:
6869
tester.execute("poetry-plugin")
6970

@@ -85,6 +86,7 @@ def test_remove_installed_package(tester: CommandTester) -> None:
8586
assert not dependencies
8687

8788

89+
@pytest.mark.xfail(reason="remove command does not support dependency-groups yet")
8890
def test_remove_installed_package_dry_run(tester: CommandTester) -> None:
8991
tester.execute("poetry-plugin --dry-run")
9092

tests/console/commands/self/utils.py

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
from pathlib import Path
44
from typing import Any
55

6-
from tomlkit.items import Table as TOMLTable
6+
from tomlkit.items import Array
77

88
from poetry.factory import Factory
99

1010

11-
def get_self_command_dependencies(locked: bool = True) -> TOMLTable:
11+
def get_self_command_dependencies(locked: bool = True) -> Array:
1212
from poetry.console.commands.self.self_command import SelfCommand
1313
from poetry.locations import CONFIG_DIR
1414

@@ -23,14 +23,10 @@ def get_self_command_dependencies(locked: bool = True) -> TOMLTable:
2323
poetry = Factory().create_poetry(system_pyproject_file.parent, disable_plugins=True)
2424

2525
pyproject: dict[str, Any] = poetry.file.read()
26-
content = pyproject["tool"]["poetry"]
26+
content = pyproject["dependency-groups"]
2727

28-
assert "group" in content
29-
assert SelfCommand.ADDITIONAL_PACKAGE_GROUP in content["group"]
30-
assert "dependencies" in content["group"][SelfCommand.ADDITIONAL_PACKAGE_GROUP]
28+
assert SelfCommand.ADDITIONAL_PACKAGE_GROUP in content
3129

32-
dependencies = content["group"][SelfCommand.ADDITIONAL_PACKAGE_GROUP][
33-
"dependencies"
34-
]
35-
assert isinstance(dependencies, TOMLTable)
30+
dependencies = content[SelfCommand.ADDITIONAL_PACKAGE_GROUP]
31+
assert isinstance(dependencies, Array)
3632
return dependencies

0 commit comments

Comments
 (0)