diff --git a/piptools/_compat/__init__.py b/piptools/_compat/__init__.py index fda80d571..f67f0949a 100644 --- a/piptools/_compat/__init__.py +++ b/piptools/_compat/__init__.py @@ -4,7 +4,7 @@ import six -from .pip_compat import PIP_VERSION, parse_requirements +from .pip_compat import BAR_TYPES, PIP_VERSION, parse_requirements if six.PY2: from .tempfile import TemporaryDirectory diff --git a/piptools/_compat/pip_compat.py b/piptools/_compat/pip_compat.py index 9508b7546..543593ad9 100644 --- a/piptools/_compat/pip_compat.py +++ b/piptools/_compat/pip_compat.py @@ -13,9 +13,11 @@ def install_req_from_parsed_requirement(req, **kwargs): return req + from pip._internal.utils.ui import BAR_TYPES else: from pip._internal.req.constructors import install_req_from_parsed_requirement + from pip._internal.cli.progress_bars import BAR_TYPES def parse_requirements( diff --git a/piptools/logging.py b/piptools/logging.py index eea6772af..dcf068f7a 100644 --- a/piptools/logging.py +++ b/piptools/logging.py @@ -3,6 +3,7 @@ import contextlib import logging +import sys from . import click @@ -12,6 +13,8 @@ class LogContext(object): + stream = sys.stderr + def __init__(self, verbosity=0, indent_width=2): self.verbosity = verbosity self.current_indent = 0 diff --git a/piptools/repositories/pypi.py b/piptools/repositories/pypi.py index 7f6975ccc..9c4bff01b 100644 --- a/piptools/repositories/pypi.py +++ b/piptools/repositories/pypi.py @@ -3,6 +3,8 @@ import collections import hashlib +import itertools +import logging import os from contextlib import contextmanager from shutil import rmtree @@ -15,12 +17,13 @@ from pip._internal.req import RequirementSet from pip._internal.req.req_tracker import get_requirement_tracker from pip._internal.utils.hashes import FAVORITE_HASH +from pip._internal.utils.logging import indent_log, setup_logging from pip._internal.utils.misc import normalize_path from pip._internal.utils.temp_dir import TempDirectory, global_tempdir_manager from pip._internal.utils.urls import path_to_url, url_to_path from pip._vendor.requests import RequestException -from .._compat import PIP_VERSION, TemporaryDirectory, contextlib +from .._compat import BAR_TYPES, PIP_VERSION, TemporaryDirectory, contextlib from ..click import progressbar from ..exceptions import NoCandidateFound from ..logging import log @@ -82,6 +85,8 @@ def __init__(self, pip_args, cache_dir): self._download_dir = fs_str(os.path.join(self._cache_dir, "pkgs")) self._wheel_download_dir = fs_str(os.path.join(self._cache_dir, "wheels")) + self._setup_logging() + def freshen_build_caches(self): """ Start with fresh build/source caches. Will remove any old build @@ -144,7 +149,7 @@ def find_best_match(self, ireq, prereleases=None): def resolve_reqs(self, download_dir, ireq, wheel_cache): with get_requirement_tracker() as req_tracker, TempDirectory( kind="resolver" - ) as temp_dir: + ) as temp_dir, indent_log(): preparer = self.command.make_requirement_preparer( temp_build_dir=temp_dir, options=self.options, @@ -368,11 +373,17 @@ def _get_file_hash(self, link): # Choose a context manager depending on verbosity if log.verbosity >= 1: iter_length = f.size / FILE_CHUNK_SIZE if f.size else None - bar_template = "{prefix}[%(bar)s] %(info)s".format( + bar_template = "{prefix} |%(bar)s| %(info)s".format( prefix=" " * log.current_indent ) context_manager = progressbar( - chunks, length=iter_length, bar_template=bar_template + chunks, + length=iter_length, + # Make it look like default pip progress bar + fill_char="█", + empty_char=" ", + bar_template=bar_template, + width=32, ) else: context_manager = contextlib.nullcontext(chunks) @@ -415,6 +426,35 @@ def _wheel_support_index_min(self, tags=None): Wheel.support_index_min = original_support_index_min self._available_candidates_cache = original_cache + def _setup_logging(self): + """ + Setup pip's logger. Ensure pip is verbose same as pip-tools and sync + pip's log stream with LogContext.stream. + """ + # Default pip's logger is noisy, so decrease it's verbosity + setup_logging( + verbosity=log.verbosity - 1, + no_color=self.options.no_color, + user_log_file=self.options.log, + ) + + # Sync pip's console handler stream with LogContext.stream + logger = logging.getLogger() + console_handlers = [ + handler for handler in logger.handlers if handler.name == "console" + ] + if console_handlers: # pragma: no branch + console_handlers[0].stream = log.stream + else: # pragma: no cover + # There is always a console handler. This warning would be a signal that + # this block should be removed/revisited, because of pip possibly + # refactored-out logging config. + log.warning("Couldn't find a 'console' logging handler") + + # Sync pip's progress bars stream with LogContext.stream + for bar_cls in itertools.chain(*BAR_TYPES.values()): + bar_cls.file = log.stream + @contextmanager def open_local_or_remote_file(link, session):