Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(locker): poetry lock works if an invalid/incompatible lock file exists #6753

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
2 changes: 1 addition & 1 deletion src/poetry/installation/installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
21 changes: 12 additions & 9 deletions src/poetry/packages/locker.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
"""
Expand Down Expand Up @@ -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)
Expand Down
71 changes: 71 additions & 0 deletions tests/console/commands/test_lock.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
2 changes: 2 additions & 0 deletions tests/fixtures/incompatible_lock/poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions tests/fixtures/incompatible_lock/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[tool.poetry]
name = "foobar"
version = "0.1.0"
description = ""
authors = ["Poetry Developer <developer@python-poetry.org>"]

[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"
1 change: 1 addition & 0 deletions tests/fixtures/invalid_lock/poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions tests/fixtures/invalid_lock/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[tool.poetry]
name = "foobar"
version = "0.1.0"
description = ""
authors = ["Poetry Developer <developer@python-poetry.org>"]

[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"