Repository: pypa/pip

Issue:
pip unable to uninstall package with non-canonical version
Somewhat similar to #2293: pip installs into a path but is not able to uninstall it if the version is not in its 'canonical' form.

```
$ pip install youtube_dl
Collecting youtube-dl
  DEPRECATION: Failed to find 'youtube-dl' at https://xfernandez:Quee4CheTohkei4B@pypi-dev.polyconseil.fr//simple/youtube-dl/. It is suggested to upgrade your index to support normalized names as the name in /simple/{name}.
  Using cached youtube_dl-2015.01.11-py2.py3-none-any.whl
Installing collected packages: youtube-dl

Successfully installed youtube-dl-2015.1.11
$ ls ~/.virtualenvs/test_youtube/lib/python2.7/site-packages/
easy_install.py  easy_install.pyc  _markerlib  pip  pip-6.0.6.dist-info  pkg_resources  setuptools  setuptools-11.3.1.dist-info  youtube_dl  youtube_dl-2015.01.11.dist-info
$  pip uninstall youtube_dl
Uninstalling youtube-dl-2015.1.11:
  /home/xfernandez/.virtualenvs/test_youtube/bin/youtube-dl
Proceed (y/n)? y
  Successfully uninstalled youtube-dl-2015.1.11
$ ls ~/.virtualenvs/test_youtube/lib/python2.7/site-packages/
easy_install.py  easy_install.pyc  _markerlib  pip  pip-6.0.6.dist-info  pkg_resources  setuptools  setuptools-11.3.1.dist-info  youtube_dl  youtube_dl-2015.01.11.dist-info
$ pip list
pip (6.0.6)
setuptools (11.3.1)
youtube-dl (2015.1.11)
```

Issue uninstalling egg with slightly different name
I've an old venv created via virtualenv 1.4.9:

```
$ virtualenv --setuptools --no-site-packages test_setuptools
$ ls test_setuptools/lib/python2.6/site-packages/
easy-install.pth  pip-6.0.3-py2.6.egg  setuptools-0.6c11-py2.6.egg  setuptools.pth
```

Note the setuptools-0.6c11-py2.6.egg.

```
(test_setuptools) $ pip install -U setuptools
DEPRECATION: --download-cache has been deprecated and will be removed in the future. Pip now automatically uses and configures its cache.
Processing ./.cache/pip/wheelhouse/setuptools-10.0.1-py2.py3-none-any.whl
Installing collected packages: setuptools
  Found existing installation: setuptools 0.6rc11
    Uninstalling setuptools-0.6rc11:
      Successfully uninstalled setuptools-0.6rc11

Successfully installed setuptools-0.6rc11

(test_setuptools) $ pip list | grep setup
setuptools (0.6rc11)

(test_setuptools) $ ls test_setuptools/lib/python2.6/site-packages/
easy-install.pth  easy_install.py  easy_install.pyc  _markerlib  pip-6.0.3-py2.6.egg  pkg_resources  setuptools  setuptools-0.6c11-py2.6.egg  setuptools-10.0.1.dist-info  setuptools.pth
```

Setuptools 10.0.1 is indeed installed but 0.6rc11 is not uninstalled because the egg is named setuptools-0.6c11-py2.6.egg and not setuptools-0.6rc11-py2.6.egg



Discussion:

I've this issue on Debian 6.0 (squeeze)


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
@@ -557,33 +557,27 @@ def uninstall(self, auto_confirm=False):
         dist = self.satisfied_by or self.conflicts_with
 
         paths_to_remove = UninstallPathSet(dist)
-
-        pip_egg_info_path = os.path.join(dist.location,
-                                         dist.egg_name()) + '.egg-info'
-        dist_info_path = os.path.join(dist.location,
-                                      '-'.join(dist.egg_name().split('-')[:2])
-                                      ) + '.dist-info'
-        # Workaround - http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=618367
-        debian_egg_info_path = pip_egg_info_path.replace(
-            '-py%s' % pkg_resources.PY_MAJOR, '')
-        easy_install_egg = dist.egg_name() + '.egg'
         develop_egg_link = egg_link_path(dist)
-
-        pip_egg_info_exists = os.path.exists(pip_egg_info_path)
-        debian_egg_info_exists = os.path.exists(debian_egg_info_path)
-        dist_info_exists = os.path.exists(dist_info_path)
-        if pip_egg_info_exists or debian_egg_info_exists:
-            # package installed by pip
-            if pip_egg_info_exists:
-                egg_info_path = pip_egg_info_path
-            else:
-                egg_info_path = debian_egg_info_path
-            paths_to_remove.add(egg_info_path)
+        egg_info_exists = dist.egg_info and os.path.exists(dist.egg_info)
+        if develop_egg_link:
+            # develop egg
+            with open(develop_egg_link, 'r') as fh:
+                link_pointer = os.path.normcase(fh.readline().strip())
+            assert (link_pointer == dist.location), (
+                'Egg-link %s does not match installed location of %s '
+                '(at %s)' % (link_pointer, self.name, dist.location)
+            )
+            paths_to_remove.add(develop_egg_link)
+            easy_install_pth = os.path.join(os.path.dirname(develop_egg_link),
+                                            'easy-install.pth')
+            paths_to_remove.add_pth(easy_install_pth, dist.location)
+        elif egg_info_exists and dist.egg_info.endswith('.egg-info'):
+            paths_to_remove.add(dist.egg_info)
             if dist.has_metadata('installed-files.txt'):
                 for installed_file in dist.get_metadata(
                         'installed-files.txt').splitlines():
                     path = os.path.normpath(
-                        os.path.join(egg_info_path, installed_file)
+                        os.path.join(dist.egg_info, installed_file)
                     )
                     paths_to_remove.add(path)
             # FIXME: need a test for this elif block
