From 35a3e8b7f526efa58761184866db7fbdc4ef39c8 Mon Sep 17 00:00:00 2001 From: Ian Wienand Date: Wed, 20 Jan 2016 16:50:18 +1100 Subject: [PATCH] Restore ability to uninstall distutils packages This is a partial revert of 6afc718307fea36b9ffddd376c1395ee1061795c to restore the ability to overwrite distutils installed packages. It is not elegant, but some projects (such as OpenStack's devstack) rely on overwriting packages installed via the system package manager. These packages can't be removed because they are dependencies for parts of the base system, but many of the things devstack needs to run requires later dependencies. For historical reasons it's not easy to fix this into a virtualenv, etc, all at once. If distributions move to setuptools based packages, this problem might fix itself. --- pip/req/req_install.py | 15 ++++++++++----- tests/functional/test_uninstall.py | 22 ++++++++++++++++++++++ 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/pip/req/req_install.py b/pip/req/req_install.py index 6f497cbc0..3d078431c 100644 --- a/pip/req/req_install.py +++ b/pip/req/req_install.py @@ -7,6 +7,7 @@ import shutil import sys import tempfile import traceback +import warnings import zipfile from distutils.util import change_root @@ -34,7 +35,9 @@ from pip.utils import ( call_subprocess, read_text_file, FakeFile, _make_build_dir, ensure_dir, get_installed_version, canonicalize_name ) + from pip.utils.hashes import Hashes +from pip.utils.deprecation import RemovedInPip10Warning from pip.utils.logging import indent_log from pip.utils.setuptools_build import SETUPTOOLS_SHIM from pip.utils.ui import open_spinner @@ -647,12 +650,14 @@ class InstallRequirement(object): paths_to_remove.add(path + '.pyo') elif distutils_egg_info: - raise UninstallationError( - "Detected a distutils installed project ({0!r}) which we " - "cannot uninstall. The metadata provided by distutils does " - "not contain a list of files which have been installed, so " - "pip does not know which files to uninstall.".format(self.name) + warnings.warn( + "Uninstalling a distutils installed project ({0}) has been " + "deprecated and will be removed in a future version. This is " + "due to the fact that uninstalling a distutils project will " + "only partially uninstall the project.".format(self.name), + RemovedInPip10Warning, ) + paths_to_remove.add(distutils_egg_info) elif dist.location.endswith('.egg'): # package installed by easy_install diff --git a/tests/functional/test_uninstall.py b/tests/functional/test_uninstall.py index b8003f274..bda4da06e 100644 --- a/tests/functional/test_uninstall.py +++ b/tests/functional/test_uninstall.py @@ -30,6 +30,28 @@ def test_simple_uninstall(script): assert_all_changes(result, result2, [script.venv / 'build', 'cache']) +def test_simple_uninstall_distutils(script): + """ + Test simple install and uninstall. + + """ + script.scratch_path.join("distutils_install").mkdir() + pkg_path = script.scratch_path / 'distutils_install' + pkg_path.join("setup.py").write(textwrap.dedent(""" + from distutils.core import setup + setup( + name='distutils-install', + version='0.1', + ) + """)) + result = script.run('python', pkg_path / 'setup.py', 'install') + result = script.pip('list') + assert "distutils-install (0.1)" in result.stdout + script.pip('uninstall', 'distutils_install', '-y', expect_stderr=True) + result2 = script.pip('list') + assert "distutils-install (0.1)" not in result2.stdout + + @pytest.mark.network def test_uninstall_with_scripts(script): """