Mercurial > repos > bcclaywell > argo_navis
comparison venv/lib/python2.7/site-packages/pip/utils/ui.py @ 0:d67268158946 draft
planemo upload commit a3f181f5f126803c654b3a66dd4e83a48f7e203b
| author | bcclaywell |
|---|---|
| date | Mon, 12 Oct 2015 17:43:33 -0400 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:d67268158946 |
|---|---|
| 1 from __future__ import absolute_import | |
| 2 from __future__ import division | |
| 3 | |
| 4 import itertools | |
| 5 import sys | |
| 6 from signal import signal, SIGINT, default_int_handler | |
| 7 | |
| 8 from pip.compat import WINDOWS | |
| 9 from pip.utils import format_size | |
| 10 from pip.utils.logging import get_indentation | |
| 11 from pip._vendor import six | |
| 12 from pip._vendor.progress.bar import Bar, IncrementalBar | |
| 13 from pip._vendor.progress.helpers import WritelnMixin | |
| 14 from pip._vendor.progress.spinner import Spinner | |
| 15 | |
| 16 try: | |
| 17 from pip._vendor import colorama | |
| 18 # Lots of different errors can come from this, including SystemError and | |
| 19 # ImportError. | |
| 20 except Exception: | |
| 21 colorama = None | |
| 22 | |
| 23 | |
| 24 def _select_progress_class(preferred, fallback): | |
| 25 encoding = getattr(preferred.file, "encoding", None) | |
| 26 | |
| 27 # If we don't know what encoding this file is in, then we'll just assume | |
| 28 # that it doesn't support unicode and use the ASCII bar. | |
| 29 if not encoding: | |
| 30 return fallback | |
| 31 | |
| 32 # Collect all of the possible characters we want to use with the preferred | |
| 33 # bar. | |
| 34 characters = [ | |
| 35 getattr(preferred, "empty_fill", six.text_type()), | |
| 36 getattr(preferred, "fill", six.text_type()), | |
| 37 ] | |
| 38 characters += list(getattr(preferred, "phases", [])) | |
| 39 | |
| 40 # Try to decode the characters we're using for the bar using the encoding | |
| 41 # of the given file, if this works then we'll assume that we can use the | |
| 42 # fancier bar and if not we'll fall back to the plaintext bar. | |
| 43 try: | |
| 44 six.text_type().join(characters).encode(encoding) | |
| 45 except UnicodeEncodeError: | |
| 46 return fallback | |
| 47 else: | |
| 48 return preferred | |
| 49 | |
| 50 | |
| 51 _BaseBar = _select_progress_class(IncrementalBar, Bar) | |
| 52 | |
| 53 | |
| 54 class InterruptibleMixin(object): | |
| 55 """ | |
| 56 Helper to ensure that self.finish() gets called on keyboard interrupt. | |
| 57 | |
| 58 This allows downloads to be interrupted without leaving temporary state | |
| 59 (like hidden cursors) behind. | |
| 60 | |
| 61 This class is similar to the progress library's existing SigIntMixin | |
| 62 helper, but as of version 1.2, that helper has the following problems: | |
| 63 | |
| 64 1. It calls sys.exit(). | |
| 65 2. It discards the existing SIGINT handler completely. | |
| 66 3. It leaves its own handler in place even after an uninterrupted finish, | |
| 67 which will have unexpected delayed effects if the user triggers an | |
| 68 unrelated keyboard interrupt some time after a progress-displaying | |
| 69 download has already completed, for example. | |
| 70 """ | |
| 71 | |
| 72 def __init__(self, *args, **kwargs): | |
| 73 """ | |
| 74 Save the original SIGINT handler for later. | |
| 75 """ | |
| 76 super(InterruptibleMixin, self).__init__(*args, **kwargs) | |
| 77 | |
| 78 self.original_handler = signal(SIGINT, self.handle_sigint) | |
| 79 | |
| 80 # If signal() returns None, the previous handler was not installed from | |
| 81 # Python, and we cannot restore it. This probably should not happen, | |
| 82 # but if it does, we must restore something sensible instead, at least. | |
| 83 # The least bad option should be Python's default SIGINT handler, which | |
| 84 # just raises KeyboardInterrupt. | |
| 85 if self.original_handler is None: | |
| 86 self.original_handler = default_int_handler | |
| 87 | |
| 88 def finish(self): | |
| 89 """ | |
| 90 Restore the original SIGINT handler after finishing. | |
| 91 | |
| 92 This should happen regardless of whether the progress display finishes | |
| 93 normally, or gets interrupted. | |
| 94 """ | |
| 95 super(InterruptibleMixin, self).finish() | |
| 96 signal(SIGINT, self.original_handler) | |
| 97 | |
| 98 def handle_sigint(self, signum, frame): | |
| 99 """ | |
| 100 Call self.finish() before delegating to the original SIGINT handler. | |
| 101 | |
| 102 This handler should only be in place while the progress display is | |
| 103 active. | |
| 104 """ | |
| 105 self.finish() | |
| 106 self.original_handler(signum, frame) | |
| 107 | |
| 108 | |
| 109 class DownloadProgressMixin(object): | |
| 110 | |
| 111 def __init__(self, *args, **kwargs): | |
| 112 super(DownloadProgressMixin, self).__init__(*args, **kwargs) | |
| 113 self.message = (" " * (get_indentation() + 2)) + self.message | |
| 114 | |
| 115 @property | |
| 116 def downloaded(self): | |
| 117 return format_size(self.index) | |
| 118 | |
| 119 @property | |
| 120 def download_speed(self): | |
| 121 # Avoid zero division errors... | |
| 122 if self.avg == 0.0: | |
| 123 return "..." | |
| 124 return format_size(1 / self.avg) + "/s" | |
| 125 | |
| 126 @property | |
| 127 def pretty_eta(self): | |
| 128 if self.eta: | |
| 129 return "eta %s" % self.eta_td | |
| 130 return "" | |
| 131 | |
| 132 def iter(self, it, n=1): | |
| 133 for x in it: | |
| 134 yield x | |
| 135 self.next(n) | |
| 136 self.finish() | |
| 137 | |
| 138 | |
| 139 class WindowsMixin(object): | |
| 140 | |
| 141 def __init__(self, *args, **kwargs): | |
| 142 # The Windows terminal does not support the hide/show cursor ANSI codes | |
| 143 # even with colorama. So we'll ensure that hide_cursor is False on | |
| 144 # Windows. | |
| 145 # This call neds to go before the super() call, so that hide_cursor | |
| 146 # is set in time. The base progress bar class writes the "hide cursor" | |
| 147 # code to the terminal in its init, so if we don't set this soon | |
| 148 # enough, we get a "hide" with no corresponding "show"... | |
| 149 if WINDOWS and self.hide_cursor: | |
| 150 self.hide_cursor = False | |
| 151 | |
| 152 super(WindowsMixin, self).__init__(*args, **kwargs) | |
| 153 | |
| 154 # Check if we are running on Windows and we have the colorama module, | |
| 155 # if we do then wrap our file with it. | |
| 156 if WINDOWS and colorama: | |
| 157 self.file = colorama.AnsiToWin32(self.file) | |
| 158 # The progress code expects to be able to call self.file.isatty() | |
| 159 # but the colorama.AnsiToWin32() object doesn't have that, so we'll | |
| 160 # add it. | |
| 161 self.file.isatty = lambda: self.file.wrapped.isatty() | |
| 162 # The progress code expects to be able to call self.file.flush() | |
| 163 # but the colorama.AnsiToWin32() object doesn't have that, so we'll | |
| 164 # add it. | |
| 165 self.file.flush = lambda: self.file.wrapped.flush() | |
| 166 | |
| 167 | |
| 168 class DownloadProgressBar(WindowsMixin, InterruptibleMixin, | |
| 169 DownloadProgressMixin, _BaseBar): | |
| 170 | |
| 171 file = sys.stdout | |
| 172 message = "%(percent)d%%" | |
| 173 suffix = "%(downloaded)s %(download_speed)s %(pretty_eta)s" | |
| 174 | |
| 175 | |
| 176 class DownloadProgressSpinner(WindowsMixin, InterruptibleMixin, | |
| 177 DownloadProgressMixin, WritelnMixin, Spinner): | |
| 178 | |
| 179 file = sys.stdout | |
| 180 suffix = "%(downloaded)s %(download_speed)s" | |
| 181 | |
| 182 def next_phase(self): | |
| 183 if not hasattr(self, "_phaser"): | |
| 184 self._phaser = itertools.cycle(self.phases) | |
| 185 return next(self._phaser) | |
| 186 | |
| 187 def update(self): | |
| 188 message = self.message % self | |
| 189 phase = self.next_phase() | |
| 190 suffix = self.suffix % self | |
| 191 line = ''.join([ | |
| 192 message, | |
| 193 " " if message else "", | |
| 194 phase, | |
| 195 " " if suffix else "", | |
| 196 suffix, | |
| 197 ]) | |
| 198 | |
| 199 self.writeln(line) |
