Skip to content

Commit 98fd668

Browse files
dimblebyCopilot
andcommitted
feat: migrate from tomlkit to tomlrt
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 811a12d commit 98fd668

38 files changed

Lines changed: 1532 additions & 1672 deletions

poetry.lock

Lines changed: 1153 additions & 1150 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ dependencies = [
2121
"requests-toolbelt (>=1.0.0,<2.0.0)",
2222
"shellingham (>=1.5,<2.0)",
2323
"tomli (>=2.0.1,<3.0.0) ; python_version < '3.11'",
24-
"tomlkit (>=0.11.4,<1.0.0)",
24+
"tomlrt (>=1.4,<2)",
2525
# trove-classifiers uses calver, so version is unclamped
2626
"trove-classifiers (>=2022.5.19)",
2727
"virtualenv (>=20.26.6)",

src/poetry/config/file_config_source.py

Lines changed: 13 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
44
from typing import TYPE_CHECKING
55
from typing import Any
66

7-
from tomlkit import document
8-
from tomlkit import table
7+
from tomlrt import Document
98

109
from poetry.config.config_source import ConfigSource
1110
from poetry.config.config_source import PropertyNotFoundError
@@ -16,8 +15,6 @@
1615
from collections.abc import Iterator
1716
from collections.abc import Sequence
1817

19-
from tomlkit.toml_document import TOMLDocument
20-
2118
from poetry.toml.file import TOMLFile
2219

2320

@@ -34,37 +31,19 @@ def file(self) -> TOMLFile:
3431
return self._file
3532

3633
def get_property(self, key: str | Sequence[str]) -> Any:
37-
keys = split_key(key)
38-
39-
config = self.file.read() if self.file.exists() else {}
40-
41-
for i, sub_key in enumerate(keys):
42-
if sub_key not in config:
43-
raise PropertyNotFoundError(f"Key {'.'.join(keys)} not in config")
44-
45-
if i == len(keys) - 1:
46-
return config[sub_key]
47-
48-
config = config[sub_key]
49-
50-
def add_property(self, key: str | Sequence[str], value: Any) -> None:
51-
with self.secure() as toml:
52-
config: dict[str, Any] = toml
34+
config = self.file.read() if self.file.exists() else Document()
35+
try:
36+
return config.entry(key)
37+
except KeyError:
5338
keys = split_key(key)
39+
raise PropertyNotFoundError(f"Key {'.'.join(keys)} not in config")
5440

55-
for i, sub_key in enumerate(keys):
56-
if sub_key not in config and i < len(keys) - 1:
57-
config[sub_key] = table()
58-
59-
if i == len(keys) - 1:
60-
config[sub_key] = value
61-
break
62-
63-
config = config[sub_key]
41+
def add_property(self, key: str | Sequence[str], value: Any) -> None:
42+
with self.secure() as config:
43+
config.install(key, value)
6444

6545
def remove_property(self, key: str | Sequence[str]) -> None:
66-
with self.secure() as toml:
67-
config: dict[str, Any] = toml
46+
with self.secure() as config:
6847
keys = split_key(key)
6948

7049
# Descend to the leaf, recording the (parent, key) at each step.
@@ -84,13 +63,13 @@ def remove_property(self, key: str | Sequence[str]) -> None:
8463
break
8564

8665
@contextmanager
87-
def secure(self) -> Iterator[TOMLDocument]:
66+
def secure(self) -> Iterator[Document]:
8867
if self.file.exists():
8968
initial_config = self.file.read()
9069
config = self.file.read()
9170
else:
92-
initial_config = document()
93-
config = document()
71+
initial_config = Document()
72+
config = Document()
9473

9574
new_file = not self.file.exists()
9675

src/poetry/config/source.py

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99

1010
if TYPE_CHECKING:
11-
from tomlkit.items import Table
11+
from tomlrt import Table
1212

1313

1414
@dataclasses.dataclass(order=True, eq=True)
@@ -34,11 +34,6 @@ def to_dict(self) -> dict[str, str | bool]:
3434
)
3535

