Skip to content

Commit 5ecf4ac

Browse files
committed
use tomli and tomliw to read and write lockfile
1 parent 2d09741 commit 5ecf4ac

File tree

8 files changed

+2904
-916
lines changed

8 files changed

+2904
-916
lines changed

poetry.lock

+2,793-834
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

+2
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ platformdirs = "^2.5.2"
6565
requests = "^2.18"
6666
requests-toolbelt = "^0.9.1"
6767
shellingham = "^1.5"
68+
tomli = "^2.0.1"
69+
tomli-w = "^1.0.0"
6870
# exclude 0.11.2 and 0.11.3 due to https://github.com/sdispater/tomlkit/issues/225
6971
tomlkit = ">=0.11.1,<1.0.0,!=0.11.2,!=0.11.3"
7072
trove-classifiers = "^2022.5.19"

src/poetry/console/commands/add.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ def handle(self) -> int:
233233

234234
# Refresh the locker
235235
self.poetry.set_locker(
236-
self.poetry.locker.__class__(self.poetry.locker.lock.path, poetry_content)
236+
self.poetry.locker.__class__(self.poetry.locker.lock, poetry_content)
237237
)
238238
self.installer.set_locker(self.poetry.locker)
239239

src/poetry/console/commands/remove.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ def handle(self) -> int:
103103

104104
# Refresh the locker
105105
self.poetry.set_locker(
106-
self.poetry.locker.__class__(self.poetry.locker.lock.path, poetry_content)
106+
self.poetry.locker.__class__(self.poetry.locker.lock, poetry_content)
107107
)
108108
self.installer.set_locker(self.poetry.locker)
109109

src/poetry/packages/locker.py

+33-51
Original file line numberDiff line numberDiff line change
@@ -11,30 +11,22 @@
1111
from typing import Any
1212
from typing import cast
1313

14+
import tomli
15+
import tomli_w
16+
1417
from poetry.core.packages.dependency import Dependency
1518
from poetry.core.packages.package import Package
1619
from poetry.core.semver.helpers import parse_constraint
1720
from poetry.core.semver.version import Version
18-
from poetry.core.toml.file import TOMLFile
1921
from poetry.core.version.markers import parse_marker
2022
from poetry.core.version.requirements import InvalidRequirement
21-
from tomlkit import array
22-
from tomlkit import comment
23-
from tomlkit import document
24-
from tomlkit import inline_table
25-
from tomlkit import item
26-
from tomlkit import table
27-
from tomlkit.exceptions import TOMLKitError
28-
from tomlkit.items import Array
2923

3024

3125
if TYPE_CHECKING:
3226
from poetry.core.packages.directory_dependency import DirectoryDependency
3327
from poetry.core.packages.file_dependency import FileDependency
3428
from poetry.core.packages.url_dependency import URLDependency
3529
from poetry.core.packages.vcs_dependency import VCSDependency
36-
from tomlkit.items import Table
37-
from tomlkit.toml_document import TOMLDocument
3830

3931
from poetry.repositories import Repository
4032

@@ -53,17 +45,17 @@ class Locker:
5345
_relevant_keys = [*_legacy_keys, "group"]
5446

5547
def __init__(self, lock: str | Path, local_config: dict[str, Any]) -> None:
56-
self._lock = TOMLFile(lock)
48+
self._lock = lock if isinstance(lock, Path) else Path(lock)
5749
self._local_config = local_config
58-
self._lock_data: TOMLDocument | None = None
50+
self._lock_data: dict[str, Any] | None = None
5951
self._content_hash = self._get_content_hash()
6052

6153
@property
62-
def lock(self) -> TOMLFile:
54+
def lock(self) -> Path:
6355
return self._lock
6456

6557
@property
66-
def lock_data(self) -> TOMLDocument:
58+
def lock_data(self) -> dict[str, Any]:
6759
if self._lock_data is None:
6860
self._lock_data = self._get_lock_data()
6961

@@ -73,7 +65,7 @@ def is_locked(self) -> bool:
7365
"""
7466
Checks whether the locker has been locked (lockfile found).
7567
"""
76-
if not self._lock.exists():
68+
if not self.lock.exists():
7769
return False
7870

7971
return "package" in self.lock_data
@@ -82,7 +74,8 @@ def is_fresh(self) -> bool:
8274
"""
8375
Checks whether the lock file is still up to date with the current hash.
8476
"""
85-
lock = self._lock.read()
77+
with self.lock.open("rb") as f:
78+
lock = tomli.load(f)
8679
metadata = lock.get("metadata", {})
8780

8881
if "content-hash" in metadata:
@@ -113,7 +106,7 @@ def locked_repository(self) -> Repository:
113106
source_type = source.get("type")
114107
url = source.get("url")
115108
if source_type in ["directory", "file"]:
116-
url = self._lock.path.parent.joinpath(url).resolve().as_posix()
109+
url = self.lock.parent.joinpath(url).resolve().as_posix()
117110

