From 2f664545d26480a6de9bbc6640330cd3441b6f92 Mon Sep 17 00:00:00 2001 From: Frankie Dintino Date: Fri, 13 Jul 2012 13:22:18 -0400 Subject: [PATCH] Adds --upgrade-recursive option, makes -U non-recursive by default. refs #304 --- pip/commands/install.py | 15 ++++++--- pip/req.py | 72 +++++++++++++++++++++++++---------------- 2 files changed, 55 insertions(+), 32 deletions(-) diff --git a/pip/commands/install.py b/pip/commands/install.py index 71ec42f5195..9c8252e0336 100644 --- a/pip/commands/install.py +++ b/pip/commands/install.py @@ -113,7 +113,12 @@ def __init__(self): '-U', '--upgrade', dest='upgrade', action='store_true', - help='Upgrade all packages to the newest available version') + help='Upgrade packages to the latest version, but not dependencies') + self.parser.add_option( + '-R', '--upgrade-recursive', + dest='upgrade_recursive', + action='store_true', + help='Upgrade packages to the latest version, including dependencies') self.parser.add_option( '--force-reinstall', dest='force_reinstall', @@ -213,18 +218,20 @@ def run(self, options, args): src_dir=options.src_dir, download_dir=options.download_dir, download_cache=options.download_cache, - upgrade=options.upgrade, + upgrade=options.upgrade or options.upgrade_recursive, + upgrade_recursive=options.upgrade_recursive, as_egg=options.as_egg, ignore_installed=options.ignore_installed, ignore_dependencies=options.ignore_dependencies, force_reinstall=options.force_reinstall, use_user_site=options.use_user_site) + upgrade = options.upgrade or options.upgrade_recursive for name in args: requirement_set.add_requirement( - InstallRequirement.from_line(name, None)) + InstallRequirement.from_line(name, None, upgrade=upgrade)) for name in options.editables: requirement_set.add_requirement( - InstallRequirement.from_editable(name, default_vcs=options.default_vcs)) + InstallRequirement.from_editable(name, default_vcs=options.default_vcs, upgrade=upgrade)) for filename in options.requirements: for req in parse_requirements(filename, finder=finder, options=options): requirement_set.add_requirement(req) diff --git a/pip/req.py b/pip/req.py index 6571b17bbb5..da1c29e50f8 100644 --- a/pip/req.py +++ b/pip/req.py @@ -36,7 +36,7 @@ class InstallRequirement(object): def __init__(self, req, comes_from, source_dir=None, editable=False, - url=None, as_egg=False, update=True): + url=None, as_egg=False, update=True, upgrade=False): self.extras = () if isinstance(req, string_types): req = pkg_resources.Requirement.parse(req) @@ -58,6 +58,8 @@ def __init__(self, req, comes_from, source_dir=None, editable=False, self._is_bundle = None # True if the editable should be updated: self.update = update + # True if the requirement should be upgraded to the latest version + self.upgrade = upgrade # Set to True after successful installation self.install_succeeded = None # UninstallPathSet of uninstalled distribution (for possible rollback) @@ -65,14 +67,15 @@ def __init__(self, req, comes_from, source_dir=None, editable=False, self.use_user_site = False @classmethod - def from_editable(cls, editable_req, comes_from=None, default_vcs=None): + def from_editable(cls, editable_req, comes_from=None, default_vcs=None, upgrade=False): name, url, extras_override = parse_editable(editable_req, default_vcs) if url.startswith('file:'): source_dir = url_to_path(url) else: source_dir = None - res = cls(name, comes_from, source_dir=source_dir, editable=True, url=url) + res = cls(name, comes_from, source_dir=source_dir, editable=True, + url=url, upgrade=upgrade) if extras_override is not None: res.extras = extras_override @@ -80,7 +83,7 @@ def from_editable(cls, editable_req, comes_from=None, default_vcs=None): return res @classmethod - def from_line(cls, name, comes_from=None): + def from_line(cls, name, comes_from=None, upgrade=False): """Creates an InstallRequirement from a name, which might be a requirement, directory containing 'setup.py', filename, or URL. """ @@ -114,7 +117,7 @@ def from_line(cls, name, comes_from=None): else: req = name - return cls(req, comes_from, url=url) + return cls(req, comes_from, url=url, upgrade=upgrade) def __str__(self): if self.req: @@ -706,7 +709,7 @@ def is_bundle(self): or os.path.exists(os.path.join(base, 'pyinstall-manifest.txt'))) return self._is_bundle - def bundle_requirements(self): + def bundle_requirements(self, upgrade=False): for dest_dir in self._bundle_editable_dirs: package = os.path.basename(dest_dir) ## FIXME: svnism: @@ -726,13 +729,11 @@ def bundle_requirements(self): else: url = None yield InstallRequirement( - package, self, editable=True, url=url, - update=False, source_dir=dest_dir) + package, self, editable=True, url=url, update=False, + source_dir=dest_dir, upgrade=upgrade) for dest_dir in self._bundle_build_dirs: package = os.path.basename(dest_dir) - yield InstallRequirement( - package, self, - source_dir=dest_dir) + yield InstallRequirement(package, self, source_dir=dest_dir, upgrade=upgrade) def move_bundle_files(self, dest_build_dir, dest_src_dir): base = self._temp_build_dir @@ -808,13 +809,15 @@ def __repr__(self): class RequirementSet(object): def __init__(self, build_dir, src_dir, download_dir, download_cache=None, - upgrade=False, ignore_installed=False, as_egg=False, - ignore_dependencies=False, force_reinstall=False, use_user_site=False): + ignore_installed=False, as_egg=False, force_reinstall=False, + ignore_dependencies=False, use_user_site=False, upgrade=False, + upgrade_recursive=False): self.build_dir = build_dir self.src_dir = src_dir self.download_dir = download_dir self.download_cache = download_cache - self.upgrade = upgrade + self.upgrade = upgrade or upgrade_recursive + self.upgrade_recursive = upgrade_recursive self.ignore_installed = ignore_installed self.force_reinstall = force_reinstall self.requirements = Requirements() @@ -908,15 +911,19 @@ def locate_files(self): if not self.ignore_installed and not req_to_install.editable: req_to_install.check_if_exists() if req_to_install.satisfied_by: - if self.upgrade: + if req_to_install.upgrade: req_to_install.conflicts_with = req_to_install.satisfied_by req_to_install.satisfied_by = None else: install_needed = False if req_to_install.satisfied_by: + if self.upgrade and not self.upgrade_recursive: + upgrade_cmd = '--upgrade-recursive' + else: + upgrade_cmd = '--upgrade' logger.notify('Requirement already satisfied ' - '(use --upgrade to upgrade): %s' - % req_to_install) + '(use %s to upgrade): %s' + % (upgrade_cmd, req_to_install)) if req_to_install.editable: if req_to_install.source_dir is None: @@ -946,11 +953,11 @@ def prepare_files(self, finder, force_root_egg_info=False, bundle=False): if not self.ignore_installed and not req_to_install.editable: req_to_install.check_if_exists() if req_to_install.satisfied_by: - if self.upgrade: + if req_to_install.upgrade: if not self.force_reinstall and not req_to_install.url: try: url = finder.find_requirement( - req_to_install, self.upgrade) + req_to_install, upgrade=True) except BestVersionAlreadyInstalled: best_installed = True install = False @@ -970,9 +977,13 @@ def prepare_files(self, finder, force_root_egg_info=False, bundle=False): logger.notify('Requirement already up-to-date: %s' % req_to_install) else: + if self.upgrade and not self.upgrade_recursive: + upgrade_cmd = '--upgrade-recursive' + else: + upgrade_cmd = '--upgrade' logger.notify('Requirement already satisfied ' - '(use --upgrade to upgrade): %s' - % req_to_install) + '(use %s to upgrade): %s' + % (upgrade_cmd, req_to_install)) if req_to_install.editable: logger.notify('Obtaining %s' % req_to_install) elif install: @@ -1012,7 +1023,7 @@ def prepare_files(self, finder, force_root_egg_info=False, bundle=False): if req_to_install.url is None: if not_found: raise not_found - url = finder.find_requirement(req_to_install, upgrade=self.upgrade) + url = finder.find_requirement(req_to_install, upgrade=req_to_install.upgrade) else: ## FIXME: should req_to_install.url already be a link? url = Link(req_to_install.url) @@ -1033,7 +1044,7 @@ def prepare_files(self, finder, force_root_egg_info=False, bundle=False): is_bundle = req_to_install.is_bundle if is_bundle: req_to_install.move_bundle_files(self.build_dir, self.src_dir) - for subreq in req_to_install.bundle_requirements(): + for subreq in req_to_install.bundle_requirements(upgrade=self.upgrade_recursive): reqs.append(subreq) self.add_requirement(subreq) elif self.is_download: @@ -1057,7 +1068,7 @@ def prepare_files(self, finder, force_root_egg_info=False, bundle=False): # repeat check_if_exists to uninstall-on-upgrade (#14) req_to_install.check_if_exists() if req_to_install.satisfied_by: - if self.upgrade or self.ignore_installed: + if req_to_install.upgrade or self.ignore_installed: req_to_install.conflicts_with = req_to_install.satisfied_by req_to_install.satisfied_by = None else: @@ -1079,7 +1090,7 @@ def prepare_files(self, finder, force_root_egg_info=False, bundle=False): if self.has_requirement(name): ## FIXME: check for conflict continue - subreq = InstallRequirement(req, req_to_install) + subreq = InstallRequirement(req, req_to_install, upgrade=self.upgrade_recursive) reqs.append(subreq) self.add_requirement(subreq) if req_to_install.name not in self.requirements: @@ -1322,16 +1333,21 @@ def parse_requirements(filename, finder=None, comes_from=None, options=None): if finder: finder.index_urls.append(line) else: + # Use getattr since uninstall doesn't set these options + upgrade = (getattr(options, 'upgrade', False) + or getattr(options, 'upgrade_recursive', False)) comes_from = '-r %s (line %s)' % (filename, line_number) if line.startswith('-e') or line.startswith('--editable'): if line.startswith('-e'): line = line[2:].strip() else: line = line[len('--editable'):].strip().lstrip('=') - req = InstallRequirement.from_editable( - line, comes_from=comes_from, default_vcs=options.default_vcs) + req = InstallRequirement.from_editable(line, + comes_from=comes_from, + default_vcs=options.default_vcs, + upgrade=upgrade) else: - req = InstallRequirement.from_line(line, comes_from) + req = InstallRequirement.from_line(line, comes_from, upgrade=upgrade) yield req