3636
def to_toml_table(self) -> Table:
37-
from tomlkit import nl
38-
from tomlkit import table
39-
40-
source_table: Table = table()
41-
for key, value in self.to_dict().items():
42-
source_table.add(key, value)
43-
source_table.add(nl())
44-
return source_table
37+
from tomlrt import Table
38+
39+
return Table.section(self.to_dict())

src/poetry/console/commands/add.py

Lines changed: 23 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
from packaging.utils import canonicalize_name
1313
from poetry.core.packages.dependency import Dependency
1414
from poetry.core.packages.dependency_group import MAIN_GROUP
15-
from tomlkit.toml_document import TOMLDocument
1615

1716
from poetry.console.commands.init import InitCommand
1817
from poetry.console.commands.installer_command import InstallerCommand
@@ -126,10 +125,8 @@ class AddCommand(InstallerCommand, InitCommand):
126125

127126
def handle(self) -> int:
128127
from poetry.core.constraints.version import parse_constraint
129-
from tomlkit import array
130-
from tomlkit import inline_table
131-
from tomlkit import nl
132-
from tomlkit import table
128+
from tomlrt import Array
129+
from tomlrt import Table
133130

134131
from poetry.factory import Factory
135132

@@ -148,11 +145,9 @@ def handle(self) -> int:
148145
if optional and group != MAIN_GROUP:
149146
raise ValueError("You can only add optional dependencies to the main group")
150147

151-
# tomlkit types are awkward to work with, treat content as a mostly untyped
152-
# dictionary.
153-
content: dict[str, Any] = self.poetry.file.read()
154-
project_content = content.get("project", table())
155-
poetry_content = content.get("tool", {}).get("poetry", table())
148+
content = self.poetry.file.read()
149+
project_content = content.get("project", Table.section())
150+
poetry_content = content.get("tool", {}).get("poetry", Table.section())
156151
groups_content = content.get("dependency-groups", {})
157152
project_name = (
158153
canonicalize_name(name)
@@ -174,25 +169,24 @@ def handle(self) -> int:
174169
if optional:
175170
project_section = project_content.get(
176171
"optional-dependencies", {}
177-
).get(optional, array())
172+
).get(optional, Array())
178173
else:
179-
project_section = project_content.get("dependencies", array())
174+
project_section = project_content.get("dependencies", Array())
180175
project_dependency_names = [
181176
Dependency.create_from_pep_508(dep).name for dep in project_section
182177
]
183178
else:
184-
project_section = array()
179+
project_section = Array()
185180

186-
poetry_section = poetry_content.get("dependencies", table())
181+
poetry_section = poetry_content.get("dependencies", Table.section())
187182

188183
# Dependency Groups
189184
else:
190185
if groups_content or "group" not in poetry_content:
191186
use_groups_section = True
192187
if not groups_content:
193-
groups_content = table(is_super_table=True)
194-
if group not in groups_content:
195-
groups_content[group] = array("[\n]")
188+
groups_content = Table.section()
189+
groups_content.setdefault(group, Array(multiline=True))
196190