118111
package = Package(
119112
info["name"],
@@ -186,7 +179,7 @@ def locked_repository(self) -> Repository:
186179
package.marker = parse_marker(split_dep[1].strip())
187180

188181
for dep_name, constraint in info.get("dependencies", {}).items():
189-
root_dir = self._lock.path.parent
182+
root_dir = self.lock.parent
190183
if package.source_type == "directory":
191184
# root dir should be the source of the package relative to the lock
192185
# path
@@ -213,29 +206,23 @@ def locked_repository(self) -> Repository:
213206
return packages
214207

215208
def set_lock_data(self, root: Package, packages: list[Package]) -> bool:
216-
files: dict[str, Any] = table()
209+
files: dict[str, Any] = {}
217210
package_specs = self._lock_packages(packages)
218211
# Retrieving hashes
219212
for package in package_specs:
220213
if package["name"] not in files:
221214
files[package["name"]] = []
222215

223216
for f in package["files"]:
224-
file_metadata = inline_table()
217+
file_metadata = {}
225218
for k, v in sorted(f.items()):
226219
file_metadata[k] = v
227220

228221
files[package["name"]].append(file_metadata)
229222

230-
if files[package["name"]]:
231-
package_files = item(files[package["name"]])
232-
assert isinstance(package_files, Array)
233-
files[package["name"]] = package_files.multiline(True)
234-
235223
del package["files"]
236224

237-
lock = document()
238-
lock.add(comment(GENERATED_COMMENT))
225+
lock: dict[str, Any] = {}
239226
lock["package"] = package_specs
240227

241228
if root.extras:
@@ -258,12 +245,10 @@ def set_lock_data(self, root: Package, packages: list[Package]) -> bool:
258245

259246
return False
260247

261-
def _write_lock_data(self, data: TOMLDocument) -> None:
262-
self.lock.write(data)
263-
264-
# Checking lock file data consistency
265-
if data != self.lock.read():
266-
raise RuntimeError("Inconsistent lock file data.")
248+
def _write_lock_data(self, data: dict[str, Any]) -> None:
249+
with self.lock.open("wb") as f:
250+
f.write(f"# {GENERATED_COMMENT}\n\n".encode())
251+
tomli_w.dump(data, f)
267252

268253
self._lock_data = None
269254

@@ -284,16 +269,17 @@ def _get_content_hash(self) -> str:
284269

285270
return sha256(json.dumps(relevant_content, sort_keys=True).encode()).hexdigest()
286271

287-
def _get_lock_data(self) -> TOMLDocument:
288-
if not self._lock.exists():
272+
def _get_lock_data(self) -> dict[str, Any]:
273+
if not self.lock.exists():
289274
raise RuntimeError("No lockfile found. Unable to read locked packages")
290275

291-
try:
292-
lock_data: TOMLDocument = self._lock.read()
293-
except TOMLKitError as e:
294-
raise RuntimeError(f"Unable to read the lock file ({e}).")
276+
with self.lock.open("rb") as f:
277+
try:
278+
lock_data = tomli.load(f)
279+
except tomli.TOMLDecodeError as e:
280+
raise RuntimeError(f"Unable to read the lock file ({e}).")
295281

296-
metadata = cast("Table", lock_data["metadata"])
282+
metadata = lock_data["metadata"]
297283
lock_version = Version.parse(metadata.get("lock-version", "1.0"))
298284
current_version = Version.parse(self._VERSION)
299285
# We expect the locker to be able to read lock files
@@ -348,7 +334,7 @@ def _dump_package(self, package: Package) -> dict[str, Any]:
348334
if dependency.pretty_name not in dependencies:
349335
dependencies[dependency.pretty_name] = []
350336

351-
constraint = inline_table()
337+
constraint: dict[str, Any] = {}
352338

353339
if dependency.is_directory():
354340
dependency = cast("DirectoryDependency", dependency)
@@ -414,14 +400,10 @@ def _dump_package(self, package: Package) -> dict[str, Any]:
414400
}
415401

416402
if dependencies:
417-
data["dependencies"] = table()
418-
for k, constraints in dependencies.items():
419-
if len(constraints) == 1:
420-
data["dependencies"][k] = constraints[0]
421-
else:
422-
data["dependencies"][k] = array().multiline(True)
423-
for constraint in constraints:
424-
data["dependencies"][k].append(constraint)
403+
data["dependencies"] = {
404+
name: constraints[0] if len(constraints) == 1 else constraints
405+
for name, constraints in dependencies.items()
406+
}
425407

426408
if package.extras:
427409
extras = {}
@@ -437,7 +419,7 @@ def _dump_package(self, package: Package) -> dict[str, Any]:
437419
url = Path(
438420
os.path.relpath(
439421
Path(url).resolve(),
440-
Path(self._lock.path.parent).resolve(),
422+
Path(self.lock.parent).resolve(),
441423
)
442424
).as_posix()
443425

tests/conftest.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,7 @@ def _factory(
416416
poetry = Factory().create_poetry(project_dir)
417417

418418
locker = TestLocker(
419-
poetry.locker.lock.path, locker_config or poetry.locker._local_config
419+
poetry.locker.lock, locker_config or poetry.locker._local_config
420420
)
421421
locker.write()
422422

tests/helpers.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ def reset_poetry(self) -> None:
182182
self._poetry.set_pool(poetry.pool)
183183
self._poetry.set_config(poetry.config)
184184
self._poetry.set_locker(
185-
TestLocker(poetry.locker.lock.path, self._poetry.local_config)
185+
TestLocker(poetry.locker.lock, self._poetry.local_config)
186186
)
187187

188188

0 commit comments

Comments
 (0)