diff --git a/src/pip/_internal/req/req_install.py b/src/pip/_internal/req/req_install.py index 62908f71e..43b85665b 100644 --- a/src/pip/_internal/req/req_install.py +++ b/src/pip/_internal/req/req_install.py @@ -648,9 +648,8 @@ class InstallRequirement(object): """ if not self.check_if_exists(): - raise UninstallationError( - "Cannot uninstall requirement %s, not installed" % (self.name,) - ) + logger.warning("Skipping %s as it is not installed.", self.name) + return dist = self.satisfied_by or self.conflicts_with uninstalled_pathset = UninstallPathSet.from_dist(dist) diff --git a/tests/functional/test_uninstall.py b/tests/functional/test_uninstall.py index 633d3d350..609d28393 100644 --- a/tests/functional/test_uninstall.py +++ b/tests/functional/test_uninstall.py @@ -468,3 +468,25 @@ def test_uninstall_editable_and_pip_install(script, data): ) in uninstall2.files_deleted, list(uninstall2.files_deleted.keys()) list_result2 = script.pip('list', '--format=json') assert "FSPkg" not in {p["name"] for p in json.loads(list_result2.stdout)} + + +def test_uninstall_ignores_missing_packages(script, data): + """Uninstall of a non existent package prints a warning and exits cleanly + """ + result = script.pip( + 'uninstall', '-y', 'non-existent-pkg', expect_stderr=True, + ) + + assert "Skipping non-existent-pkg as it is not installed." in result.stderr + assert result.returncode == 0, "Expected clean exit" + + +def test_uninstall_ignores_missing_packages_and_uninstalls_rest(script, data): + script.pip_install_local('simple') + result = script.pip( + 'uninstall', '-y', 'non-existent-pkg', 'simple', expect_stderr=True, + ) + + assert "Skipping non-existent-pkg as it is not installed." in result.stderr + assert "Successfully uninstalled simple" in result.stdout + assert result.returncode == 0, "Expected clean exit"