197191
project_dependency_names = [
198192
Dependency.create_from_pep_508(dep).name
@@ -207,7 +201,7 @@ def handle(self) -> int:
207201
poetry_section = (
208202
poetry_content.get("group", {})
209203
.get(group, {})
210-
.get("dependencies", table())
204+
.get("dependencies", Table.section())
211205
)
212206
project_section = []
213207

@@ -244,7 +238,7 @@ def handle(self) -> int:
244238
assert isinstance(version, str)
245239
parse_constraint(version)
246240

247-
constraint: dict[str, Any] = inline_table()
241+
constraint: dict[str, Any] = Table.inline()
248242
for key, value in _constraint.items():
249243
if key == "name":
250244
continue
@@ -331,7 +325,7 @@ def handle(self) -> int:
331325

332326
# create a second constraint for tool.poetry.dependencies with keys
333327
# that cannot be stored in the project section
334-
poetry_constraint: dict[str, Any] = inline_table()
328+
poetry_constraint: dict[str, Any] = Table.inline()
335329
if not isinstance(constraint, str):
336330
for key in ["allow-prereleases", "develop", "source"]:
337331
if value := constraint.get(key):
@@ -363,38 +357,23 @@ def handle(self) -> int:
363357
if project_section:
364358
assert group == MAIN_GROUP
365359
if optional:
366-
if "optional-dependencies" not in project_content:
367-
project_content["optional-dependencies"] = table()
368-
if optional not in project_content["optional-dependencies"]:
369-
project_content["optional-dependencies"][optional] = project_section
360+
opt_deps_table = project_content.ensure_table("optional-dependencies")
361+
opt_deps_table.setdefault(optional, project_section)
370362
elif "dependencies" not in project_content:
371363
project_content["dependencies"] = project_section
372364

373365
if poetry_section:
374-
if "tool" not in content:
375-
content["tool"] = table()
376-
if "poetry" not in content["tool"]:
377-
content["tool"]["poetry"] = poetry_content
366+
tool_table = content.ensure_table("tool")
367+
tool_table.setdefault("poetry", poetry_content)
368+
378369
if group == MAIN_GROUP:
379-
if "dependencies" not in poetry_content:
380-
poetry_content["dependencies"] = poetry_section
370+
poetry_content.setdefault("dependencies", poetry_section)
381371
else:
382-
if "group" not in poetry_content:
383-
poetry_content["group"] = table(is_super_table=True)
384-
385-
groups = poetry_content["group"]
386-
387-
if group not in groups:
388-
groups[group] = table()
389-
groups.add(nl())
390-
391-
if "dependencies" not in groups[group]:
392-
groups[group]["dependencies"] = poetry_section
372+
group_table = poetry_content.ensure_table(("group", group))
373+
group_table.setdefault("dependencies", poetry_section)
393374

394375
if groups_content and group != MAIN_GROUP:
395-
if "dependency-groups" not in content:
396-
content["dependency-groups"] = table()
397-
content["dependency-groups"][group] = groups_content[group]
376+
content.install(("dependency-groups", group), groups_content[group])
398377

399378
self.poetry.locker.set_pyproject_data(content)
400379
self.installer.set_locker(self.poetry.locker)
@@ -413,7 +392,6 @@ def handle(self) -> int:
413392
status = self.installer.run()
414393

415394
if status == 0 and not self.option("dry-run"):
416-
assert isinstance(content, TOMLDocument)
417395
self.poetry.file.write(content)
418396

419397
return status

src/poetry/console/commands/init.py

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
from cleo.helpers import option
1313
from packaging.utils import canonicalize_name
14-
from tomlkit import inline_table
14+
from tomlrt import Table
1515

1616
from poetry.console.commands.command import Command
1717
from poetry.console.commands.env_command import EnvCommand
@@ -23,7 +23,6 @@
2323
from cleo.io.inputs.option import Option
2424
from packaging.utils import NormalizedName
2525
from poetry.core.packages.package import Package
26-
from tomlkit.items import InlineTable
2726

2827
from poetry.repositories import RepositoryPool
2928

@@ -253,13 +252,12 @@ def _init_pyproject(
253252
layout_.create(project_path, with_pyproject=False)
254253

255254
content = layout_.generate_project_content(project_path)
256-
for section, item in content.items():
257-
pyproject.data.append(section, item)
255+
pyproject.data.update(content)
258256

259257
if is_interactive:
260258
self.line("<info>Generated file</info>")
261259
self.line("")
262-
self.line(pyproject.data.as_string().replace("\r\n", "\n"))
260+
self.line(pyproject.data.render().replace("\r\n", "\n"))
263261
self.line("")
264262

265263
if is_interactive and not self.confirm("Do you confirm generation?", True):
@@ -483,13 +481,11 @@ def _format_requirements(self, requirements: list[dict[str, str]]) -> Requiremen
483481
requires: Requirements = {}
484482
for requirement in requirements:
485483
name = requirement.pop("name")
486-
constraint: str | InlineTable
484+
constraint: str | Table
487485
if "version" in requirement and len(requirement) == 1:
488486
constraint = requirement["version"]
489487
else:
490-
constraint = inline_table()
491-
constraint.trivia.trail = "\n"
492-
constraint.update(requirement)
488+
constraint = Table.inline(requirement)
493489

494490
requires[name] = constraint
495491

src/poetry/console/commands/remove.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
from packaging.utils import canonicalize_name
1010
from poetry.core.packages.dependency import Dependency
1111
from poetry.core.packages.dependency_group import MAIN_GROUP
12-
from tomlkit.toml_document import TOMLDocument
1312

1413
from poetry.console.commands.installer_command import InstallerCommand
1514

@@ -63,7 +62,7 @@ def handle(self) -> int:
6362
else:
6463
group = self.option("group", self.default_group)
6564

66-
content: dict[str, Any] = self.poetry.file.read()
65+
content = self.poetry.file.read()
6766
project_content = content.get("project", {})
6867
groups_content = content.get("dependency-groups", {})
6968
poetry_content = content.get("tool", {}).get("poetry", {})
@@ -179,7 +178,6 @@ def handle(self) -> int:
179178
status = self.installer.run()
180179

181180
if not self.option("dry-run") and status == 0:
182-
assert isinstance(content, TOMLDocument)
183181
self.poetry.file.write(content)
184182

185183
return status

src/poetry/console/commands/self/self_command.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,10 @@ def generate_system_pyproject(self) -> None:
8787
package.python_versions = ".".join(str(v) for v in self.env.version_info[:3])
8888

8989
content = Factory.create_legacy_pyproject_from_package(package=package)
90-
content["tool"]["poetry"]["package-mode"] = False # type: ignore[index]
90+
content["tool"]["poetry"]["package-mode"] = False
9191

9292
for key in preserved:
93-
content["tool"]["poetry"][key] = preserved[key] # type: ignore[index]
93+
content["tool"]["poetry"][key] = preserved[key]
9494

9595
if preserved_groups:
9696
content["dependency-groups"] = preserved_groups

src/poetry/console/commands/source/add.py

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
from __future__ import annotations
22

33
from typing import TYPE_CHECKING
4-
from typing import Any
54
from typing import ClassVar
65

76
from cleo.helpers import argument
87
from cleo.helpers import option
98
from cleo.io.null_io import NullIO
10-
from tomlkit import table
11-
from tomlkit.items import AoT
9+
from tomlrt import AoT
1210

1311
from poetry.config.source import Source
1412
from poetry.console.commands.command import Command
@@ -106,13 +104,8 @@ def handle(self) -> int:
106104
)
107105
return 1
108106

109-
# tomlkit types are awkward to work with, treat content as a mostly untyped
110-
# dictionary.
111-
content: dict[str, Any] = self.poetry.pyproject.data
112-
if "tool" not in content:
113-
content["tool"] = table()
114-
if "poetry" not in content["tool"]:
115-
content["tool"]["poetry"] = table()
107+
content = self.poetry.pyproject.data
108+
content.ensure_table("tool.poetry")
116109
self.poetry.pyproject.poetry_config["source"] = sources
117110
self.poetry.pyproject.save()
118111

src/poetry/console/commands/source/remove.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from typing import ClassVar
55

66
from cleo.helpers import argument
7-
from tomlkit.items import AoT
7+
from tomlrt import AoT
88

99
from poetry.console.commands.command import Command
1010

0 commit comments

Comments
 (0)