diff --git a/CHANGES.txt b/CHANGES.txt index aebfd1bfe10..3ddbdec5716 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,5 +1,8 @@ **8.2.0 (unreleased)** +* Uninstall existing packages when performing an editable installation of + the same packages (:issue:`1548`). + * Pip show is less verbose by default. `--verbose` prints multiline fields * Added optional column formatting to ``pip list`` (:issue:`3651`). diff --git a/pip/req/req_install.py b/pip/req/req_install.py index e99e8757dae..fe51e9b6e22 100644 --- 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 index a4e6b0e161b..40b0d3e1307 100644 --- 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( diff --git a/tests/functional/test_install.py b/tests/functional/test_install.py index 1440f3335f9..dd4f7ea6639 100644 --- 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')