From 0552ffeeda62759797cfdccaba8a3fa0cf816199 Mon Sep 17 00:00:00 2001 From: Avner Cohen Date: Thu, 22 Dec 2016 16:59:55 +0200 Subject: [PATCH] Add `--no-progress ` to ``pip download`` and ``pip install`` to suppress progress bar in the console --- CHANGES.txt | 4 +++ pip/_vendor/progress/bar.py | 12 ++++++++ pip/cmdoptions.py | 12 ++++++++ pip/commands/download.py | 4 ++- pip/commands/install.py | 2 ++ pip/commands/wheel.py | 4 ++- pip/download.py | 24 +++++++++------- pip/req/req_set.py | 6 ++-- pip/utils/ui.py | 57 +++++++++++++++++++++++++++++++++++-- 9 files changed, 108 insertions(+), 17 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 3b16a494711..2cba17e2e03 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -10,6 +10,10 @@ * Add `--exclude-editable` to ``pip freeze`` to exclude editable packages from installed package list. +* Add `--progress-bar ` to ``pip download``, ``pip install`` + and ``pip wheel`` to suppress progress bar in the console (:pull:`4194`) + + **9.0.1 (2016-11-06)** * Correct the deprecation message when not specifying a --format so that it diff --git a/pip/_vendor/progress/bar.py b/pip/_vendor/progress/bar.py index 8ce14610046..b6e5263afd4 100644 --- a/pip/_vendor/progress/bar.py +++ b/pip/_vendor/progress/bar.py @@ -41,6 +41,11 @@ def update(self): self.writeln(line) +class SilentBar(Bar): + def update(self): + pass + + class ChargingBar(Bar): suffix = '%(percent)d%%' bar_prefix = ' ' @@ -81,3 +86,10 @@ def update(self): class ShadyBar(IncrementalBar): phases = (u' ', u'░', u'▒', u'▓', u'█') + + +class BlueEmojiBar(IncrementalBar): + suffix = '%(percent)d%%' + bar_prefix = ' ' + bar_suffix = ' ' + phases = (u"\U0001F539", u"\U0001F537", u"\U0001F535") diff --git a/pip/cmdoptions.py b/pip/cmdoptions.py index f71488cdf51..d420ed2fd16 100644 --- a/pip/cmdoptions.py +++ b/pip/cmdoptions.py @@ -19,6 +19,7 @@ from pip.models import PyPI from pip.locations import USER_CACHE_DIR, src_prefix from pip.utils.hashes import STRONG_HASHES +from pip.utils.ui import BAR_TYPES def make_option_group(group, parser): @@ -119,6 +120,16 @@ def getname(n): ' levels).') ) +progress_bar = partial( + Option, + '--progress-bar', + dest='progress_bar', + type='choice', + choices=list(BAR_TYPES.keys()), + default='on', + help='Specify type of progress to be displayed [' + + '|'.join(BAR_TYPES.keys()) + '] (default: %default)') + log = partial( Option, "--log", "--log-file", "--local-log", @@ -529,6 +540,7 @@ def only_binary(): help="Don't periodically check PyPI to determine whether a new version " "of pip is available for download. Implied with --no-index.") + # Deprecated, Remove later always_unzip = partial( Option, diff --git a/pip/commands/download.py b/pip/commands/download.py index 5b887bbec18..7fe7ef05900 100644 --- a/pip/commands/download.py +++ b/pip/commands/download.py @@ -56,6 +56,7 @@ def __init__(self, *args, **kw): cmd_opts.add_option(cmdoptions.pre()) cmd_opts.add_option(cmdoptions.no_clean()) cmd_opts.add_option(cmdoptions.require_hashes()) + cmd_opts.add_option(cmdoptions.progress_bar()) cmd_opts.add_option( '-d', '--dest', '--destination-dir', '--destination-directory', @@ -180,7 +181,8 @@ def run(self, options, args): ignore_dependencies=options.ignore_dependencies, session=session, isolated=options.isolated_mode, - require_hashes=options.require_hashes + require_hashes=options.require_hashes, + progress_bar=options.progress_bar ) self.populate_requirement_set( requirement_set, diff --git a/pip/commands/install.py b/pip/commands/install.py index aecbeb3a83b..b7e662bd84f 100644 --- a/pip/commands/install.py +++ b/pip/commands/install.py @@ -178,6 +178,7 @@ def __init__(self, *args, **kw): cmd_opts.add_option(cmdoptions.only_binary()) cmd_opts.add_option(cmdoptions.no_clean()) cmd_opts.add_option(cmdoptions.require_hashes()) + cmd_opts.add_option(cmdoptions.progress_bar()) index_opts = cmdoptions.make_option_group( cmdoptions.index_group, @@ -303,6 +304,7 @@ def run(self, options, args): isolated=options.isolated_mode, wheel_cache=wheel_cache, require_hashes=options.require_hashes, + progress_bar=options.progress_bar, ) self.populate_requirement_set( diff --git a/pip/commands/wheel.py b/pip/commands/wheel.py index e61d0b3641c..18afde0a63b 100644 --- a/pip/commands/wheel.py +++ b/pip/commands/wheel.py @@ -73,6 +73,7 @@ def __init__(self, *args, **kw): cmd_opts.add_option(cmdoptions.ignore_requires_python()) cmd_opts.add_option(cmdoptions.no_deps()) cmd_opts.add_option(cmdoptions.build_dir()) + cmd_opts.add_option(cmdoptions.progress_bar()) cmd_opts.add_option( '--global-option', @@ -177,7 +178,8 @@ def run(self, options, args): session=session, wheel_cache=wheel_cache, wheel_download_dir=options.wheel_dir, - require_hashes=options.require_hashes + require_hashes=options.require_hashes, + progress_bar=options.progress_bar ) self.populate_requirement_set( diff --git a/pip/download.py b/pip/download.py index 8f5850b493e..10790b5a1cc 100644 --- a/pip/download.py +++ b/pip/download.py @@ -34,7 +34,7 @@ from pip.utils.logging import indent_log from pip.utils.setuptools_build import SETUPTOOLS_SHIM from pip.utils.glibc import libc_ver -from pip.utils.ui import DownloadProgressBar, DownloadProgressSpinner +from pip.utils.ui import DownloadProgressProvider from pip.locations import write_delete_marker_file from pip.vcs import vcs from pip._vendor import requests, six @@ -514,14 +514,13 @@ def _progress_indicator(iterable, *args, **kwargs): return iterable -def _download_url(resp, link, content_file, hashes): +def _download_url(resp, link, content_file, hashes, progress_bar): try: total_length = int(resp.headers['content-length']) except (ValueError, KeyError, TypeError): total_length = 0 cached_resp = getattr(resp, "from_cache", False) - if logger.getEffectiveLevel() > logging.INFO: show_progress = False elif cached_resp: @@ -585,12 +584,12 @@ def written_chunks(chunks): url = link.url_without_fragment if show_progress: # We don't show progress on cached responses + progress_indicator = DownloadProgressProvider(progress_bar, + max=total_length) if total_length: logger.info("Downloading %s (%s)", url, format_size(total_length)) - progress_indicator = DownloadProgressBar(max=total_length).iter else: logger.info("Downloading %s", url) - progress_indicator = DownloadProgressSpinner().iter elif cached_resp: logger.info("Using cached %s", url) else: @@ -638,7 +637,7 @@ def _copy_file(filename, location, link): def unpack_http_url(link, location, download_dir=None, - session=None, hashes=None): + session=None, hashes=None, progress_bar="on"): if session is None: raise TypeError( "unpack_http_url() missing 1 required keyword argument: 'session'" @@ -661,7 +660,8 @@ def unpack_http_url(link, location, download_dir=None, from_path, content_type = _download_http_url(link, session, temp_dir, - hashes) + hashes, + progress_bar) # unpack the archive to the build dir location. even when only downloading # archives, they have to be unpacked to parse dependencies @@ -790,7 +790,8 @@ def request(self, host, handler, request_body, verbose=False): def unpack_url(link, location, download_dir=None, - only_download=False, session=None, hashes=None): + only_download=False, session=None, hashes=None, + progress_bar="on"): """Unpack link. If link is a VCS link: if only_download, export into download_dir and ignore location @@ -823,13 +824,14 @@ def unpack_url(link, location, download_dir=None, location, download_dir, session, - hashes=hashes + hashes=hashes, + progress_bar=progress_bar ) if only_download: write_delete_marker_file(location) -def _download_http_url(link, session, temp_dir, hashes): +def _download_http_url(link, session, temp_dir, hashes, progress_bar): """Download link url into temp_dir using provided session""" target_url = link.url.split('#', 1)[0] try: @@ -884,7 +886,7 @@ def _download_http_url(link, session, temp_dir, hashes): filename += ext file_path = os.path.join(temp_dir, filename) with open(file_path, 'wb') as content_file: - _download_url(resp, link, content_file, hashes) + _download_url(resp, link, content_file, hashes, progress_bar) return file_path, content_type diff --git a/pip/req/req_set.py b/pip/req/req_set.py index 9ec4af995fc..1c5b394a713 100644 --- a/pip/req/req_set.py +++ b/pip/req/req_set.py @@ -147,7 +147,7 @@ def __init__(self, build_dir, src_dir, download_dir, upgrade=False, force_reinstall=False, use_user_site=False, session=None, pycompile=True, isolated=False, wheel_download_dir=None, wheel_cache=None, require_hashes=False, - ignore_requires_python=False): + ignore_requires_python=False, progress_bar="on"): """Create a RequirementSet. :param wheel_download_dir: Where still-packed .whl files should be @@ -182,6 +182,7 @@ def __init__(self, build_dir, src_dir, download_dir, upgrade=False, self.unnamed_requirements = [] self.ignore_dependencies = ignore_dependencies self.ignore_requires_python = ignore_requires_python + self.progress_bar = progress_bar self.successfully_downloaded = [] self.successfully_installed = [] self.reqs_to_cleanup = [] @@ -618,7 +619,8 @@ def _prepare_file(self, unpack_url( req_to_install.link, req_to_install.source_dir, download_dir, autodelete_unpacked, - session=self.session, hashes=hashes) + session=self.session, hashes=hashes, + progress_bar=self.progress_bar) except requests.HTTPError as exc: logger.critical( 'Could not install requirement %s because ' diff --git a/pip/utils/ui.py b/pip/utils/ui.py index bba73e3b133..9d7a35060cf 100644 --- a/pip/utils/ui.py +++ b/pip/utils/ui.py @@ -12,7 +12,12 @@ from pip.utils import format_size from pip.utils.logging import get_indentation from pip._vendor import six + from pip._vendor.progress.bar import Bar, IncrementalBar +from pip._vendor.progress.bar import FillingCirclesBar, FillingSquaresBar +from pip._vendor.progress.bar import ChargingBar, ShadyBar +from pip._vendor.progress.bar import BlueEmojiBar, SilentBar + from pip._vendor.progress.helpers import (WritelnMixin, HIDE_CURSOR, SHOW_CURSOR) from pip._vendor.progress.spinner import Spinner @@ -171,14 +176,46 @@ def __init__(self, *args, **kwargs): self.file.flush = lambda: self.file.wrapped.flush() -class DownloadProgressBar(WindowsMixin, InterruptibleMixin, - DownloadProgressMixin, _BaseBar): +class BaseDownloadProgressBar(WindowsMixin, InterruptibleMixin, + DownloadProgressMixin): file = sys.stdout message = "%(percent)d%%" suffix = "%(downloaded)s %(download_speed)s %(pretty_eta)s" +class DefaultDownloadProgressBar(BaseDownloadProgressBar, _BaseBar): + pass + + +class DownloadSilentBar(BaseDownloadProgressBar, SilentBar): + pass + + +class DownloadIncrementalBar(BaseDownloadProgressBar, IncrementalBar): + pass + + +class DownloadChargingBar(BaseDownloadProgressBar, ChargingBar): + pass + + +class DownloadShadyBar(BaseDownloadProgressBar, ShadyBar): + pass + + +class DownloadFillingSquaresBar(BaseDownloadProgressBar, FillingSquaresBar): + pass + + +class DownloadFillingCirclesBar(BaseDownloadProgressBar, FillingCirclesBar): + pass + + +class DownloadBlueEmojiProgressBar(BaseDownloadProgressBar, BlueEmojiBar): + pass + + class DownloadProgressSpinner(WindowsMixin, InterruptibleMixin, DownloadProgressMixin, WritelnMixin, Spinner): @@ -205,6 +242,22 @@ def update(self): self.writeln(line) +BAR_TYPES = { + "off": (DownloadSilentBar, DownloadSilentBar), + "on": (DefaultDownloadProgressBar, DownloadProgressSpinner), + "ascii": (DownloadIncrementalBar, DownloadProgressSpinner), + "pretty": (DownloadFillingCirclesBar, DownloadProgressSpinner), + "emoji": (DownloadBlueEmojiProgressBar, DownloadProgressSpinner) +} + + +def DownloadProgressProvider(progress_bar, max=None): + if max is None or max == 0: + return BAR_TYPES[progress_bar][1]().iter + else: + return BAR_TYPES[progress_bar][0](max=max).iter + + ################################################################ # Generic "something is happening" spinners #