diff --git a/docs/cli.md b/docs/cli.md index 17cd02040a2..b5913b69040 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -471,6 +471,7 @@ about dependency groups. * `--group (-G)`: The group to remove the dependency from. * `--dev (-D)`: Removes a package from the development dependencies. (**Deprecated**, use `-G dev` instead) * `--dry-run` : Outputs the operations but will not execute anything (implicitly enables --verbose). +* `--lock`: Do not perform operations (only update the lockfile). ## show diff --git a/src/poetry/console/commands/remove.py b/src/poetry/console/commands/remove.py index f8ad7f51d75..10b605b4d9f 100644 --- a/src/poetry/console/commands/remove.py +++ b/src/poetry/console/commands/remove.py @@ -35,6 +35,7 @@ class RemoveCommand(InstallerCommand): "(implicitly enables --verbose)." ), ), + option("lock", None, "Do not perform operations (only update the lockfile)."), ] help = """The remove command removes a package from the current @@ -108,15 +109,13 @@ def handle(self) -> int: ) # Refresh the locker - self.poetry.set_locker( - self.poetry.locker.__class__(self.poetry.locker.lock, poetry_content) - ) + self.poetry.locker.set_local_config(poetry_content) self.installer.set_locker(self.poetry.locker) - self.installer.set_package(self.poetry.package) self.installer.dry_run(self.option("dry-run", False)) self.installer.verbose(self.io.is_verbose()) self.installer.update(True) + self.installer.execute_operations(not self.option("lock")) self.installer.whitelist(removed_set) status = self.installer.run() diff --git a/src/poetry/packages/locker.py b/src/poetry/packages/locker.py index a13ba0b3c9b..a0cbfb24f51 100644 --- a/src/poetry/packages/locker.py +++ b/src/poetry/packages/locker.py @@ -90,6 +90,10 @@ def is_fresh(self) -> bool: return False + def set_local_config(self, local_config: dict[str, Any]) -> None: + self._local_config = local_config + self._content_hash = self._get_content_hash() + def locked_repository(self) -> LockfileRepository: """ Searches and returns a repository of locked packages. diff --git a/tests/console/commands/test_remove.py b/tests/console/commands/test_remove.py index 4826f0ae57a..8cb0650e776 100644 --- a/tests/console/commands/test_remove.py +++ b/tests/console/commands/test_remove.py @@ -10,6 +10,7 @@ from poetry.core.packages.package import Package from poetry.factory import Factory +from tests.helpers import TestLocker from tests.helpers import get_package @@ -33,12 +34,16 @@ def poetry_with_up_to_date_lockfile( ) -> Poetry: source = fixture_dir("up_to_date_lock") - return project_factory( + poetry = project_factory( name="foobar", pyproject_content=(source / "pyproject.toml").read_text(encoding="utf-8"), poetry_lock_content=(source / "poetry.lock").read_text(encoding="utf-8"), ) + assert isinstance(poetry.locker, TestLocker) + poetry.locker.locked(True) + return poetry + @pytest.fixture() def tester(command_tester_factory: CommandTesterFactory) -> CommandTester: @@ -49,7 +54,6 @@ def test_remove_without_specific_group_removes_from_all_groups( tester: CommandTester, app: PoetryTestApplication, repo: TestRepository, - command_tester_factory: CommandTesterFactory, installed: Repository, ) -> None: """ @@ -108,7 +112,6 @@ def test_remove_without_specific_group_removes_from_specific_groups( tester: CommandTester, app: PoetryTestApplication, repo: TestRepository, - command_tester_factory: CommandTesterFactory, installed: Repository, ) -> None: """ @@ -166,7 +169,6 @@ def test_remove_does_not_live_empty_groups( tester: CommandTester, app: PoetryTestApplication, repo: TestRepository, - command_tester_factory: CommandTesterFactory, installed: Repository, ) -> None: """ @@ -213,7 +215,6 @@ def test_remove_canonicalized_named_removes_dependency_correctly( tester: CommandTester, app: PoetryTestApplication, repo: TestRepository, - command_tester_factory: CommandTesterFactory, installed: Repository, ) -> None: """ @@ -308,3 +309,47 @@ def test_remove_with_dry_run_keep_files_intact( assert ( poetry_with_up_to_date_lockfile._locker.lock_data == original_lockfile_content ) + + +def test_remove_performs_uninstall_op( + poetry_with_up_to_date_lockfile: Poetry, + command_tester_factory: CommandTesterFactory, + installed: Repository, +) -> None: + installed.add_package(get_package("docker", "4.3.1")) + tester = command_tester_factory("remove", poetry=poetry_with_up_to_date_lockfile) + + tester.execute("docker") + + expected = """\ +Updating dependencies +Resolving dependencies... + +Package operations: 0 installs, 0 updates, 1 removal + + • Removing docker (4.3.1) + +Writing lock file +""" + + assert tester.io.fetch_output() == expected + + +def test_remove_with_lock_does_not_perform_uninstall_op( + poetry_with_up_to_date_lockfile: Poetry, + command_tester_factory: CommandTesterFactory, + installed: Repository, +) -> None: + installed.add_package(get_package("docker", "4.3.1")) + tester = command_tester_factory("remove", poetry=poetry_with_up_to_date_lockfile) + + tester.execute("docker --lock") + + expected = """\ +Updating dependencies +Resolving dependencies... + +Writing lock file +""" + + assert tester.io.fetch_output() == expected diff --git a/tests/helpers.py b/tests/helpers.py index 236745101c8..eefe9c74c1a 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -187,12 +187,8 @@ class TestLocker(Locker): __test__ = False def __init__(self, lock: Path, local_config: dict[str, Any]) -> None: - self._lock = lock - self._local_config = local_config - self._lock_data = None - self._content_hash = self._get_content_hash() + super().__init__(lock, local_config) self._locked = False - self._lock_data = None self._write = False def write(self, write: bool = True) -> None: