diff --git a/docs/cli.md b/docs/cli.md index 085f1fe86a9..8a58b4f7998 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -708,7 +708,9 @@ poetry search requests pendulum This command locks (without installing) the dependencies specified in `pyproject.toml`. {{% note %}} -By default, this will lock all dependencies to the latest available compatible versions. To only refresh the lock file, use the `--no-update` option. +By default, packages that have already been added to the lock file before will not be updated. +To update all dependencies to the latest available compatible versions, use `poetry update --lock` +or `poetry lock --regenerate`, which normally produce the same result. This command is also available as a pre-commit hook. See [pre-commit hooks]({{< relref "pre-commit-hooks#poetry-lock">}}) for more information. {{% /note %}} @@ -719,7 +721,7 @@ poetry lock ### Options * `--check`: Verify that `poetry.lock` is consistent with `pyproject.toml`. (**Deprecated**) Use `poetry check --lock` instead. -* `--no-update`: Do not update locked versions, only refresh lock file. +* `--regenerate`: Ignore existing lock file and overwrite it with a new lock file created from scratch. ## version diff --git a/src/poetry/console/commands/lock.py b/src/poetry/console/commands/lock.py index c64f1dfd2ad..9c7733d667d 100644 --- a/src/poetry/console/commands/lock.py +++ b/src/poetry/console/commands/lock.py @@ -18,7 +18,10 @@ class LockCommand(InstallerCommand): options: ClassVar[list[Option]] = [ option( - "no-update", None, "Do not update locked versions, only refresh lock file." + "regenerate", + None, + "Ignore existing lock file" + " and overwrite it with a new lock file created from scratch.", ), option( "check", @@ -34,6 +37,8 @@ class LockCommand(InstallerCommand): current directory, processes it, and locks the dependencies in the\ poetry.lock file. +By default, packages that have already been added to the lock file before +will not be updated. poetry lock """ @@ -57,6 +62,6 @@ def handle(self) -> int: ) return 1 - self.installer.lock(update=not self.option("no-update")) + self.installer.lock(update=self.option("regenerate")) return self.installer.run() diff --git a/tests/console/commands/test_lock.py b/tests/console/commands/test_lock.py index 38d0c746412..428009a26c9 100644 --- a/tests/console/commands/test_lock.py +++ b/tests/console/commands/test_lock.py @@ -135,7 +135,7 @@ def test_lock_check_up_to_date_legacy( assert status_code == 0 -def test_lock_no_update( +def test_lock_does_not_update_if_not_necessary( command_tester_factory: CommandTesterFactory, poetry_with_old_lockfile: Poetry, repo: TestRepository, @@ -156,7 +156,7 @@ def test_lock_no_update( ) tester = command_tester_factory("lock", poetry=poetry_with_old_lockfile) - tester.execute("--no-update") + tester.execute() locker = Locker( lock=poetry_with_old_lockfile.pyproject.file.path.parent / "poetry.lock", @@ -172,10 +172,12 @@ def test_lock_no_update( assert locked_repository.find_packages(package.to_dependency()) -def test_lock_no_update_path_dependencies( +@pytest.mark.parametrize("regenerate", [True, False]) +def test_lock_always_updates_path_dependencies( command_tester_factory: CommandTesterFactory, poetry_with_nested_path_deps_old_lockfile: Poetry, repo: TestRepository, + regenerate: bool, ) -> None: """ The lock file contains a variant of the directory dependency "quix" that does @@ -195,14 +197,14 @@ def test_lock_no_update_path_dependencies( tester = command_tester_factory( "lock", poetry=poetry_with_nested_path_deps_old_lockfile ) - tester.execute("--no-update") + tester.execute("--regenerate" if regenerate else "") packages = locker.locked_repository().packages assert {p.name for p in packages} == {"quix", "sampleproject"} -@pytest.mark.parametrize("update", [True, False]) +@pytest.mark.parametrize("regenerate", [True, False]) @pytest.mark.parametrize( "project", ["missing_directory_dependency", "missing_file_dependency"] ) @@ -211,7 +213,7 @@ def test_lock_path_dependency_does_not_exist( project_factory: ProjectFactory, fixture_dir: FixtureDirGetter, project: str, - update: bool, + regenerate: bool, ) -> None: poetry = _project_factory(project, project_factory, fixture_dir) locker = Locker( @@ -219,10 +221,10 @@ def test_lock_path_dependency_does_not_exist( pyproject_data=poetry.locker._pyproject_data, ) poetry.set_locker(locker) - options = "" if update else "--no-update" + options = "--regenerate" if regenerate else "" tester = command_tester_factory("lock", poetry=poetry) - if update or "directory" in project: + if regenerate or "directory" in project: # directory dependencies are always updated with pytest.raises(ValueError, match="does not exist"): tester.execute(options) @@ -230,7 +232,7 @@ def test_lock_path_dependency_does_not_exist( tester.execute(options) -@pytest.mark.parametrize("update", [True, False]) +@pytest.mark.parametrize("regenerate", [True, False]) @pytest.mark.parametrize( "project", ["deleted_directory_dependency", "deleted_file_dependency"] ) @@ -239,7 +241,7 @@ def test_lock_path_dependency_deleted_from_pyproject( project_factory: ProjectFactory, fixture_dir: FixtureDirGetter, project: str, - update: bool, + regenerate: bool, ) -> None: poetry = _project_factory(project, project_factory, fixture_dir) locker = Locker( @@ -249,22 +251,19 @@ def test_lock_path_dependency_deleted_from_pyproject( poetry.set_locker(locker) tester = command_tester_factory("lock", poetry=poetry) - if update: - tester.execute("") - else: - tester.execute("--no-update") + tester.execute("--regenerate" if regenerate else "") packages = locker.locked_repository().packages assert {p.name for p in packages} == set() -@pytest.mark.parametrize("is_no_update", [False, True]) +@pytest.mark.parametrize("regenerate", [True, False]) def test_lock_with_incompatible_lockfile( command_tester_factory: CommandTesterFactory, poetry_with_incompatible_lockfile: Poetry, repo: TestRepository, - is_no_update: bool, + regenerate: bool, ) -> None: repo.add_package(get_package("sampleproject", "1.3.1")) @@ -276,26 +275,26 @@ def test_lock_with_incompatible_lockfile( poetry_with_incompatible_lockfile.set_locker(locker) tester = command_tester_factory("lock", poetry=poetry_with_incompatible_lockfile) - if is_no_update: + if regenerate: + # still possible because lock file is not required + status_code = tester.execute("--regenerate") + assert status_code == 0 + else: # 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 + tester.execute() -@pytest.mark.parametrize("is_no_update", [False, True]) +@pytest.mark.parametrize("regenerate", [True, False]) def test_lock_with_invalid_lockfile( command_tester_factory: CommandTesterFactory, poetry_with_invalid_lockfile: Poetry, repo: TestRepository, - is_no_update: bool, + regenerate: bool, ) -> None: repo.add_package(get_package("sampleproject", "1.3.1")) @@ -306,11 +305,11 @@ def test_lock_with_invalid_lockfile( 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: + if regenerate: # still possible because lock file is not required - status_code = tester.execute() + status_code = tester.execute("--regenerate") assert status_code == 0 + else: + # not possible because of broken lock file + with pytest.raises(RuntimeError, match="Unable to read the lock file"): + tester.execute()