Repository: pypa/pip

Issue:
pip will uninstall .egg-info files from pure distutils installs
as stated in it's docs, pip doesn't support uninstalling pure distutils distributions installed with `python setup.py install`

but if you do attempt to uninstall one using pip, it will prompt you to remove the distutils egg-info file like so.

```
$ py32/bin/pip uninstall lupyne
Uninstalling lupyne:
  /tmp/py32/lib/python3.2/site-packages/lupyne-1.2-py3.2.egg-info
Proceed (y/n)? 
```

pip shouldn't do this, and could probably offer a message that removing such a package is not supported.

fix uninstall for distutils installed packages
it should fix #2384 but via a unfortunate special case...

pip==6.0.7 unable uninstall or upgrade packages with egg-info file
Since https://github.com/pypa/pip/commit/6e415159e00c80944dfca5aa2271c556f55149d8 the pip fails to uninstall or upgrade packages previously installed from rpm  (for example on Fedora 20).

> $ pip uninstall requests
> Can't uninstall 'requests'. No files were found to uninstall.

The previous code was able to find the /usr/lib/python2.7/site-packages/requests-1.2.3-py2.7.egg-info file (it is not a directory) installed from python-requests-1.2.3-5.fc20.noarch package.

dist.egg_info is None, so the now code thinks there is no egg-info.

> egg_info_exists = dist.egg_info and os.path.exists(dist.egg_info)

The older code was able to use the correct path.

> pip_egg_info_path = os.path.join(dist.location, dist.egg_name()) + '.egg-info')



Discussion:
pip is conflating egg-info files with egg-info dirs in the uninstall code, and some of the vars are named in a way that promotes confusion.

e.g.

`pip_egg_info_path = os.path.join(dist.location, dist.egg_name()) + '.egg-info'`

that can be a file or a dir.  if it's a file, it's not pip's *.egg-info path.

What's the status of this?

#667 stalled with some travis/debian issues.
should be easy to overcome though.
feel free to reuse the work in that and submit a new pull.


Hello, could you tell me what dist._provider and dist._provider.path contains ?

Sure:

`dist._provider:<pip._vendor.pkg_resources.FileMetadata instance at 0x187e1b8>`

dist._provider's attributes (exuding methods):

> egg_info:None
> egg_name:None
> loader:None
> module_path:None
> path:/usr/lib/python2.7/site-packages/requests-1.2.3-py2.7.egg-info

That's what I was expecting but I'm not sure how to deal with it.
Either we deal with this special case in pip or maybe pkg_resources.FileMetadata should fill in the egg_info attribute ?
@dstufft @jaraco any opinion ?


Resolving patch:
diff --git a/pip/req/req_install.py b/pip/req/req_install.py
--- a/pip/req/req_install.py
+++ b/pip/req/req_install.py
@@ -6,6 +6,7 @@
 import shutil
 import sys
 import tempfile
+import warnings
 import zipfile
 
 from distutils.util import change_root
@@ -32,6 +33,7 @@
     dist_in_usersite, dist_in_site_packages, egg_link_path, make_path_relative,
     call_subprocess, read_text_file, FakeFile, _make_build_dir,
 )
+from pip.utils.deprecation import RemovedInPip8Warning
 from pip.utils.logging import indent_log
 from pip.req.req_uninstall import UninstallPathSet
 from pip.vcs import vcs
@@ -559,6 +561,8 @@ def uninstall(self, auto_confirm=False):
         paths_to_remove = UninstallPathSet(dist)
         develop_egg_link = egg_link_path(dist)
         egg_info_exists = dist.egg_info and os.path.exists(dist.egg_info)
+        # Special case for distutils installed package
+        distutils_egg_info = getattr(dist._provider, 'path', None)
         if develop_egg_link:
             # develop egg
             with open(develop_egg_link, 'r') as fh:
@@ -597,6 +601,16 @@ def uninstall(self, auto_confirm=False):
                     paths_to_remove.add(path + '.py')
                     paths_to_remove.add(path + '.pyc')
 
+        elif distutils_egg_info:
+            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),
+                RemovedInPip8Warning,
+            )
+            paths_to_remove.add(distutils_egg_info)
+
         elif dist.location.endswith('.egg'):
             # package installed by easy_install
             # We cannot match on dist.egg_name because it can slightly vary


Test patch:
diff --git a/tests/functional/test_uninstall.py b/tests/functional/test_uninstall.py
--- 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')
+    result2 = script.pip('list')
+    assert "distutils-install (0.1)" not in result2.stdout
+
+
 @pytest.mark.network
 def test_uninstall_with_scripts(script):
     """