@@ -603,28 +597,23 @@ def uninstall(self, auto_confirm=False):
                     paths_to_remove.add(path + '.py')
                     paths_to_remove.add(path + '.pyc')
 
-        elif dist.location.endswith(easy_install_egg):
+        elif dist.location.endswith('.egg'):
             # package installed by easy_install
+            # We cannot match on dist.egg_name because it can slightly vary
+            # i.e. setuptools-0.6c11-py2.6.egg vs setuptools-0.6rc11-py2.6.egg
             paths_to_remove.add(dist.location)
+            easy_install_egg = os.path.split(dist.location)[1]
             easy_install_pth = os.path.join(os.path.dirname(dist.location),
                                             'easy-install.pth')
             paths_to_remove.add_pth(easy_install_pth, './' + easy_install_egg)
 
-        elif develop_egg_link:
-            # develop egg
-            with open(develop_egg_link, 'r') as fh:
-                link_pointer = os.path.normcase(fh.readline().strip())
-            assert (link_pointer == dist.location), (
-                'Egg-link %s does not match installed location of %s '
-                '(at %s)' % (link_pointer, self.name, dist.location)
-            )
-            paths_to_remove.add(develop_egg_link)
-            easy_install_pth = os.path.join(os.path.dirname(develop_egg_link),
-                                            'easy-install.pth')
-            paths_to_remove.add_pth(easy_install_pth, dist.location)
-        elif dist_info_exists:
+        elif egg_info_exists and dist.egg_info.endswith('.dist-info'):
             for path in pip.wheel.uninstallation_paths(dist):
                 paths_to_remove.add(path)
+        else:
+            logger.debug(
+                'Not sure how to uninstall: %s - Check: %s',
+                dist, dist.location)
 
         # find distutils scripts= scripts
         if dist.has_metadata('scripts') and dist.metadata_isdir('scripts'):


Test patch:
diff --git a/tests/functional/test_install_upgrade.py b/tests/functional/test_install_upgrade.py
--- a/tests/functional/test_install_upgrade.py
+++ b/tests/functional/test_install_upgrade.py
@@ -329,7 +329,7 @@ def test_py2_from_setuptools_6_to_setuptools_7(
             "Found existing installation: setuptools 0.6rc11" in result.stdout
         )
         result = self.script.run(self.ve_bin / 'pip', 'list')
-        "setuptools (0.9.8)" in result.stdout
+        assert "setuptools (0.9.8)" in result.stdout
 
     def test_py2_py3_from_distribute_6_to_setuptools_7(
             self, script, data, virtualenv):
@@ -344,8 +344,8 @@ def test_py2_py3_from_distribute_6_to_setuptools_7(
             "Found existing installation: distribute 0.6.34" in result.stdout
         )
         result = self.script.run(self.ve_bin / 'pip', 'list')
-        "setuptools (0.9.8)" in result.stdout
-        "distribute (0.7.3)" in result.stdout
+        assert "setuptools (0.9.8)" in result.stdout
+        assert "distribute (0.7.3)" not in result.stdout
 
     def test_from_setuptools_7_to_setuptools_7(self, script, data, virtualenv):
         self.prep_ve(script, '1.10', virtualenv.pip_source_dir)
@@ -355,7 +355,7 @@ def test_from_setuptools_7_to_setuptools_7(self, script, data, virtualenv):
         )
         assert "Found existing installation: setuptools 0.9.7" in result.stdout
         result = self.script.run(self.ve_bin / 'pip', 'list')
-        "setuptools (0.9.8)" in result.stdout
+        assert "setuptools (0.9.8)" in result.stdout
 
     def test_from_setuptools_7_to_setuptools_7_using_wheel(
             self, script, data, virtualenv):
@@ -368,7 +368,7 @@ def test_from_setuptools_7_to_setuptools_7_using_wheel(
         # only wheels use dist-info
         assert 'setuptools-0.9.8.dist-info' in str(result.files_created)
         result = self.script.run(self.ve_bin / 'pip', 'list')
-        "setuptools (0.9.8)" in result.stdout
+        assert "setuptools (0.9.8)" in result.stdout
 
     # disabling intermittent travis failure:
     #   https://github.com/pypa/pip/issues/1379
@@ -387,13 +387,12 @@ def test_from_setuptools_7_to_setuptools_7_with_distribute_7_installed(
             '--find-links=%s' % data.find_links, 'setuptools==0.9.6'
         )
         result = self.script.run(self.ve_bin / 'pip', 'list')
-        "setuptools (0.9.6)" in result.stdout
-        "distribute (0.7.3)" in result.stdout
+        assert "setuptools (0.9.6)" in result.stdout
+        assert "distribute (0.7.3)" not in result.stdout
         result = self.script.run(
             self.ve_bin / 'pip', 'install', '--no-index',
             '--find-links=%s' % data.find_links, '-U', 'setuptools'
         )
         assert "Found existing installation: setuptools 0.9.6" in result.stdout
         result = self.script.run(self.ve_bin / 'pip', 'list')
-        "setuptools (0.9.8)" in result.stdout
-        "distribute (0.7.3)" in result.stdout
+        assert "setuptools (0.9.8)" in result.stdout