Skip to content

Commit bbf37ad

Browse files
committed
Adds --upgrade-recursive option, makes -U non-recursive by default.
refs pypa#304
1 parent 5562a6b commit bbf37ad

File tree

2 files changed

+47
-17
lines changed

2 files changed

+47
-17
lines changed

pip/commands/install.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,12 @@ def __init__(self):
114114
dest='upgrade',
115115
action='store_true',
116116
help='Upgrade all packages to the newest available version')
117+
self.parser.add_option(
118+
'-R', '--upgrade-recursive',
119+
dest='upgrade_recursive',
120+
action='store_true',
121+
help='Upgrade package to the newest available version, recursing '
122+
'into its dependencies')
117123
self.parser.add_option(
118124
'--force-reinstall',
119125
dest='force_reinstall',
@@ -213,7 +219,8 @@ def run(self, options, args):
213219
src_dir=options.src_dir,
214220
download_dir=options.download_dir,
215221
download_cache=options.download_cache,
216-
upgrade=options.upgrade,
222+
upgrade=options.upgrade or options.upgrade_recursive,
223+
upgrade_recursive=options.upgrade_recursive,
217224
as_egg=options.as_egg,
218225
ignore_installed=options.ignore_installed,
219226
ignore_dependencies=options.ignore_dependencies,

pip/req.py

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
class InstallRequirement(object):
3737

3838
def __init__(self, req, comes_from, source_dir=None, editable=False,
39-
url=None, as_egg=False, update=True):
39+
url=None, as_egg=False, update=True, is_extra=False):
4040
self.extras = ()
4141
if isinstance(req, string_types):
4242
req = pkg_resources.Requirement.parse(req)
@@ -58,6 +58,8 @@ def __init__(self, req, comes_from, source_dir=None, editable=False,
5858
self._is_bundle = None
5959
# True if the editable should be updated:
6060
self.update = update
61+
# True if the requirement comes from another requirement's extras
62+
self.is_extra = is_extra
6163
# Set to True after successful installation
6264
self.install_succeeded = None
6365
# UninstallPathSet of uninstalled distribution (for possible rollback)
@@ -208,6 +210,10 @@ def url_name(self):
208210
def setup_py(self):
209211
return os.path.join(self.source_dir, 'setup.py')
210212

213+
@property
214+
def is_subrequirement(self):
215+
return self.req and self.comes_from.__class__ is self.__class__
216+
211217
def run_egg_info(self, force_root_egg_info=False):
212218
assert self.source_dir
213219
if self.name:
@@ -353,7 +359,7 @@ def requirements(self, extras=()):
353359
logger.debug('skipping extra %s' % in_extra)
354360
# Skip requirement for an extra we aren't requiring
355361
continue
356-
yield line
362+
yield line, bool(in_extra in extras)
357363

358364
@property
359365
def absolute_versions(self):
@@ -808,13 +814,15 @@ def __repr__(self):
808814
class RequirementSet(object):
809815

810816
def __init__(self, build_dir, src_dir, download_dir, download_cache=None,
811-
upgrade=False, ignore_installed=False, as_egg=False,
812-
ignore_dependencies=False, force_reinstall=False, use_user_site=False):
817+
ignore_installed=False, as_egg=False, force_reinstall=False,
818+
ignore_dependencies=False, use_user_site=False, upgrade=False,
819+
upgrade_recursive=False):
813820
self.build_dir = build_dir
814821
self.src_dir = src_dir
815822
self.download_dir = download_dir
816823
self.download_cache = download_cache
817-
self.upgrade = upgrade
824+
self.upgrade = upgrade or upgrade_recursive
825+
self.upgrade_recursive = upgrade_recursive
818826
self.ignore_installed = ignore_installed
819827
self.force_reinstall = force_reinstall
820828
self.requirements = Requirements()
@@ -908,15 +916,19 @@ def locate_files(self):
908916
if not self.ignore_installed and not req_to_install.editable:
909917
req_to_install.check_if_exists()
910918
if req_to_install.satisfied_by:
911-
if self.upgrade:
919+
if self.is_upgradeable(req_to_install):
912920
req_to_install.conflicts_with = req_to_install.satisfied_by
913921
req_to_install.satisfied_by = None
914922
else:
915923
install_needed = False
916924
if req_to_install.satisfied_by:
925+
if self.upgrade and not self.upgrade_recursive:
926+
upgrade_cmd = '--upgrade-recursive'
927+
else:
928+
upgrade_cmd = '--upgrade'
917929
logger.notify('Requirement already satisfied '
918-
'(use --upgrade to upgrade): %s'
919-
% req_to_install)
930+
'(use %s to upgrade): %s'
931+
% (upgrade_cmd, req_to_install))
920932

