From 790e1568d08998c3e34a3ecd841a501db4ee8907 Mon Sep 17 00:00:00 2001 From: David Hotham Date: Sat, 24 Sep 2022 12:54:41 +0100 Subject: [PATCH] [1.2] feat: forward compatibility for lock file format 2.0 (#6608) Backport the ability to read 2.0 lockfiles per #6393 --- src/poetry/packages/locker.py | 24 ++++++++++++++++++--- tests/packages/test_locker.py | 40 ++++++++++++++++++++++++++++++++++- 2 files changed, 60 insertions(+), 4 deletions(-) diff --git a/src/poetry/packages/locker.py b/src/poetry/packages/locker.py index ceadc1febf0..4cde37acb73 100644 --- a/src/poetry/packages/locker.py +++ b/src/poetry/packages/locker.py @@ -109,8 +109,9 @@ def locked_repository(self) -> Repository: if source_type in ["directory", "file"]: url = self._lock.path.parent.joinpath(url).resolve().as_posix() + name = info["name"] package = Package( - info["name"], + name, info["version"], info["version"], source_type=source_type, @@ -123,8 +124,19 @@ def locked_repository(self) -> Repository: package.category = info.get("category", "main") package.optional = info["optional"] metadata = cast("dict[str, Any]", lock_data["metadata"]) - name = info["name"] - if "hashes" in metadata: + + # Storing of package files and hashes has been through a few generations in + # the lockfile, we can read them all: + # + # - latest and preferred is that this is read per package, from + # package.files + # - oldest is that hashes were stored in metadata.hashes without filenames + # - in between those two, hashes were stored alongside filenames in + # metadata.files + package_files = info.get("files") + if package_files is not None: + package.files = package_files + elif "hashes" in metadata: # Old lock so we create dummy files from the hashes hashes = cast("dict[str, Any]", metadata["hashes"]) package.files = [{"name": h, "hash": h} for h in hashes[name]] @@ -288,6 +300,12 @@ def _get_lock_data(self) -> TOMLDocument: metadata = cast("Table", lock_data["metadata"]) lock_version = Version.parse(metadata.get("lock-version", "1.0")) + + # As a special case: the ability to read a 2.0 lockfile was backported and is + # fully supported. + if lock_version == Version.parse("2.0"): + return lock_data + current_version = Version.parse(self._VERSION) # We expect the locker to be able to read lock files # from the same semantic versioning range diff --git a/tests/packages/test_locker.py b/tests/packages/test_locker.py index 98dbe3a710e..6112c4ece21 100644 --- a/tests/packages/test_locker.py +++ b/tests/packages/test_locker.py @@ -700,14 +700,52 @@ def test_locker_should_emit_warnings_if_lock_version_is_newer_but_allowed( assert record.message == expected -def test_locker_should_raise_an_error_if_lock_version_is_newer_and_not_allowed( +def test_locker_can_read_2_0_lockfile_because_backported( locker: Locker, caplog: LogCaptureFixture ): content = """\ +[[package]] +name = "demo" +version = "1.0" +description = "" +category = "main" +optional = false +python-versions = "*" +develop = false +files = [ + {file = "demo-1.0-cp39-win_amd64.whl", hash = "sha256"}, + {file = "demo-1.0.tar.gz", hash = "sha256"}, + {file = "demo-1.0-py3-none-any.whl", hash = "sha256"}, +] + [metadata] lock-version = "2.0" python-versions = "~2.7 || ^3.4" content-hash = "c3d07fca33fba542ef2b2a4d75bf5b48d892d21a830e2ad9c952ba5123a52f77" +""" + caplog.set_level(logging.WARNING, logger="poetry.packages.locker") + + locker.lock.write(tomlkit.parse(content)) + + repository = locker.locked_repository() + packages = repository.packages + assert len(packages) == 1 + package = packages[0] + assert package.files == [ + {"file": "demo-1.0-cp39-win_amd64.whl", "hash": "sha256"}, + {"file": "demo-1.0.tar.gz", "hash": "sha256"}, + {"file": "demo-1.0-py3-none-any.whl", "hash": "sha256"}, + ] + + +def test_locker_should_raise_an_error_if_lock_version_is_newer_and_not_allowed( + locker: Locker, caplog: LogCaptureFixture +): + content = """\ +[metadata] +lock-version = "2.1" +python-versions = "~2.7 || ^3.4" +content-hash = "c3d07fca33fba542ef2b2a4d75bf5b48d892d21a830e2ad9c952ba5123a52f77" [metadata.files] """