From ad8d0b6abf0da047144f00de4693c4cc1d0cc6e7 Mon Sep 17 00:00:00 2001 From: "Pradyun S. Gedam" Date: Tue, 21 Jun 2016 16:17:26 +0530 Subject: [PATCH 01/17] Change install command's default behaviour This commit changes pip install's default behavior to upgrade directly mentioned packages and not reinstall dependecies if they are already installed and meet the minimum requirements. --- pip/commands/install.py | 8 ++------ pip/req/req_set.py | 28 +++++++++++++++++++--------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/pip/commands/install.py b/pip/commands/install.py index 28d30c59f7b..a39741b96e4 100644 --- a/pip/commands/install.py +++ b/pip/commands/install.py @@ -87,17 +87,14 @@ def __init__(self, *args, **kw): '-U', '--upgrade', dest='upgrade', action='store_true', - help='Upgrade all specified packages to the newest available ' - 'version. This process is recursive regardless of whether ' - 'a dependency is already satisfied.' + help='No-op option. Kept for backwards compatibility.' ) cmd_opts.add_option( '--force-reinstall', dest='force_reinstall', action='store_true', - help='When upgrading, reinstall all packages even if they are ' - 'already up-to-date.') + help='Reinstall all packages even if they are already up-to-date.') cmd_opts.add_option( '-I', '--ignore-installed', @@ -268,7 +265,6 @@ def run(self, options, args): build_dir=build_dir, src_dir=options.src_dir, download_dir=options.download_dir, - upgrade=options.upgrade, as_egg=options.as_egg, ignore_installed=options.ignore_installed, ignore_dependencies=options.ignore_dependencies, diff --git a/pip/req/req_set.py b/pip/req/req_set.py index a4e6b0e161b..e19c2e93157 100644 --- a/pip/req/req_set.py +++ b/pip/req/req_set.py @@ -139,7 +139,7 @@ def prep_for_dist(self): class RequirementSet(object): - def __init__(self, build_dir, src_dir, download_dir, upgrade=False, + def __init__(self, build_dir, src_dir, download_dir, ignore_installed=False, as_egg=False, target_dir=None, ignore_dependencies=False, force_reinstall=False, use_user_site=False, session=None, pycompile=True, @@ -169,7 +169,6 @@ def __init__(self, build_dir, src_dir, download_dir, upgrade=False, # be combined if we're willing to have non-wheel archives present in # the wheelhouse output by 'pip wheel'. self.download_dir = download_dir - self.upgrade = upgrade self.ignore_installed = ignore_installed self.force_reinstall = force_reinstall self.requirements = Requirements() @@ -241,6 +240,8 @@ def add_requirement(self, install_req, parent_req_name=None): install_req.use_user_site = self.use_user_site install_req.target_dir = self.target_dir install_req.pycompile = self.pycompile + install_req.is_direct = (parent_req_name is None) + if not name: # url or path requirement w/o an egg fragment self.unnamed_requirements.append(install_req) @@ -396,15 +397,24 @@ def _check_skip_installed(self, req_to_install, finder): # Check whether to upgrade/reinstall this req or not. req_to_install.check_if_exists() if req_to_install.satisfied_by: - skip_reason = 'satisfied (use --upgrade to upgrade)' - if self.upgrade: + upgrade_allowed = False + + # Determine why upgrading this may not allowed. + if req_to_install.is_direct: + upgrade_allowed = True + skip_reason = 'satisfied' + else: + skip_reason = 'skipping as not directly required' + + if upgrade_allowed: best_installed = False # For link based requirements we have to pull the # tree down and inspect to assess the version #, so # its handled way down. if not (self.force_reinstall or req_to_install.link): try: - finder.find_requirement(req_to_install, self.upgrade) + finder.find_requirement( + req_to_install, upgrade_allowed) except BestVersionAlreadyInstalled: skip_reason = 'up-to-date' best_installed = True @@ -443,6 +453,7 @@ def _prepare_file(self, return [] req_to_install.prepared = True + upgrade_allowed = req_to_install.is_direct # ###################### # # # print log messages # # @@ -519,7 +530,7 @@ def _prepare_file(self, % (req_to_install, req_to_install.source_dir) ) req_to_install.populate_link( - finder, self.upgrade, require_hashes) + finder, upgrade_allowed, require_hashes) # We can't hit this spot and have populate_link return None. # req_to_install.satisfied_by is None here (because we're # guarded) and upgrade has no impact except when satisfied_by @@ -609,7 +620,7 @@ def _prepare_file(self, if not self.ignore_installed: req_to_install.check_if_exists() if req_to_install.satisfied_by: - if self.upgrade or self.ignore_installed: + if upgrade_allowed or self.ignore_installed: # don't uninstall conflict if user install and # conflict is not user install if not (self.use_user_site and not @@ -620,8 +631,7 @@ def _prepare_file(self, req_to_install.satisfied_by = None else: logger.info( - 'Requirement already satisfied (use ' - '--upgrade to upgrade): %s', + 'Requirement already satisfied: %s', req_to_install, ) From 867f03ca32fe7adf2e15e6ee94535d7dad007c2a Mon Sep 17 00:00:00 2001 From: "Pradyun S. Gedam" Date: Tue, 21 Jun 2016 17:08:00 +0530 Subject: [PATCH 02/17] Fix skipping messages --- pip/req/req_set.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pip/req/req_set.py b/pip/req/req_set.py index e19c2e93157..579f15ecb74 100644 --- a/pip/req/req_set.py +++ b/pip/req/req_set.py @@ -402,9 +402,9 @@ def _check_skip_installed(self, req_to_install, finder): # Determine why upgrading this may not allowed. if req_to_install.is_direct: upgrade_allowed = True - skip_reason = 'satisfied' + skip_reason = 'already satisfied' else: - skip_reason = 'skipping as not directly required' + skip_reason = 'skipped as not directly required' if upgrade_allowed: best_installed = False @@ -416,7 +416,7 @@ def _check_skip_installed(self, req_to_install, finder): finder.find_requirement( req_to_install, upgrade_allowed) except BestVersionAlreadyInstalled: - skip_reason = 'up-to-date' + skip_reason = 'already up-to-date' best_installed = True except DistributionNotFound: # No distribution found, so we squash the @@ -474,7 +474,7 @@ def _prepare_file(self, 'req_to_install.satisfied_by is set to %r' % (req_to_install.satisfied_by,)) logger.info( - 'Requirement already %s: %s', skip_reason, + 'Requirement %s: %s', skip_reason, req_to_install) else: if (req_to_install.link and From 6184b356777a88ee17498051a652428cb0f37f13 Mon Sep 17 00:00:00 2001 From: "Pradyun S. Gedam" Date: Tue, 21 Jun 2016 17:22:19 +0530 Subject: [PATCH 03/17] Change behaviour of install --target, add install --no-replace pip install changed behaviour to upgrade by default. install --target will now also behave similarly, replacing directories unless told to do otherwise via a flag. The behaviour change makes --upgrade option a no-op. This commit also adds a new --no-replace flag that does not allow replacement of existing files/folders with --target and does not reinstall packages when used without --target. [skip ci] because the tests aren't updated for this change. --- pip/commands/install.py | 20 ++++++++++++++------ pip/req/req_set.py | 8 +++++--- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/pip/commands/install.py b/pip/commands/install.py index a39741b96e4..57da69392e9 100644 --- a/pip/commands/install.py +++ b/pip/commands/install.py @@ -67,9 +67,16 @@ def __init__(self, *args, **kw): metavar='dir', default=None, help='Install packages into . ' - 'By default this will not replace existing files/folders in ' - '. Use --upgrade to replace existing packages in ' - 'with new versions.' + 'This will replace existing files/folders in . ' + 'Use --no-replace to disable this behaviour.' + ) + + cmd_opts.add_option( + '--no-replace', + dest='no_replace', + action="store_true", + help='Disallow replacement of already-installed packages and ' + 'existing folders.' ) cmd_opts.add_option( @@ -265,6 +272,7 @@ def run(self, options, args): build_dir=build_dir, src_dir=options.src_dir, download_dir=options.download_dir, + no_replace=options.no_replace, as_egg=options.as_egg, ignore_installed=options.ignore_installed, ignore_dependencies=options.ignore_dependencies, @@ -364,10 +372,10 @@ def run(self, options, args): for item in os.listdir(lib_dir): target_item_dir = os.path.join(options.target_dir, item) if os.path.exists(target_item_dir): - if not options.upgrade: + if options.no_replace: logger.warning( - 'Target directory %s already exists. Specify ' - '--upgrade to force replacement.', + 'Target directory %s already exists. Skipping ' + 'due to --no-replace', target_item_dir ) continue diff --git a/pip/req/req_set.py b/pip/req/req_set.py index 579f15ecb74..64264c8457d 100644 --- a/pip/req/req_set.py +++ b/pip/req/req_set.py @@ -139,7 +139,7 @@ def prep_for_dist(self): class RequirementSet(object): - def __init__(self, build_dir, src_dir, download_dir, + def __init__(self, build_dir, src_dir, download_dir, no_replace=False, ignore_installed=False, as_egg=False, target_dir=None, ignore_dependencies=False, force_reinstall=False, use_user_site=False, session=None, pycompile=True, @@ -169,6 +169,7 @@ def __init__(self, build_dir, src_dir, download_dir, # be combined if we're willing to have non-wheel archives present in # the wheelhouse output by 'pip wheel'. self.download_dir = download_dir + self.no_replace = no_replace self.ignore_installed = ignore_installed self.force_reinstall = force_reinstall self.requirements = Requirements() @@ -399,8 +400,9 @@ def _check_skip_installed(self, req_to_install, finder): if req_to_install.satisfied_by: upgrade_allowed = False - # Determine why upgrading this may not allowed. - if req_to_install.is_direct: + if self.no_replace: + skip_reason = 'skipped due to --no-replace' + elif req_to_install.is_direct: upgrade_allowed = True skip_reason = 'already satisfied' else: From 6f4bd711c48a00b1e9fa4d833c208e07780adcdd Mon Sep 17 00:00:00 2001 From: "Pradyun S. Gedam" Date: Tue, 21 Jun 2016 23:32:21 +0530 Subject: [PATCH 04/17] Revert "Change behaviour of install --target, add install --no-replace" This reverts commit 998b6fde3740873952c8873e31f2285dba19008f. --- pip/commands/install.py | 20 ++++++-------------- pip/req/req_set.py | 8 +++----- 2 files changed, 9 insertions(+), 19 deletions(-) diff --git a/pip/commands/install.py b/pip/commands/install.py index 57da69392e9..a39741b96e4 100644 --- a/pip/commands/install.py +++ b/pip/commands/install.py @@ -67,16 +67,9 @@ def __init__(self, *args, **kw): metavar='dir', default=None, help='Install packages into . ' - 'This will replace existing files/folders in . ' - 'Use --no-replace to disable this behaviour.' - ) - - cmd_opts.add_option( - '--no-replace', - dest='no_replace', - action="store_true", - help='Disallow replacement of already-installed packages and ' - 'existing folders.' + 'By default this will not replace existing files/folders in ' + '. Use --upgrade to replace existing packages in ' + 'with new versions.' ) cmd_opts.add_option( @@ -272,7 +265,6 @@ def run(self, options, args): build_dir=build_dir, src_dir=options.src_dir, download_dir=options.download_dir, - no_replace=options.no_replace, as_egg=options.as_egg, ignore_installed=options.ignore_installed, ignore_dependencies=options.ignore_dependencies, @@ -372,10 +364,10 @@ def run(self, options, args): for item in os.listdir(lib_dir): target_item_dir = os.path.join(options.target_dir, item) if os.path.exists(target_item_dir): - if options.no_replace: + if not options.upgrade: logger.warning( - 'Target directory %s already exists. Skipping ' - 'due to --no-replace', + 'Target directory %s already exists. Specify ' + '--upgrade to force replacement.', target_item_dir ) continue diff --git a/pip/req/req_set.py b/pip/req/req_set.py index 64264c8457d..579f15ecb74 100644 --- a/pip/req/req_set.py +++ b/pip/req/req_set.py @@ -139,7 +139,7 @@ def prep_for_dist(self): class RequirementSet(object): - def __init__(self, build_dir, src_dir, download_dir, no_replace=False, + def __init__(self, build_dir, src_dir, download_dir, ignore_installed=False, as_egg=False, target_dir=None, ignore_dependencies=False, force_reinstall=False, use_user_site=False, session=None, pycompile=True, @@ -169,7 +169,6 @@ def __init__(self, build_dir, src_dir, download_dir, no_replace=False, # be combined if we're willing to have non-wheel archives present in # the wheelhouse output by 'pip wheel'. self.download_dir = download_dir - self.no_replace = no_replace self.ignore_installed = ignore_installed self.force_reinstall = force_reinstall self.requirements = Requirements() @@ -400,9 +399,8 @@ def _check_skip_installed(self, req_to_install, finder): if req_to_install.satisfied_by: upgrade_allowed = False - if self.no_replace: - skip_reason = 'skipped due to --no-replace' - elif req_to_install.is_direct: + # Determine why upgrading this may not allowed. + if req_to_install.is_direct: upgrade_allowed = True skip_reason = 'already satisfied' else: From 7020c460e16551be0cf4e787f2e1a7f4b0812740 Mon Sep 17 00:00:00 2001 From: "Pradyun S. Gedam" Date: Tue, 21 Jun 2016 23:40:51 +0530 Subject: [PATCH 05/17] Change the behaviour of install --target Remove the ability to replace existing files and folders with install --target to truly remove the need for --upgrade. [skip ci] because tests aren't ready yet. --- pip/commands/install.py | 29 ++++++----------------------- 1 file changed, 6 insertions(+), 23 deletions(-) diff --git a/pip/commands/install.py b/pip/commands/install.py index a39741b96e4..82dc087c511 100644 --- a/pip/commands/install.py +++ b/pip/commands/install.py @@ -67,9 +67,7 @@ def __init__(self, *args, **kw): metavar='dir', default=None, help='Install packages into . ' - 'By default this will not replace existing files/folders in ' - '. Use --upgrade to replace existing packages in ' - 'with new versions.' + 'This will not replace existing files/folders in .' ) cmd_opts.add_option( @@ -364,26 +362,11 @@ def run(self, options, args): for item in os.listdir(lib_dir): target_item_dir = os.path.join(options.target_dir, item) if os.path.exists(target_item_dir): - if not options.upgrade: - logger.warning( - 'Target directory %s already exists. Specify ' - '--upgrade to force replacement.', - target_item_dir - ) - continue - if os.path.islink(target_item_dir): - logger.warning( - 'Target directory %s already exists and is ' - 'a link. Pip will not automatically replace ' - 'links, please remove if replacement is ' - 'desired.', - target_item_dir - ) - continue - if os.path.isdir(target_item_dir): - shutil.rmtree(target_item_dir) - else: - os.remove(target_item_dir) + logger.warning( + 'Target directory %s already exists.', + target_item_dir + ) + continue shutil.move( os.path.join(lib_dir, item), From 298a375b147ba9dd8d05d04dcf6aca9cc633ab5a Mon Sep 17 00:00:00 2001 From: "Pradyun S. Gedam" Date: Wed, 22 Jun 2016 17:26:24 +0530 Subject: [PATCH 06/17] Change tests according to new install-as-upgrade behaviour Correct existing tests according to the new behaviour Add new tests for previously non-existent behaviour --- tests/data/packages/README.txt | 5 ++ tests/data/packages/require_simple-1.0.tar.gz | Bin 0 -> 735 bytes tests/functional/test_install_upgrade.py | 65 +++++++++++++----- 3 files changed, 54 insertions(+), 16 deletions(-) create mode 100644 tests/data/packages/require_simple-1.0.tar.gz diff --git a/tests/data/packages/README.txt b/tests/data/packages/README.txt index c36d77f1b0e..b2f61b5138c 100644 --- a/tests/data/packages/README.txt +++ b/tests/data/packages/README.txt @@ -108,3 +108,8 @@ requires_wheelbroken_upper -------------------------- Requires wheelbroken and upper - used for testing implicit wheel building during install. + +require_simple-1.0.tar.gz +------------------------ +contains "require_simple" package which requires simple>=2.0 - used for testing +if dependencies are handled correctly. diff --git a/tests/data/packages/require_simple-1.0.tar.gz b/tests/data/packages/require_simple-1.0.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..61afdaff824970d82d39ea8b14264a870e09a946 GIT binary patch literal 735 zcmV<50wDb#iwFRqjcHc^1MON{PunmQ<~hH@g9kvOt?k6Mgw#pXu86ImA+`sEqR_aV zMdAe8>DGTgH_+0QhAgZKg8EY>H^(PBu8+TSu`1b3#wGI=kJE^?P#bo()(r?DB?Ooe zQ34moIRL^C6Au$J0*DBr-7P@YzLFKlln$g>%FXXV60&l=s`9oelV$cA`XE$0!}}O*&!&N4X2=Smc?-(Cx_s?f80O$*>}DqF>9s4h~10cW%^el zm!DfK4tR7g{mKVSsD;-)Fg1`o)w!*i@H7haFp=@SE4D=s=rj6pgvDOxx~nZzJJ2Rcb2 z1|0ZDjzs*Js{-2$ke}Dh+tgZj(dN!*0zfk`{YWPnOwES-b zvtr*VdcEVDrjTpje>n9pA(SdCoS0Ibkflt7ObjM|#KpCGR9PpfAF(lua;@jyyVrmk z{9om%f3i_|{{zud{YPlmwf8?wz>;XQQG@@5;9JK=CI1nf=YNlSIsXZ?`M(J))%ef7 z9E*lg$^SL{9iaaG55(BD_diXb+W!lkz!Pm$*MD^8e@qbuQ}4UP)_+Z4C(Pn_V&lGr Rg@whY;V;Vr>q`JC002_-cgz3) literal 0 HcmV?d00001 diff --git a/tests/functional/test_install_upgrade.py b/tests/functional/test_install_upgrade.py index f4a78f0ca7c..d866614bb41 100644 --- a/tests/functional/test_install_upgrade.py +++ b/tests/functional/test_install_upgrade.py @@ -11,18 +11,46 @@ from tests.lib.local_repos import local_checkout -def test_no_upgrade_unless_requested(script): +def test_upgrade_by_default(script): """ - No upgrade if not specifically requested. + It does upgrade if not specifically requested. """ script.pip('install', 'INITools==0.1', expect_error=True) result = script.pip('install', 'INITools', expect_error=True) - assert not result.files_created, ( - 'pip install INITools upgraded when it should not have' + assert result.files_created, ( + 'pip install INITools did not upgrade when it should have' ) +def test_does_not_upgrade_dependecies_if_existing_version_satisfies(script): + """ + It does not upgrade a dependency if it already satisfies the requirements. + + """ + script.pip('install', 'simple==2.0', expect_error=True) + result = script.pip('install', 'require_simple', expect_error=True) + + assert ( + script.site_packages / 'simple-2.0-py%s.egg-info' % + pyversion not in result.files_deleted + ), "should not have uninstalled simple==2.0" + + +def test_upgrade_dependecies_if_existing_version_does_not_satisfy(script): + """ + It does upgrade a dependency if it already satisfies the requirements. + + """ + script.pip('install', 'simple==1.0', expect_error=True) + result = script.pip('install', 'require_simple', expect_error=True) + + assert ( + script.site_packages / 'simple-1.0-py%s.egg-info' % + pyversion not in result.files_deleted + ), "should have uninstalled simple==1.0" + + @pytest.mark.network def test_upgrade_to_specific_version(script): """ @@ -51,8 +79,8 @@ def test_upgrade_if_requested(script): """ script.pip('install', 'INITools==0.1', expect_error=True) - result = script.pip('install', '--upgrade', 'INITools', expect_error=True) - assert result.files_created, 'pip install --upgrade did not upgrade' + result = script.pip('install', 'INITools', expect_error=True) + assert result.files_created, 'pip install did not upgrade' assert ( script.site_packages / 'INITools-0.1-py%s.egg-info' % pyversion not in result.files_created @@ -66,7 +94,7 @@ def test_upgrade_with_newest_already_installed(script, data): """ script.pip('install', '-f', data.find_links, '--no-index', 'simple') result = script.pip( - 'install', '--upgrade', '-f', data.find_links, '--no-index', 'simple' + 'install', '-f', data.find_links, '--no-index', 'simple' ) assert not result.files_created, 'simple upgraded when it should not have' assert 'already up-to-date' in result.stdout, result.stdout @@ -83,7 +111,7 @@ def test_upgrade_force_reinstall_newest(script): sorted(result.files_created.keys()) ) result2 = script.pip( - 'install', '--upgrade', '--force-reinstall', 'INITools' + 'install', '--force-reinstall', 'INITools' ) assert result2.files_updated, 'upgrade to INITools 0.3 failed' result3 = script.pip('uninstall', 'initools', '-y', expect_error=True) @@ -130,8 +158,8 @@ def test_uninstall_before_upgrade_from_url(script): @pytest.mark.network def test_upgrade_to_same_version_from_url(script): """ - When installing from a URL the same version that is already installed, no - need to uninstall and reinstall if --upgrade is not specified. + When installing from a URL the same version that is already installed, + ensure an uninstall and reinstall take place. """ result = script.pip('install', 'INITools==0.3', expect_error=True) @@ -144,7 +172,7 @@ def test_upgrade_to_same_version_from_url(script): '0.3.tar.gz', expect_error=True, ) - assert not result2.files_updated, 'INITools 0.3 reinstalled same version' + assert result2.files_updated, 'INITools 0.3 did not reinstall same version' result3 = script.pip('uninstall', 'initools', '-y', expect_error=True) assert_all_changes(result, result3, [script.venv / 'build', 'cache']) @@ -169,7 +197,7 @@ def test_upgrade_from_reqs_file(script): INITools """)) script.pip( - 'install', '--upgrade', '-r', script.scratch_path / 'test-req.txt' + 'install', '-r', script.scratch_path / 'test-req.txt' ) uninstall_result = script.pip( 'uninstall', '-r', script.scratch_path / 'test-req.txt', '-y' @@ -277,7 +305,7 @@ def test_upgrade_vcs_req_with_no_dists_found(script, tmpdir): tmpdir.join("cache"), ) script.pip("install", req) - result = script.pip("install", "-U", req) + result = script.pip("install", req) assert not result.returncode @@ -294,7 +322,7 @@ def test_upgrade_vcs_req_with_dist_found(script): ) ) script.pip("install", req, expect_stderr=True) - result = script.pip("install", "-U", req, expect_stderr=True) + result = script.pip("install", req, expect_stderr=True) assert "pypi.python.org" not in result.stdout, result.stdout @@ -304,13 +332,18 @@ class TestUpgradeDistributeToSetuptools(object): allow distribute to conflict with setuptools, so that the following would work to upgrade distribute: - ``pip install -U setuptools`` + ``pip install -U setuptools`` In pip7, the hacks were removed. This test remains to at least confirm pip can upgrade distribute to setuptools using: ``pip install -U distribute`` + In pip9, install started upgrading by default, thus removing the need to + pass -U: + + ``pip install distribute`` + The reason this works is that a final version of distribute (v0.7.3) was released that is simple wrapper with: @@ -354,7 +387,7 @@ def test_from_distribute_6_to_setuptools_7( ) result = self.script.run( self.ve_bin / 'pip', 'install', '--no-index', - '--find-links=%s' % data.find_links, '-U', 'distribute', + '--find-links=%s' % data.find_links, 'distribute', expect_stderr=True if sys.version_info[:2] == (2, 6) else False, ) assert ( From e9e39db253da22654bca0d1f49bfe89e26263b88 Mon Sep 17 00:00:00 2001 From: "Pradyun S. Gedam" Date: Wed, 22 Jun 2016 17:27:02 +0530 Subject: [PATCH 07/17] Change tests according to new target behaviour --- tests/functional/test_install.py | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/tests/functional/test_install.py b/tests/functional/test_install.py index 1440f3335f9..07fb62c73a0 100644 --- a/tests/functional/test_install.py +++ b/tests/functional/test_install.py @@ -527,33 +527,17 @@ def test_install_package_with_target(script): str(result) ) - # Test repeated call without --upgrade, no files should have changed + # Test repeated call, no files should have changed result = script.pip_install_local( '-t', target_dir, "simple==1.0", expect_stderr=True, ) assert not Path('scratch') / 'target' / 'simple' in result.files_updated - # Test upgrade call, check that new version is installed - result = script.pip_install_local('--upgrade', '-t', - target_dir, "simple==2.0") - assert Path('scratch') / 'target' / 'simple' in result.files_updated, ( - str(result) - ) - egg_folder = ( - Path('scratch') / 'target' / 'simple-2.0-py%s.egg-info' % pyversion) - assert egg_folder in result.files_created, ( - str(result) - ) - - # Test install and upgrade of single-module package + # Test install of single-module package result = script.pip_install_local('-t', target_dir, 'singlemodule==0.0.0') singlemodule_py = Path('scratch') / 'target' / 'singlemodule.py' assert singlemodule_py in result.files_created, str(result) - result = script.pip_install_local('-t', target_dir, 'singlemodule==0.0.1', - '--upgrade') - assert singlemodule_py in result.files_updated, str(result) - def test_install_package_with_root(script, data): """ @@ -815,7 +799,7 @@ def test_install_upgrade_editable_depending_on_other_editable(script): version='0.1', install_requires=['pkga']) """)) - script.pip('install', '--upgrade', '--editable', pkgb_path, '--no-index') + script.pip('install', '--editable', pkgb_path, '--no-index') result = script.pip('list', '--format=freeze') assert "pkgb==0.1" in result.stdout From ce21547f71a47d148490d692b9d20cfd76cde721 Mon Sep 17 00:00:00 2001 From: "Pradyun S. Gedam" Date: Wed, 22 Jun 2016 21:17:52 +0530 Subject: [PATCH 08/17] Update documentation according to new behaviour --- docs/quickstart.rst | 6 ++-- docs/reference/pip_install.rst | 7 ----- docs/user_guide.rst | 50 ++++++++++++++-------------------- 3 files changed, 23 insertions(+), 40 deletions(-) diff --git a/docs/quickstart.rst b/docs/quickstart.rst index efd6d348630..560048d6999 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -11,7 +11,7 @@ Install a package from `PyPI`_: [...] Successfully installed SomePackage -Install a package already downloaded from `PyPI`_ or got elsewhere. +Install a package already downloaded from `PyPI`_ or obtained via other means. This is useful if the target machine does not have a network connection: :: @@ -39,11 +39,11 @@ List what packages are outdated: $ pip list --outdated SomePackage (Current: 1.0 Latest: 2.0) -Upgrade a package: +To upgrade an existing package, simply run the install command again: :: - $ pip install --upgrade SomePackage + $ pip install SomePackage [...] Found existing installation: SomePackage 1.0 Uninstalling SomePackage: diff --git a/docs/reference/pip_install.rst b/docs/reference/pip_install.rst index 6dac31e8c02..e5de9a53979 100644 --- a/docs/reference/pip_install.rst +++ b/docs/reference/pip_install.rst @@ -758,13 +758,6 @@ Examples $ pip install -r requirements.txt -#. Upgrade an already installed `SomePackage` to the latest from PyPI. - - :: - - $ pip install --upgrade SomePackage - - #. Install a local project in "editable" mode. See the section on :ref:`Editable Installs `. :: diff --git a/docs/user_guide.rst b/docs/user_guide.rst index 6bffdc22a4b..cfe812e1afd 100644 --- a/docs/user_guide.rst +++ b/docs/user_guide.rst @@ -469,35 +469,33 @@ Then, to install from local only, you'll be using :ref:`--find-links $ pip install --no-index --find-links=DIR -r requirements.txt -"Only if needed" Recursive Upgrade -********************************** +Upgrade Behaviour +***************** -``pip install --upgrade`` is currently written to perform an eager recursive -upgrade, i.e. it upgrades all dependencies regardless of whether they still -satisfy the new parent requirements. +``pip install`` is currently performs a non-eager recursive upgrade, i.e. +it upgrades dependencies only when they no longer satisfy the new parent +requirements. -E.g. supposing: +As an example, consider this scenario: +* Only `SomePackage-1.0` and `AnotherPackage-1.0` are currently installed +* `SomePackage-2.0`, `AnotherPackage-2.0` are the latest versions available on PyPI. * `SomePackage-1.0` requires `AnotherPackage>=1.0` * `SomePackage-2.0` requires `AnotherPackage>=1.0` and `OneMorePackage==1.0` -* `SomePackage-1.0` and `AnotherPackage-1.0` are currently installed -* `SomePackage-2.0` and `AnotherPackage-2.0` are the latest versions available on PyPI. - -Running ``pip install --upgrade SomePackage`` would upgrade `SomePackage` *and* -`AnotherPackage` despite `AnotherPackage` already being satisfied. -pip doesn't currently have an option to do an "only if needed" recursive -upgrade, but you can achieve it using these 2 steps:: +Running ``pip install SomePackage`` would: +* install `OneMorePackage-1.0` and `SomePackage-2.0` +* do not install/upgrade `AnotherPackage` as it satisfies existing requirements - pip install --upgrade --no-deps SomePackage - pip install SomePackage +pip doesn't currently have an option to do an upgrade package(s) and +dependencies to latest version. This can currently only be achieved by +explicitly listing all the dependencies of the package(s) as command line +arguments or in a requirements file. -The first line will upgrade `SomePackage`, but not dependencies like -`AnotherPackage`. The 2nd line will fill in new dependencies like -`OneMorePackage`. - -See :issue:`59` for a plan of making "only if needed" recursive the default -behavior for a new ``pip upgrade`` command. +See :issue:`59` for discussion related to this behaviour. An ``upgrade-all`` +command has been proposed, with the intent of using it in virtual environments +as an alternative to the earlier behaviour (before pip 9.0) of eager upgrading, +upgrading dependencies to the latest version. User Installs @@ -565,10 +563,6 @@ From within a real python, where ``SomePackage`` *is* installed globally, but is $ pip install --user SomePackage [...] - Requirement already satisfied (use --upgrade to upgrade) - - $ pip install --user --upgrade SomePackage - [...] Successfully installed SomePackage @@ -576,10 +570,6 @@ From within a real python, where ``SomePackage`` *is* installed globally, and is $ pip install --user SomePackage [...] - Requirement already satisfied (use --upgrade to upgrade) - - $ pip install --user --upgrade SomePackage - [...] Requirement already up-to-date: SomePackage # force the install @@ -657,7 +647,7 @@ You can then install from the archive like this:: $ tempdir=$(mktemp -d /tmp/wheelhouse-XXXXX) $ (cd $tempdir; tar -xvf /path/to/bundled.tar.bz2) - $ pip install --force-reinstall --ignore-installed --upgrade --no-index --no-deps $tempdir/* + $ pip install --force-reinstall --ignore-installed --no-index --no-deps $tempdir/* Note that compiled packages are typically OS- and architecture-specific, so these archives are not necessarily portable across machines. From e0eb3121accac6d3625df764f9e5320645ff91e4 Mon Sep 17 00:00:00 2001 From: "Pradyun S. Gedam" Date: Thu, 23 Jun 2016 11:40:26 +0530 Subject: [PATCH 09/17] Make corrections in new tests The tests added for non-eager upgrading checks were passing but they were not checking if things got installed. Upon addition of these checks, the tests started failing sincethey were not using the data/packages directory. This patch fixes the tests to run pip with correct arguments and check for the right things. --- tests/data/packages/require_simple-1.0.tar.gz | Bin 735 -> 760 bytes tests/functional/test_install_upgrade.py | 26 +++++++++++++----- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/tests/data/packages/require_simple-1.0.tar.gz b/tests/data/packages/require_simple-1.0.tar.gz index 61afdaff824970d82d39ea8b14264a870e09a946..c4eca04a783de0cc5b102062dad0552b8c6b30bc 100644 GIT binary patch literal 760 zcmV}pp6|72-%bT4vcadl~OWnXh?ZE$R5Eio=IE_7jX0PUD>Z<{a> z#(B-B@VXamrIHv3SgMps)BdYlTCM8#qN)&09EJ*p851qveok7Nk~LYdco$n<7S;mqFMV#gdx9kq>8cjnrjCr%wW*)t}Rv5O!T*vc$>h}z4`#s+`h-b7w zS*TDFV&pO$O-oTkEB5{Eef2}C^_d|5^nbxsnRkZcs7e3(Grz9#KXdE;cc@F5K`8wH z=$!K?3Su$NE=(0h!6;Om^a-6m6cZ6hK8cGs6Sv08GEPSN$ute{{JBT}byK$){cn~3 z9ow^A{m6tlmRU;XaiFp+DToe^OsZ?k0U5_) z6y)LXDvWqxnse)(2vgqQt%uX@fl1~}xSU-g`@1^6ZjG_fe|?e!H{TTf^PTI@UiF<0 z-MOgtRVVdp=kD#_yxvFdYF7UzM<1+DgZIa+>EB__Qvc69y+Hph@P(^TH+`Y??OLLT zM2CPm&|*F$_1G|LJ5KejA(YcYayB>`9Dg5}AG4HOxmM=F^t#l^L@qyDJPqSyVf`Er zxhU>@_Ldhz8Ru$#R%JX%LN(50y0AdIt=#`RJc_K@fS~(N`roeqa~ssGdCv#)R7oo=iC4f?;{a{l+IkN&R}s%GC@ zJ@qqJ1w_>-m>WdQ$~_~Vb1~v#I1Q3mTor3KSFPgOO%JY9(EnQf-{+}j>OZxY^v@j6 zh5p+B6(9QFQ2%$6Z=>~}+P&ra?`s7?|83O&#l#h<0Qz62{|C>7x1RqxJ(r^Xx55VV qUkmeJ00000000000000000000000000002sY4IE1Z+2S%PyhfL4UJR) literal 735 zcmV<50wDb#iwFRqjcHc^1MON{PunmQ<~hH@g9kvOt?k6Mgw#pXu86ImA+`sEqR_aV zMdAe8>DGTgH_+0QhAgZKg8EY>H^(PBu8+TSu`1b3#wGI=kJE^?P#bo()(r?DB?Ooe zQ34moIRL^C6Au$J0*DBr-7P@YzLFKlln$g>%FXXV60&l=s`9oelV$cA`XE$0!}}O*&!&N4X2=Smc?-(Cx_s?f80O$*>}DqF>9s4h~10cW%^el zm!DfK4tR7g{mKVSsD;-)Fg1`o)w!*i@H7haFp=@SE4D=s=rj6pgvDOxx~nZzJJ2Rcb2 z1|0ZDjzs*Js{-2$ke}Dh+tgZj(dN!*0zfk`{YWPnOwES-b zvtr*VdcEVDrjTpje>n9pA(SdCoS0Ibkflt7ObjM|#KpCGR9PpfAF(lua;@jyyVrmk z{9om%f3i_|{{zud{YPlmwf8?wz>;XQQG@@5;9JK=CI1nf=YNlSIsXZ?`M(J))%ef7 z9E*lg$^SL{9iaaG55(BD_diXb+W!lkz!Pm$*MD^8e@qbuQ}4UP)_+Z4C(Pn_V&lGr Rg@whY;V;Vr>q`JC002_-cgz3) diff --git a/tests/functional/test_install_upgrade.py b/tests/functional/test_install_upgrade.py index d866614bb41..2e143f899c1 100644 --- a/tests/functional/test_install_upgrade.py +++ b/tests/functional/test_install_upgrade.py @@ -28,12 +28,16 @@ def test_does_not_upgrade_dependecies_if_existing_version_satisfies(script): It does not upgrade a dependency if it already satisfies the requirements. """ - script.pip('install', 'simple==2.0', expect_error=True) - result = script.pip('install', 'require_simple', expect_error=True) + script.pip_install_local('simple==2.0', expect_error=True) + result = script.pip_install_local('require_simple', expect_error=True) assert ( - script.site_packages / 'simple-2.0-py%s.egg-info' % - pyversion not in result.files_deleted + (script.site_packages / 'require_simple-1.0-py%s.egg-info' % pyversion) + not in result.files_deleted + ), "should have installed require_simple==1.0" + assert ( + (script.site_packages / 'simple-2.0-py%s.egg-info' % pyversion) + not in result.files_deleted ), "should not have uninstalled simple==2.0" @@ -42,12 +46,20 @@ def test_upgrade_dependecies_if_existing_version_does_not_satisfy(script): It does upgrade a dependency if it already satisfies the requirements. """ - script.pip('install', 'simple==1.0', expect_error=True) - result = script.pip('install', 'require_simple', expect_error=True) + script.pip_install_local('simple==1.0', expect_error=True) + result = script.pip_install_local('require_simple', expect_error=True) + assert ( + (script.site_packages / 'require_simple-1.0-py%s.egg-info' % pyversion) + not in result.files_deleted + ), "should have installed require_simple==1.0" + assert ( + script.site_packages / 'simple-3.0-py%s.egg-info' % + pyversion in result.files_created + ), "should have installed simple==3.0" assert ( script.site_packages / 'simple-1.0-py%s.egg-info' % - pyversion not in result.files_deleted + pyversion in result.files_deleted ), "should have uninstalled simple==1.0" From 5f7623c06ccdfad8947fcbc54fc030e37dacd806 Mon Sep 17 00:00:00 2001 From: "Pradyun S. Gedam" Date: Fri, 24 Jun 2016 16:44:15 +0530 Subject: [PATCH 10/17] Modify docs on upgrade behaviour The section that commented on the upgrade behaviour now metions the original purpose of the adding the section and adds a historic note. --- docs/user_guide.rst | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/docs/user_guide.rst b/docs/user_guide.rst index cfe812e1afd..22363b68734 100644 --- a/docs/user_guide.rst +++ b/docs/user_guide.rst @@ -469,33 +469,32 @@ Then, to install from local only, you'll be using :ref:`--find-links $ pip install --no-index --find-links=DIR -r requirements.txt -Upgrade Behaviour -***************** +"Only if needed" Recursive Upgrade +********************************** + +``pip install``, now, defaults to performs a "only if needed" recursive +upgrade, i.e. it upgrades dependencies only when they no longer satisfy the +new parent requirements. This was not always the case. -``pip install`` is currently performs a non-eager recursive upgrade, i.e. -it upgrades dependencies only when they no longer satisfy the new parent -requirements. +Before pip 9.0, the upgrade behaviour defaulted to a different "eager" +upgrade, i.e. it used to upgrade all dependencies regardless of whether they +already satisfied the new parent requirements. This behaviour was seen as +problematic and was replaced with the current behaviour in pip 9.0. This +section contained instructions on how to work around the problematic +behaviour. The recommended fix for that behaviour, now, is to upgrade to +pip 9.0 or greater. -As an example, consider this scenario: +See :issue:`59` and :issue:`3786` for the discussion on this change in +behaviour. -* Only `SomePackage-1.0` and `AnotherPackage-1.0` are currently installed -* `SomePackage-2.0`, `AnotherPackage-2.0` are the latest versions available on PyPI. -* `SomePackage-1.0` requires `AnotherPackage>=1.0` -* `SomePackage-2.0` requires `AnotherPackage>=1.0` and `OneMorePackage==1.0` -Running ``pip install SomePackage`` would: -* install `OneMorePackage-1.0` and `SomePackage-2.0` -* do not install/upgrade `AnotherPackage` as it satisfies existing requirements +As an historic note, the fix for the behaviour was:: -pip doesn't currently have an option to do an upgrade package(s) and -dependencies to latest version. This can currently only be achieved by -explicitly listing all the dependencies of the package(s) as command line -arguments or in a requirements file. + pip install --upgrade --no-deps SomePackage + pip install SomePackage -See :issue:`59` for discussion related to this behaviour. An ``upgrade-all`` -command has been proposed, with the intent of using it in virtual environments -as an alternative to the earlier behaviour (before pip 9.0) of eager upgrading, -upgrading dependencies to the latest version. +A proposal for an ``upgrade-all`` command is being considered as a safer +alternative to the earlier behaviour of eager upgrading. User Installs From 233e9e326b790b81d28a32554ded93d3491faf68 Mon Sep 17 00:00:00 2001 From: "Pradyun S. Gedam" Date: Fri, 24 Jun 2016 17:18:48 +0530 Subject: [PATCH 11/17] Separate logic for package upgrade skipping message --- pip/req/req_set.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/pip/req/req_set.py b/pip/req/req_set.py index 579f15ecb74..7f0e70a4aee 100644 --- a/pip/req/req_set.py +++ b/pip/req/req_set.py @@ -397,17 +397,12 @@ def _check_skip_installed(self, req_to_install, finder): # Check whether to upgrade/reinstall this req or not. req_to_install.check_if_exists() if req_to_install.satisfied_by: - upgrade_allowed = False + upgrade_allowed = req_to_install.is_direct - # Determine why upgrading this may not allowed. - if req_to_install.is_direct: - upgrade_allowed = True - skip_reason = 'already satisfied' - else: - skip_reason = 'skipped as not directly required' + # Is the best version is installed. + best_installed = False if upgrade_allowed: - best_installed = False # For link based requirements we have to pull the # tree down and inspect to assess the version #, so # its handled way down. @@ -416,7 +411,6 @@ def _check_skip_installed(self, req_to_install, finder): finder.find_requirement( req_to_install, upgrade_allowed) except BestVersionAlreadyInstalled: - skip_reason = 'already up-to-date' best_installed = True except DistributionNotFound: # No distribution found, so we squash the @@ -433,6 +427,17 @@ def _check_skip_installed(self, req_to_install, finder): req_to_install.conflicts_with = \ req_to_install.satisfied_by req_to_install.satisfied_by = None + + # Figure out a nice message to say why we're skipping this. + if best_installed: + skip_reason = 'already up-to-date' + elif not upgrade_allowed: + # NOTE: Change this message if someday the upgrade strategy + # changes. + skip_reason = 'not upgraded as not directly required' + else: + skip_reason = 'already satisfied' + return skip_reason else: return None From 145f808c657f382ce5bcbb66a7e48cb0789dcc7a Mon Sep 17 00:00:00 2001 From: "Pradyun S. Gedam" Date: Fri, 24 Jun 2016 17:24:10 +0530 Subject: [PATCH 12/17] For --target, mention that the directory has been skipped --- pip/commands/install.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pip/commands/install.py b/pip/commands/install.py index 82dc087c511..db2926799bc 100644 --- a/pip/commands/install.py +++ b/pip/commands/install.py @@ -363,7 +363,8 @@ def run(self, options, args): target_item_dir = os.path.join(options.target_dir, item) if os.path.exists(target_item_dir): logger.warning( - 'Target directory %s already exists.', + 'Skipping target directory %s as it already ' + 'exists.', target_item_dir ) continue From 5bb0003d86896149375459ea77792cf7c097ff85 Mon Sep 17 00:00:00 2001 From: "Pradyun S. Gedam" Date: Fri, 24 Jun 2016 17:25:36 +0530 Subject: [PATCH 13/17] Fix test documentation --- tests/functional/test_install_upgrade.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/test_install_upgrade.py b/tests/functional/test_install_upgrade.py index 2e143f899c1..d3488923975 100644 --- a/tests/functional/test_install_upgrade.py +++ b/tests/functional/test_install_upgrade.py @@ -25,7 +25,7 @@ def test_upgrade_by_default(script): def test_does_not_upgrade_dependecies_if_existing_version_satisfies(script): """ - It does not upgrade a dependency if it already satisfies the requirements. + It doesn't upgrade a dependency if it already satisfies the requirements. """ script.pip_install_local('simple==2.0', expect_error=True) From 3798a1d6ef526003276999981d32ea34629503a8 Mon Sep 17 00:00:00 2001 From: "Pradyun S. Gedam" Date: Fri, 24 Jun 2016 17:30:07 +0530 Subject: [PATCH 14/17] Add a test to ensure that passing --upgrade doesn't change the behaviour --- tests/functional/test_install_upgrade.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/functional/test_install_upgrade.py b/tests/functional/test_install_upgrade.py index d3488923975..3fe1bf8ac34 100644 --- a/tests/functional/test_install_upgrade.py +++ b/tests/functional/test_install_upgrade.py @@ -63,6 +63,26 @@ def test_upgrade_dependecies_if_existing_version_does_not_satisfy(script): ), "should have uninstalled simple==1.0" +def test_upgrade_option_does_not_affect_behaviour(script): + """ + It does not change behaviour on passing --upgrade. + """ + script.pip_install_local('simple==2.0', expect_error=True) + result = script.pip_install_local( + 'require_simple', '--upgrade', expect_error=True + ) + + assert ( + (script.site_packages / 'require_simple-1.0-py%s.egg-info' % pyversion) + not in result.files_deleted + ), "should have installed require_simple==1.0" + assert ( + (script.site_packages / 'simple-2.0-py%s.egg-info' % pyversion) + not in result.files_deleted + ), "should not have uninstalled simple==2.0" + + + @pytest.mark.network def test_upgrade_to_specific_version(script): """ From 111d0e666b3dd3ae6030a08dc02af5f66ea0b81f Mon Sep 17 00:00:00 2001 From: "Pradyun S. Gedam" Date: Sat, 25 Jun 2016 12:00:45 +0530 Subject: [PATCH 15/17] Add tests for checking more new behaviour The behaviour which gets tests added in this was discussed in #536 and was implemented by accident in #3806. --- tests/functional/test_install.py | 86 ++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/tests/functional/test_install.py b/tests/functional/test_install.py index 07fb62c73a0..8b088618b0e 100644 --- a/tests/functional/test_install.py +++ b/tests/functional/test_install.py @@ -1010,3 +1010,89 @@ def test_double_install_fail(script, data): msg = ("Double requirement given: pip==7.1.2 (already in pip==*, " "name='pip')") assert msg in result.stderr + + +@pytest.mark.network +def test_install_when_older_version_explicitly_passed_as_url(script): + """ + When a file URL is passed, forcefully reinstall the package regardless + of whether it has a newer version already installed. + + """ + result = script.pip('install', 'INITools==0.3', expect_error=True) + assert script.site_packages / 'initools' in result.files_created, ( + sorted(result.files_created.keys()) + ) + result2 = script.pip( + 'install', + 'https://pypi.python.org/packages/source/I/INITools/INITools-' + '0.2.tar.gz', + expect_error=True, + ) + assert result2.files_updated, ( + 'INITools==0.2 was not installed over INITools==0.3 when explicitly ' + 'passed with a URL' + ) + + +def test_install_when_older_version_explicitly_passed_as_path(script, data): + """ + When a file path is passed, forcefully reinstall the package regardless + of whether it has a newer version already installed. + + """ + result = script.pip_install_local('simple==3.0', expect_error=True) + assert script.site_packages / 'simple' in result.files_created, ( + sorted(result.files_created.keys()) + ) + result2 = script.pip_install_local( + data.packages.join("simple-2.0.tar.gz"), + expect_error=True, + ) + assert result2.files_updated, ( + 'simple==0.2 was not installed over simple==0.3 when explicitly ' + 'passed with a path' + ) + + +@pytest.mark.network +def test_install_when_same_version_explicitly_passed_as_url(script): + """ + When a file URL is passed, forcefully reinstall the package regardless + of whether it has a newer version already installed. + + """ + result = script.pip('install', 'INITools==0.3', expect_error=True) + assert script.site_packages / 'initools' in result.files_created, ( + sorted(result.files_created.keys()) + ) + result2 = script.pip( + 'install', + 'https://pypi.python.org/packages/source/I/INITools/INITools-' + '0.3.tar.gz', + expect_error=True, + ) + assert result2.files_updated, ( + 'INITools==0.3 was not installed over INITools==0.3 when explicitly ' + 'passed with a URL' + ) + + +def test_install_when_same_version_explicitly_passed_as_path(script, data): + """ + When a file path is passed, forcefully reinstall the package regardless + of whether it has a newer version already installed. + + """ + result = script.pip_install_local('simple==3.0', expect_error=True) + assert script.site_packages / 'simple' in result.files_created, ( + sorted(result.files_created.keys()) + ) + result2 = script.pip_install_local( + data.packages.join("simple-3.0.tar.gz"), + expect_error=True, + ) + assert result2.files_updated, ( + 'simple==0.3 was not installed over simple==0.3 when explicitly ' + 'passed with a path' + ) From 8b4e94a3ac2dad089807d9acedf20586980da364 Mon Sep 17 00:00:00 2001 From: "Pradyun S. Gedam" Date: Sat, 25 Jun 2016 12:15:45 +0530 Subject: [PATCH 16/17] Remove all references to --upgrade --- pip/commands/wheel.py | 14 +++++++------- pip/utils/outdated.py | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pip/commands/wheel.py b/pip/commands/wheel.py index 53f85e31b27..a8096196de1 100644 --- a/pip/commands/wheel.py +++ b/pip/commands/wheel.py @@ -107,18 +107,18 @@ def check_required_packages(self): "'pip wheel' requires the 'wheel' package. To fix this, run: " "pip install wheel" ) + + need_setuptools_message = ( + "'pip wheel' requires setuptools >= 0.8 for dist-info support. " + "To fix this, run: pip install setuptools>=0.8" + ) pkg_resources = import_or_raise( 'pkg_resources', CommandError, - "'pip wheel' requires setuptools >= 0.8 for dist-info support." - " To fix this, run: pip install --upgrade setuptools" + need_setuptools_message ) if not hasattr(pkg_resources, 'DistInfoDistribution'): - raise CommandError( - "'pip wheel' requires setuptools >= 0.8 for dist-info " - "support. To fix this, run: pip install --upgrade " - "setuptools" - ) + raise CommandError(need_setuptools_message) def run(self, options, args): self.check_required_packages() diff --git a/pip/utils/outdated.py b/pip/utils/outdated.py index 2164cc3cc2f..2bc65a56d8a 100644 --- a/pip/utils/outdated.py +++ b/pip/utils/outdated.py @@ -151,7 +151,7 @@ def pip_version_check(session): logger.warning( "You are using pip version %s, however version %s is " "available.\nYou should consider upgrading via the " - "'%s install --upgrade pip' command.", + "'%s install pip' command.", pip_version, pypi_version, pip_cmd ) From 6d12474ce88357daa4b271183c81fdca8d4b3e0e Mon Sep 17 00:00:00 2001 From: "Pradyun S. Gedam" Date: Sat, 25 Jun 2016 16:04:34 +0530 Subject: [PATCH 17/17] Fix the lone pep8 warning that broke the CI build I have now realized how powerful newlines are. :P --- tests/functional/test_install_upgrade.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/functional/test_install_upgrade.py b/tests/functional/test_install_upgrade.py index 3fe1bf8ac34..1ba25ce4a5d 100644 --- a/tests/functional/test_install_upgrade.py +++ b/tests/functional/test_install_upgrade.py @@ -82,7 +82,6 @@ def test_upgrade_option_does_not_affect_behaviour(script): ), "should not have uninstalled simple==2.0" - @pytest.mark.network def test_upgrade_to_specific_version(script): """