Skip to content

Commit 8d8616b

Browse files
committed
use tomli and tomliw to read and write lockfile
1 parent 8e8aa44 commit 8d8616b

File tree

8 files changed

+129
-93
lines changed

8 files changed

+129
-93
lines changed

poetry.lock

+14-2
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,31 +11,23 @@
1111
from typing import Any
1212
from typing import cast
1313

14+
import tomli
15+
import tomli_w
16+
1417
from packaging.utils import canonicalize_name
1518
from poetry.core.constraints.version import Version
1619
from poetry.core.constraints.version import parse_constraint
1720
from poetry.core.packages.dependency import Dependency
1821
from poetry.core.packages.package import Package
19-
from poetry.core.toml.file import TOMLFile
2022
from poetry.core.version.markers import parse_marker
2123
from poetry.core.version.requirements import InvalidRequirement
22-
from tomlkit import array
23-
from tomlkit import comment
24-
from tomlkit import document
25-
from tomlkit import inline_table
26-
from tomlkit import item
27-
from tomlkit import table
28-
from tomlkit.exceptions import TOMLKitError
29-
from tomlkit.items import Array
3024

3125

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

4032
from poetry.repositories.lockfile_repository import LockfileRepository
4133

@@ -55,17 +47,17 @@ class Locker:
5547
_relevant_keys = [*_legacy_keys, "group"]
5648

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

6355
@property
64-
def lock(self) -> TOMLFile:
56+
def lock(self) -> Path:
6557
return self._lock
6658

6759
@property
68-
def lock_data(self) -> TOMLDocument:
60+
def lock_data(self) -> dict[str, Any]:
6961
if self._lock_data is None:
7062
self._lock_data = self._get_lock_data()
7163

@@ -75,7 +67,7 @@ def is_locked(self) -> bool:
7567
"""
7668
Checks whether the locker has been locked (lockfile found).
7769
"""
78-
if not self._lock.exists():
70+
if not self.lock.exists():
7971
return False
8072

8173
return "package" in self.lock_data
@@ -84,7 +76,8 @@ def is_fresh(self) -> bool:
8476
"""
8577
Checks whether the lock file is still up to date with the current hash.
8678
"""
87-
lock = self._lock.read()
79+
with self.lock.open("rb") as f:
80+
lock = tomli.load(f)
8881
metadata = lock.get("metadata", {})
8982

9083
if "content-hash" in metadata:
@@ -116,7 +109,7 @@ def locked_repository(self) -> LockfileRepository:
116109
source_type = source.get("type")
117110
url = source.get("url")
118111
if source_type in ["directory", "file"]:
119-
url = self._lock.path.parent.joinpath(url).resolve().as_posix()
112+
url = self.lock.parent.joinpath(url).resolve().as_posix()
120113

