Repository: pypa/pip

Issue:
pip install -e doesn't uninstall existing versions 
This has been an annoyance for me and my colleagues and I think I have a one line fix, but I figured that there is a good chance that this a known issue and that it's been discussed before and there's some reason not to change it. 

A pip install of one version of a package will typically cause the previously installed version to be uninstalled, so there is only one version installed at a time. 

If one installs a regular package from PyPI and then installs an editable package from git or from a local directory (e.g.: `pip install -e .`), then the former package does NOT get uninstalled and you have two versions installed (even though `pip freeze` will only show one). In this weird state, sometimes the older package will get picked up and a single `pip uninstall` is not enough to clean up. You have to do a not obvious `pip uninstall` TWICE to completely remove the package. 
## Example

``` bash
$ virtualenv pip_test
...
$ cd pip_test && source bin/activate
$ pip install requests==0.5.0
...
Successfully installed requests
Cleaning up...
$ python -c 'import requests; print(requests.__version__)'
0.5.0
$ pip install -e ~/dev/git-repos/requests/
Obtaining file:///Users/marca/dev/git-repos/requests
  Running setup.py egg_info for package from file:///Users/marca/dev/git-repos/requests

Installing collected packages: requests
  Running setup.py develop for requests

    Creating /private/tmp/pip_test/lib/python2.7/site-packages/requests.egg-link (link to .)
    Adding requests 2.0.1 to easy-install.pth file

    Installed /Users/marca/dev/git-repos/requests
Successfully installed requests
Cleaning up...
$ python -c 'import requests; print(requests); print(requests.__version__)'
<module 'requests' from '/Users/marca/dev/git-repos/requests/requests/__init__.pyc'>
2.0.1
$ pip uninstall requests
Uninstalling requests:
  /private/tmp/pip_test/lib/python2.7/site-packages/requests.egg-link
Proceed (y/n)? y
  Successfully uninstalled requests
$ python -c 'import requests; print(requests); print(requests.__version__)'
<module 'requests' from '/private/tmp/pip_test/lib/python2.7/site-packages/requests/__init__.pyc'>
0.5.0
$ pip uninstall requests
Uninstalling requests:
  /private/tmp/pip_test/lib/python2.7/site-packages/requests-0.5.0-py2.7.egg-info
  /private/tmp/pip_test/lib/python2.7/site-packages/requests/__init__.py
  /private/tmp/pip_test/lib/python2.7/site-packages/requests/__init__.pyc
...
Proceed (y/n)? y
  Successfully uninstalled requests
```

Any reason NOT to have `pip install -e` check for previously installed versions and uninstall them?



Discussion:

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
@@ -1000,6 +1000,12 @@ def check_if_exists(self):
             no_marker = Requirement(str(self.req))
             no_marker.marker = None
             self.satisfied_by = pkg_resources.get_distribution(str(no_marker))
+            if self.editable and self.satisfied_by:
+                self.conflicts_with = self.satisfied_by
+                # when installing editables, nothing pre-existing should ever
+                # satisfy
+                self.satisfied_by = None
+                return True
         except pkg_resources.DistributionNotFound:
             return False
         except pkg_resources.VersionConflict:
diff --git a/pip/req/req_set.py b/pip/req/req_set.py
--- a/pip/req/req_set.py
+++ b/pip/req/req_set.py
@@ -489,6 +489,7 @@ def _prepare_file(self,
                 abstract_dist.prep_for_dist()
                 if self.is_download:
                     req_to_install.archive(self.download_dir)
+                req_to_install.check_if_exists()
             elif req_to_install.satisfied_by:
                 if require_hashes:
                     logger.debug(


Test patch:
diff --git a/tests/functional/test_install.py b/tests/functional/test_install.py
--- a/tests/functional/test_install.py
+++ b/tests/functional/test_install.py
@@ -179,6 +179,56 @@ def test_install_editable_from_git_autobuild_wheel(script, tmpdir):
     _test_install_editable_from_git(script, tmpdir, True)
 
 
+def test_install_editable_uninstalls_existing(data, script, tmpdir):
+    """
+    Test that installing an editable uninstalls a previously installed
+    non-editable version.
+    https://github.com/pypa/pip/issues/1548
+    https://github.com/pypa/pip/pull/1552
+    """
+    to_install = data.packages.join("pip-test-package-0.1.tar.gz")
+    result = script.pip_install_local(to_install)
+    assert 'Successfully installed pip-test-package' in result.stdout
+    result.assert_installed('piptestpackage', editable=False)
+
+    result = script.pip(
+        'install', '-e',
+        '%s#egg=pip-test-package' %
+        local_checkout(
+            'git+http://github.com/pypa/pip-test-package.git',
+            tmpdir.join("cache"),
+        ),
+    )
+    result.assert_installed('pip-test-package', with_files=['.git'])
+    assert 'Found existing installation: pip-test-package 0.1' in result.stdout
+    assert 'Uninstalling pip-test-package-' in result.stdout
+    assert 'Successfully uninstalled pip-test-package' in result.stdout
+
+
+def test_install_editable_uninstalls_existing_from_path(script, data):
+    """
+    Test that installing an editable uninstalls a previously installed
+    non-editable version from path
+    """
+    to_install = data.src.join('simplewheel-1.0')
+    result = script.pip_install_local(to_install)
+    assert 'Successfully installed simplewheel' in result.stdout
+    simple_folder = script.site_packages / 'simple'
+    result.assert_installed('simple', editable=False)
+    assert simple_folder in result.files_created, str(result.stdout)
+
+    result = script.pip(
+        'install', '-e',
+        to_install,
+    )
+    install_path = script.site_packages / 'simplewheel.egg-link'
+    assert install_path in result.files_created, str(result)
+    assert 'Found existing installation: simplewheel 1.0' in result.stdout
+    assert 'Uninstalling simplewheel-' in result.stdout
+    assert 'Successfully uninstalled simplewheel' in result.stdout
+    assert simple_folder in result.files_deleted, str(result.stdout)
+
+
 def test_install_editable_from_hg(script, tmpdir):
     """Test cloning from Mercurial."""
     pkg_path = _create_test_package(script, name='testpackage', vcs='hg')