From 41dbd9711ecc5b80613381f5ea2cdafc9a037002 Mon Sep 17 00:00:00 2001 From: Marc Abramowitz Date: Mon, 10 Feb 2014 07:30:21 -0800 Subject: [PATCH 1/8] [issue 1548] Make pip install -e uninstall existing versions by adding an additional call to `requirement.check_if_exists()` to RequirementSet.install Fixes GH-1548 --- pip/req/req_install.py | 4 ++++ pip/req/req_set.py | 2 ++ 2 files changed, 6 insertions(+) diff --git a/pip/req/req_install.py b/pip/req/req_install.py index e2f018ab5ed..9fca396b267 100644 --- a/pip/req/req_install.py +++ b/pip/req/req_install.py @@ -876,6 +876,10 @@ def check_if_exists(self): return True else: self.satisfied_by = pkg_resources.get_distribution(self.req) + + if self.editable and self.satisfied_by: + self.conflicts_with = self.satisfied_by + 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 59e611ffb99..78797aaacd8 100644 --- a/pip/req/req_set.py +++ b/pip/req/req_set.py @@ -625,6 +625,8 @@ def install(self, install_options, global_options=(), *args, **kwargs): # distribute wasn't installed, so nothing to do pass + requirement.check_if_exists() + if requirement.conflicts_with: logger.notify('Found existing installation: %s' % requirement.conflicts_with) From 387058675ec2f68cafa386a78ca7f0e82a624a42 Mon Sep 17 00:00:00 2001 From: Marc Abramowitz Date: Tue, 11 Feb 2014 07:11:24 -0800 Subject: [PATCH 2/8] Check self.ignore_installed before doing requirement.check_if_exists --- pip/req/req_set.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pip/req/req_set.py b/pip/req/req_set.py index 78797aaacd8..b2407b33d28 100644 --- a/pip/req/req_set.py +++ b/pip/req/req_set.py @@ -625,7 +625,8 @@ def install(self, install_options, global_options=(), *args, **kwargs): # distribute wasn't installed, so nothing to do pass - requirement.check_if_exists() + if not self.ignore_installed: + requirement.check_if_exists() if requirement.conflicts_with: logger.notify('Found existing installation: %s' From 5b62075341723f5a76f2a7300ad008c8a1d84dbd Mon Sep 17 00:00:00 2001 From: Marc Abramowitz Date: Sat, 22 Feb 2014 19:48:00 -0800 Subject: [PATCH 3/8] Set self.satisfied_by back to None; Add a comment that says something like "when installing editables, nothing pre-existing should ever satisfy". Addresses @qwcode's comment at https://github.com/pypa/pip/pull/1552#discussion_r9781803 --- pip/req/req_install.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pip/req/req_install.py b/pip/req/req_install.py index 9fca396b267..65081ef0e62 100644 --- a/pip/req/req_install.py +++ b/pip/req/req_install.py @@ -879,6 +879,9 @@ def check_if_exists(self): 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 From 36cffa0851e0e502f6652b0303013504ca5911e4 Mon Sep 17 00:00:00 2001 From: Marc Abramowitz Date: Sat, 22 Feb 2014 19:54:14 -0800 Subject: [PATCH 4/8] Remove `requirement.check_if_exists()` I added in 3870586 Addresses @qwcode's comment at https://github.com/pypa/pip/pull/1552#discussion_r9781831 --- pip/req/req_set.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pip/req/req_set.py b/pip/req/req_set.py index b2407b33d28..a2a17b5ddd5 100644 --- a/pip/req/req_set.py +++ b/pip/req/req_set.py @@ -217,7 +217,7 @@ def prepare_files(self, finder, force_root_egg_info=False, bundle=False): ## Search for archive to fulfill requirement ## ############################################### - if not self.ignore_installed and not req_to_install.editable: + if not self.ignore_installed: req_to_install.check_if_exists() if req_to_install.satisfied_by: if self.upgrade: @@ -625,9 +625,6 @@ def install(self, install_options, global_options=(), *args, **kwargs): # distribute wasn't installed, so nothing to do pass - if not self.ignore_installed: - requirement.check_if_exists() - if requirement.conflicts_with: logger.notify('Found existing installation: %s' % requirement.conflicts_with) From bfca0f64cd3a09c5b48cf07f0aeefbb6a4f1d248 Mon Sep 17 00:00:00 2001 From: Marc Abramowitz Date: Sat, 22 Feb 2014 21:24:38 -0800 Subject: [PATCH 5/8] Trigger Travis retest --- .trigger-travis | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 .trigger-travis diff --git a/.trigger-travis b/.trigger-travis new file mode 100644 index 00000000000..e69de29bb2d From 3e0d7270a913bbaf08bd02e7c17e1747acb9ab56 Mon Sep 17 00:00:00 2001 From: Marc Abramowitz Date: Sun, 2 Mar 2014 04:31:05 -0800 Subject: [PATCH 6/8] Add test_install_editable_uninstalls_existing --- tests/functional/test_install.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/functional/test_install.py b/tests/functional/test_install.py index a0836b62e21..3e034c79e52 100644 --- a/tests/functional/test_install.py +++ b/tests/functional/test_install.py @@ -228,6 +228,33 @@ def test_install_editable_from_git(script, tmpdir): result.assert_installed('pip-test-package', with_files=['.git']) +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"), + ), + # @todo: Why do other tests specify `expect_error=True`? + ) + 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_from_hg(script, tmpdir): """ Test cloning from Mercurial. From 37bed2011d790d89c25f7bc86645246533b9ac8a Mon Sep 17 00:00:00 2001 From: Marc Abramowitz Date: Sun, 2 Mar 2014 10:24:03 -0800 Subject: [PATCH 7/8] test_install_editable_uninstalls_existing: Remove comment about expect_error=True --- tests/functional/test_install.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/functional/test_install.py b/tests/functional/test_install.py index 3e034c79e52..3807293e0d0 100644 --- a/tests/functional/test_install.py +++ b/tests/functional/test_install.py @@ -247,7 +247,6 @@ def test_install_editable_uninstalls_existing(data, script, tmpdir): 'git+http://github.com/pypa/pip-test-package.git', tmpdir.join("cache"), ), - # @todo: Why do other tests specify `expect_error=True`? ) result.assert_installed('pip-test-package', with_files=['.git']) assert 'Found existing installation: pip-test-package 0.1' in result.stdout From 65a82a2eb03a343a896ec390f449926647a33110 Mon Sep 17 00:00:00 2001 From: Xavier Fernandez Date: Fri, 5 Aug 2016 22:42:22 +0200 Subject: [PATCH 8/8] Always check_if_exists for editable But only when pip has collected the editable package's metadata. closes #1548 --- CHANGES.txt | 3 +++ pip/req/req_set.py | 1 + tests/functional/test_install.py | 26 +++++++++++++++++++++++++- 3 files changed, 29 insertions(+), 1 deletion(-) 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_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 0d74ae500bd..dd4f7ea6639 100644 --- a/tests/functional/test_install.py +++ b/tests/functional/test_install.py @@ -201,10 +201,34 @@ def test_install_editable_uninstalls_existing(data, script, tmpdir): ) 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 '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')