121114
name = info["name"]
122115
package = Package(
@@ -201,7 +194,7 @@ def locked_repository(self) -> LockfileRepository:
201194
package.marker = parse_marker(split_dep[1].strip())
202195

203196
for dep_name, constraint in info.get("dependencies", {}).items():
204-
root_dir = self._lock.path.parent
197+
root_dir = self.lock.parent
205198
if package.source_type == "directory":
206199
# root dir should be the source of the package relative to the lock
207200
# path
@@ -228,27 +221,21 @@ def locked_repository(self) -> LockfileRepository:
228221
return repository
229222

230223
def set_lock_data(self, root: Package, packages: list[Package]) -> bool:
231-
files: dict[str, Any] = table()
224+
files: dict[str, Any] = {}
232225
package_specs = self._lock_packages(packages)
233226
# Retrieving hashes
234227
for package in package_specs:
235228
if package["name"] not in files:
236229
files[package["name"]] = []
237230

238231
for f in package["files"]:
239-
file_metadata = inline_table()
232+
file_metadata = {}
240233
for k, v in sorted(f.items()):
241234
file_metadata[k] = v
242235

243236
files[package["name"]].append(file_metadata)
244237

245-
if files[package["name"]]:
246-
package_files = item(files[package["name"]])
247-
assert isinstance(package_files, Array)
248-
files[package["name"]] = package_files.multiline(True)
249-
250-
lock = document()
251-
lock.add(comment(GENERATED_COMMENT))
238+
lock: dict[str, Any] = {}
252239
lock["package"] = package_specs
253240

254241
if root.extras:
@@ -270,12 +257,10 @@ def set_lock_data(self, root: Package, packages: list[Package]) -> bool:
270257

271258
return False
272259

273-
def _write_lock_data(self, data: TOMLDocument) -> None:
274-
self.lock.write(data)
275-
276-
# Checking lock file data consistency
277-
if data != self.lock.read():
278-
raise RuntimeError("Inconsistent lock file data.")
260+
def _write_lock_data(self, data: dict[str, Any]) -> None:
261+
with self.lock.open("wb") as f:
262+
f.write(f"# {GENERATED_COMMENT}\n\n".encode())
263+
tomli_w.dump(data, f)
279264

280265
self._lock_data = None
281266

@@ -296,16 +281,17 @@ def _get_content_hash(self) -> str:
296281

297282
return sha256(json.dumps(relevant_content, sort_keys=True).encode()).hexdigest()
298283

299-
def _get_lock_data(self) -> TOMLDocument:
300-
if not self._lock.exists():
284+
def _get_lock_data(self) -> dict[str, Any]:
285+
if not self.lock.exists():
301286
raise RuntimeError("No lockfile found. Unable to read locked packages")
302287

303-
try:
304-
lock_data: TOMLDocument = self._lock.read()
305-
except TOMLKitError as e:
306-
raise RuntimeError(f"Unable to read the lock file ({e}).")
288+
with self.lock.open("rb") as f:
289+
try:
290+
lock_data = tomli.load(f)
291+
except tomli.TOMLDecodeError as e:
292+
raise RuntimeError(f"Unable to read the lock file ({e}).")
307293

308-
metadata = cast("Table", lock_data["metadata"])
294+
metadata = lock_data["metadata"]
309295
lock_version = Version.parse(metadata.get("lock-version", "1.0"))
310296
current_version = Version.parse(self._VERSION)
311297
accepted_versions = parse_constraint(self._READ_VERSION_RANGE)
@@ -356,7 +342,7 @@ def _dump_package(self, package: Package) -> dict[str, Any]:
356342
if dependency.pretty_name not in dependencies:
357343
dependencies[dependency.pretty_name] = []
358344

359-
constraint = inline_table()
345+
constraint: dict[str, Any] = {}
360346

361347
if dependency.is_directory():
362348
dependency = cast("DirectoryDependency", dependency)
@@ -422,14 +408,10 @@ def _dump_package(self, package: Package) -> dict[str, Any]:
422408
}
423409

424410
if dependencies:
425-
data["dependencies"] = table()
426-
for k, constraints in dependencies.items():
427-
if len(constraints) == 1:
428-
data["dependencies"][k] = constraints[0]
429-
else:
430-
data["dependencies"][k] = array().multiline(True)
431-
for constraint in constraints:
432-
data["dependencies"][k].append(constraint)
411+
data["dependencies"] = {
412+
name: constraints[0] if len(constraints) == 1 else constraints
413+
for name, constraints in dependencies.items()
414+
}
433415

434416
if package.extras:
435417
extras = {}
@@ -445,7 +427,7 @@ def _dump_package(self, package: Package) -> dict[str, Any]:
445427
url = Path(
446428
os.path.relpath(
447429
Path(url).resolve(),
448-
Path(self._lock.path.parent).resolve(),
430+
Path(self.lock.parent).resolve(),
449431
)
450432
).as_posix()
451433

tests/conftest.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,7 @@ def _factory(
412412
poetry = Factory().create_poetry(project_dir)
413413

414414
locker = TestLocker(
415-
poetry.locker.lock.path, locker_config or poetry.locker._local_config
415+
poetry.locker.lock, locker_config or poetry.locker._local_config
416416
)
417417
locker.write()
418418

tests/helpers.py

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

187187

0 commit comments

Comments
 (0)