Skip to content

Commit f1cf0ac

Browse files
radoeringneersighted
authored andcommitted
feat(locker): poetry lock works if an invalid/incompatible lock file exists (#6753)
After having created a lock file 2.0, running `poetry lock` with poetry 1.2.1 results in the following output: ``` The lock file is not compatible with the current version of Poetry. Upgrade Poetry to be able to read the lock file or, alternatively, regenerate the lock file with the `poetry lock` command. ``` Ironically, the error message proposes to run `poetry lock` which results in this error message. Further, it doesn't make sense that `poetry lock` fails because it creates a new lock file from scratch (in contrast to `poetry lock --no-update`). Running `poetry lock` is now also possible if there is a broken lock file. Resolves: #1196 (cherry picked from commit 7d414af)
1 parent 073c2dd commit f1cf0ac

File tree

8 files changed

+118
-10
lines changed

8 files changed

+118
-10
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ repos:
1212
- id: check-case-conflict
1313
- id: check-json
1414
- id: check-toml
15+
exclude: tests/fixtures/invalid_lock/poetry\.lock
1516
- id: check-yaml
1617
- id: pretty-format-json
1718
args: [--autofix, --no-ensure-ascii, --no-sort-keys]

src/poetry/installation/installer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ def _do_install(self) -> int:
219219

220220
locked_repository = Repository("poetry-locked")
221221
if self._update:
222-
if self._locker.is_locked() and not self._lock:
222+
if not self._lock and self._locker.is_locked():
223223
locked_repository = self._locker.locked_repository()
224224

225225
# If no packages have been whitelisted (The ones we want to update),

src/poetry/packages/locker.py

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,7 @@ def is_locked(self) -> bool:
6868
"""
6969
Checks whether the locker has been locked (lockfile found).
7070
"""
71-
if not self._lock.exists():
72-
return False
73-
74-
return "package" in self.lock_data
71+
return self._lock.exists()
7572

7673
def is_fresh(self) -> bool:
7774
"""
@@ -258,12 +255,18 @@ def set_lock_data(self, root: Package, packages: list[Package]) -> bool:
258255
"files": files,
259256
}
260257

261-
if not self.is_locked() or lock != self.lock_data:
258+
do_write = True
259+
if self.is_locked():
260+
try:
261+
lock_data = self.lock_data
262+
except RuntimeError:
263+
# incompatible, invalid or no lock file
264+
pass
265+
else:
266+
do_write = lock != lock_data
267+
if do_write:
262268
self._write_lock_data(lock)
263-
264-
return True
265-
266-
return False
269+
return do_write
267270

268271
def _write_lock_data(self, data: TOMLDocument) -> None:
269272
self.lock.write(data)

tests/console/commands/test_lock.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,20 @@ def poetry_with_old_lockfile(
6767
return _project_factory("old_lock", project_factory, fixture_dir)
6868

6969

70+
@pytest.fixture
71+
def poetry_with_incompatible_lockfile(
72+
project_factory: ProjectFactory, fixture_dir: FixtureDirGetter
73+
) -> Poetry:
74+
return _project_factory("incompatible_lock", project_factory, fixture_dir)
75+
76+
77+
@pytest.fixture
78+
def poetry_with_invalid_lockfile(
79+
project_factory: ProjectFactory, fixture_dir: FixtureDirGetter
80+
) -> Poetry:
81+
return _project_factory("invalid_lock", project_factory, fixture_dir)
82+
83+
7084
def test_lock_check_outdated(
7185
command_tester_factory: CommandTesterFactory,
7286
poetry_with_outdated_lockfile: Poetry,
@@ -150,3 +164,60 @@ def test_lock_no_update(
150164

151165
for package in packages:
152166
assert locked_repository.find_packages(package.to_dependency())
167+
168+
169+
@pytest.mark.parametrize("is_no_update", [False, True])
170+
def test_lock_with_incompatible_lockfile(
171+
command_tester_factory: CommandTesterFactory,
172+
poetry_with_incompatible_lockfile: Poetry,
173+
repo: TestRepository,
174+
is_no_update: bool,
175+
) -> None:
176+
repo.add_package(get_package("sampleproject", "1.3.1"))
177+
178+
locker = Locker(
179+
lock=poetry_with_incompatible_lockfile.pyproject.file.path.parent
180+
/ "poetry.lock",
181+
local_config=poetry_with_incompatible_lockfile.locker._local_config,
182+
)
183+
poetry_with_incompatible_lockfile.set_locker(locker)
184+
185+
tester = command_tester_factory("lock", poetry=poetry_with_incompatible_lockfile)
186+
if is_no_update:
187+
# not possible because of incompatible lock file
188+
expected = (
189+
"(?s)lock file is not compatible .*"
190+
" regenerate the lock file with the `poetry lock` command"
191+
)
192+
with pytest.raises(RuntimeError, match=expected):
193+
tester.execute("--no-update")
194+
else:
195+
# still possible because lock file is not required
196+
status_code = tester.execute()
197+
assert status_code == 0
198+
199+
200+
@pytest.mark.parametrize("is_no_update", [False, True])
201+
def test_lock_with_invalid_lockfile(
202+
command_tester_factory: CommandTesterFactory,
203+
poetry_with_invalid_lockfile: Poetry,
204+
repo: TestRepository,
205+
is_no_update: bool,
206+
) -> None:
207+
repo.add_package(get_package("sampleproject", "1.3.1"))
208+
209+
locker = Locker(
210+
lock=poetry_with_invalid_lockfile.pyproject.file.path.parent / "poetry.lock",
211+
local_config=poetry_with_invalid_lockfile.locker._local_config,
212+
)
213+
poetry_with_invalid_lockfile.set_locker(locker)
214+
215+
tester = command_tester_factory("lock", poetry=poetry_with_invalid_lockfile)
216+
if is_no_update:
217+
# not possible because of broken lock file
218+
with pytest.raises(RuntimeError, match="Unable to read the lock file"):
219+
tester.execute("--no-update")
220+
else:
221+
# still possible because lock file is not required
222+
status_code = tester.execute()
223+
assert status_code == 0

tests/fixtures/incompatible_lock/poetry.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[tool.poetry]
2+
name = "foobar"
3+
version = "0.1.0"
4+
description = ""
5+
authors = ["Poetry Developer <[email protected]>"]
6+
7+
[tool.poetry.dependencies]
8+
python = "^3.8"
9+
sampleproject = ">=1.3.1"
10+
11+
[tool.poetry.dev-dependencies]
12+
13+
[build-system]
14+
requires = ["poetry-core>=1.0.0"]
15+
build-backend = "poetry.core.masonry.api"

tests/fixtures/invalid_lock/poetry.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[tool.poetry]
2+
name = "foobar"
3+
version = "0.1.0"
4+
description = ""
5+
authors = ["Poetry Developer <[email protected]>"]
6+
7+
[tool.poetry.dependencies]
8+
python = "^3.8"
9+
sampleproject = ">=1.3.1"
10+
11+
[tool.poetry.dev-dependencies]
12+
13+
[build-system]
14+
requires = ["poetry-core>=1.0.0"]
15+
build-backend = "poetry.core.masonry.api"

0 commit comments

Comments
 (0)