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)