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) |