From a83b5e88291f4501cc49b9601755b84a5eea3760 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Randy=20D=C3=B6ring?= <30527984+radoering@users.noreply.github.com> Date: Sun, 9 Oct 2022 14:52:37 +0200 Subject: [PATCH] locker: enable `poetry lock` if an incompatible lock file exists --- .pre-commit-config.yaml | 1 + src/poetry/installation/installer.py | 2 +- src/poetry/packages/locker.py | 21 +++--- tests/console/commands/test_lock.py | 71 +++++++++++++++++++ tests/fixtures/incompatible_lock/poetry.lock | 2 + .../fixtures/incompatible_lock/pyproject.toml | 15 ++++ tests/fixtures/invalid_lock/poetry.lock | 1 + tests/fixtures/invalid_lock/pyproject.toml | 15 ++++ 8 files changed, 118 insertions(+), 10 deletions(-) create mode 100644 tests/fixtures/incompatible_lock/poetry.lock create mode 100644 tests/fixtures/incompatible_lock/pyproject.toml create mode 100644 tests/fixtures/invalid_lock/poetry.lock create mode 100644 tests/fixtures/invalid_lock/pyproject.toml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index cf3064c6a08..753817ab8e6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -12,6 +12,7 @@ repos: - id: check-case-conflict - id: check-json - id: check-toml + exclude: tests/fixtures/invalid_lock/poetry\.lock - id: check-yaml - id: pretty-format-json args: [--autofix, --no-ensure-ascii, --no-sort-keys] diff --git a/src/poetry/installation/installer.py b/src/poetry/installation/installer.py index 8567d04dd4a..83082020d21 100644 --- a/src/poetry/installation/installer.py +++ b/src/poetry/installation/installer.py @@ -218,7 +218,7 @@ def _do_install(self) -> int: locked_repository = Repository("poetry-locked") if self._update: - if self._locker.is_locked() and not self._lock: + if not self._lock and self._locker.is_locked(): locked_repository = self._locker.locked_repository() # If no packages have been whitelisted (The ones we want to update), diff --git a/src/poetry/packages/locker.py b/src/poetry/packages/locker.py index 8a6c62e75f7..b9df3e15456 100644 --- a/src/poetry/packages/locker.py +++ b/src/poetry/packages/locker.py @@ -73,10 +73,7 @@ def is_locked(self) -> bool: """ Checks whether the locker has been locked (lockfile found). """ - if not self._lock.exists(): - return False - - return "package" in self.lock_data + return self._lock.exists() def is_fresh(self) -> bool: """ @@ -256,12 +253,18 @@ def set_lock_data(self, root: Package, packages: list[Package]) -> bool: "content-hash": self._content_hash, } - if not self.is_locked() or lock != self.lock_data: + do_write = True + if self.is_locked(): + try: + lock_data = self.lock_data + except RuntimeError: + # incompatible, invalid or no lock file + pass + else: + do_write = lock != lock_data + if do_write: self._write_lock_data(lock) - - return True - - return False + return do_write def _write_lock_data(self, data: TOMLDocument) -> None: self.lock.write(data) diff --git a/tests/console/commands/test_lock.py b/tests/console/commands/test_lock.py index 5d6eb53887e..69de642e117 100644 --- a/tests/console/commands/test_lock.py +++ b/tests/console/commands/test_lock.py @@ -67,6 +67,20 @@ def poetry_with_old_lockfile( return _project_factory("old_lock", project_factory, fixture_dir) +@pytest.fixture +def poetry_with_incompatible_lockfile( + project_factory: ProjectFactory, fixture_dir: FixtureDirGetter +) -> Poetry: + return _project_factory("incompatible_lock", project_factory, fixture_dir) + + +@pytest.fixture +def poetry_with_invalid_lockfile( + project_factory: ProjectFactory, fixture_dir: FixtureDirGetter +) -> Poetry: + return _project_factory("invalid_lock", project_factory, fixture_dir) + + def test_lock_check_outdated( command_tester_factory: CommandTesterFactory, poetry_with_outdated_lockfile: Poetry, @@ -150,3 +164,60 @@ def test_lock_no_update( for package in packages: assert locked_repository.find_packages(package.to_dependency()) + + +@pytest.mark.parametrize("is_no_update", [False, True]) +def test_lock_with_incompatible_lockfile( + command_tester_factory: CommandTesterFactory, + poetry_with_incompatible_lockfile: Poetry, + repo: TestRepository, + is_no_update: bool, +) -> None: + repo.add_package(get_package("sampleproject", "1.3.1")) + + locker = Locker( + lock=poetry_with_incompatible_lockfile.pyproject.file.path.parent + / "poetry.lock", + local_config=poetry_with_incompatible_lockfile.locker._local_config, + ) + poetry_with_incompatible_lockfile.set_locker(locker) + + tester = command_tester_factory("lock", poetry=poetry_with_incompatible_lockfile) + if is_no_update: + # not possible because of incompatible lock file + expected = ( + "(?s)lock file is not compatible .*" + " regenerate the lock file with the `poetry lock` command" + ) + with pytest.raises(RuntimeError, match=expected): + tester.execute("--no-update") + else: + # still possible because lock file is not required + status_code = tester.execute() + assert status_code == 0 + + +@pytest.mark.parametrize("is_no_update", [False, True]) +def test_lock_with_invalid_lockfile( + command_tester_factory: CommandTesterFactory, + poetry_with_invalid_lockfile: Poetry, + repo: TestRepository, + is_no_update: bool, +) -> None: + repo.add_package(get_package("sampleproject", "1.3.1")) + + locker = Locker( + lock=poetry_with_invalid_lockfile.pyproject.file.path.parent / "poetry.lock", + local_config=poetry_with_invalid_lockfile.locker._local_config, + ) + poetry_with_invalid_lockfile.set_locker(locker) + + tester = command_tester_factory("lock", poetry=poetry_with_invalid_lockfile) + if is_no_update: + # not possible because of broken lock file + with pytest.raises(RuntimeError, match="Unable to read the lock file"): + tester.execute("--no-update") + else: + # still possible because lock file is not required + status_code = tester.execute() + assert status_code == 0 diff --git a/tests/fixtures/incompatible_lock/poetry.lock b/tests/fixtures/incompatible_lock/poetry.lock new file mode 100644 index 00000000000..37615c8ce57 --- /dev/null +++ b/tests/fixtures/incompatible_lock/poetry.lock @@ -0,0 +1,2 @@ +[metadata] +lock-version = "999.0" diff --git a/tests/fixtures/incompatible_lock/pyproject.toml b/tests/fixtures/incompatible_lock/pyproject.toml new file mode 100644 index 00000000000..377aa676be9 --- /dev/null +++ b/tests/fixtures/incompatible_lock/pyproject.toml @@ -0,0 +1,15 @@ +[tool.poetry] +name = "foobar" +version = "0.1.0" +description = "" +authors = ["Poetry Developer "] + +[tool.poetry.dependencies] +python = "^3.8" +sampleproject = ">=1.3.1" + +[tool.poetry.dev-dependencies] + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" diff --git a/tests/fixtures/invalid_lock/poetry.lock b/tests/fixtures/invalid_lock/poetry.lock new file mode 100644 index 00000000000..59f4e044a6c --- /dev/null +++ b/tests/fixtures/invalid_lock/poetry.lock @@ -0,0 +1 @@ +This lock file is broken! diff --git a/tests/fixtures/invalid_lock/pyproject.toml b/tests/fixtures/invalid_lock/pyproject.toml new file mode 100644 index 00000000000..377aa676be9 --- /dev/null +++ b/tests/fixtures/invalid_lock/pyproject.toml @@ -0,0 +1,15 @@ +[tool.poetry] +name = "foobar" +version = "0.1.0" +description = "" +authors = ["Poetry Developer "] + +[tool.poetry.dependencies] +python = "^3.8" +sampleproject = ">=1.3.1" + +[tool.poetry.dev-dependencies] + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api"