921933
if req_to_install.editable:
922934
if req_to_install.source_dir is None:
@@ -946,11 +958,11 @@ def prepare_files(self, finder, force_root_egg_info=False, bundle=False):
946958
if not self.ignore_installed and not req_to_install.editable:
947959
req_to_install.check_if_exists()
948960
if req_to_install.satisfied_by:
949-
if self.upgrade:
961+
if self.is_upgradeable(req_to_install):
950962
if not self.force_reinstall and not req_to_install.url:
951963
try:
952964
url = finder.find_requirement(
953-
req_to_install, self.upgrade)
965+
req_to_install, upgrade=True)
954966
except BestVersionAlreadyInstalled:
955967
best_installed = True
956968
install = False
@@ -970,9 +982,13 @@ def prepare_files(self, finder, force_root_egg_info=False, bundle=False):
970982
logger.notify('Requirement already up-to-date: %s'
971983
% req_to_install)
972984
else:
985+
if self.upgrade and not self.upgrade_recursive:
986+
upgrade_cmd = '--upgrade-recursive'
987+
else:
988+
upgrade_cmd = '--upgrade'
973989
logger.notify('Requirement already satisfied '
974-
'(use --upgrade to upgrade): %s'
975-
% req_to_install)
990+
'(use %s to upgrade): %s'
991+
% (upgrade_cmd, req_to_install))
976992
if req_to_install.editable:
977993
logger.notify('Obtaining %s' % req_to_install)
978994
elif install:
@@ -1012,7 +1028,8 @@ def prepare_files(self, finder, force_root_egg_info=False, bundle=False):
10121028
if req_to_install.url is None:
10131029
if not_found:
10141030
raise not_found
1015-
url = finder.find_requirement(req_to_install, upgrade=self.upgrade)
1031+
url = finder.find_requirement(req_to_install,
1032+
upgrade=self.is_upgradeable(req_to_install))
10161033
else:
10171034
## FIXME: should req_to_install.url already be a link?
10181035
url = Link(req_to_install.url)
@@ -1057,7 +1074,7 @@ def prepare_files(self, finder, force_root_egg_info=False, bundle=False):
10571074
# repeat check_if_exists to uninstall-on-upgrade (#14)
10581075
req_to_install.check_if_exists()
10591076
if req_to_install.satisfied_by:
1060-
if self.upgrade or self.ignore_installed:
1077+
if self.ignore_installed or self.is_upgradeable(req_to_install):
10611078
req_to_install.conflicts_with = req_to_install.satisfied_by
10621079
req_to_install.satisfied_by = None
10631080
else:
@@ -1068,7 +1085,7 @@ def prepare_files(self, finder, force_root_egg_info=False, bundle=False):
10681085
if (req_to_install.extras):
10691086
logger.notify("Installing extra requirements: %r" % ','.join(req_to_install.extras))
10701087
if not self.ignore_dependencies:
1071-
for req in req_to_install.requirements(req_to_install.extras):
1088+
for req, is_extra in req_to_install.requirements(req_to_install.extras):
10721089
try:
10731090
name = pkg_resources.Requirement.parse(req).project_name
10741091
except ValueError:
@@ -1079,7 +1096,7 @@ def prepare_files(self, finder, force_root_egg_info=False, bundle=False):
10791096
if self.has_requirement(name):
10801097
## FIXME: check for conflict
10811098
continue
1082-
subreq = InstallRequirement(req, req_to_install)
1099+
subreq = InstallRequirement(req, req_to_install, is_extra=is_extra)
10831100
reqs.append(subreq)
10841101
self.add_requirement(subreq)
10851102
if req_to_install.name not in self.requirements:
@@ -1096,6 +1113,12 @@ def prepare_files(self, finder, force_root_egg_info=False, bundle=False):
10961113
finally:
10971114
logger.indent -= 2
10981115

1116+
def is_upgradeable(self, requirement):
1117+
if requirement.is_subrequirement and not requirement.is_extra:
1118+
return self.upgrade_recursive
1119+
else:
1120+
return self.upgrade
1121+
10991122
def cleanup_files(self, bundle=False):
11001123
"""Clean up files, remove builds."""
11011124
logger.notify('Cleaning up...')

0 commit comments

Comments
 (0)