84
82
# end-of-file; possibly should raise an error here instead
87
def get_integer(self, prompt):
90
line = self.stdin.readline()
96
85
def get_non_echoed_password(self):
97
86
isatty = getattr(self.stdin, 'isatty', None)
98
87
if isatty is not None and isatty():
158
147
return NullProgressView()
160
def _make_output_stream_explicit(self, encoding, encoding_type):
161
if encoding_type == 'exact':
162
# force sys.stdout to be binary stream on win32;
163
# NB: this leaves the file set in that mode; may cause problems if
164
# one process tries to do binary and then text output
165
if sys.platform == 'win32':
166
fileno = getattr(self.stdout, 'fileno', None)
169
msvcrt.setmode(fileno(), os.O_BINARY)
170
return TextUIOutputStream(self, self.stdout)
172
encoded_stdout = codecs.getwriter(encoding)(self.stdout,
173
errors=encoding_type)
174
# For whatever reason codecs.getwriter() does not advertise its encoding
175
# it just returns the encoding of the wrapped file, which is completely
176
# bogus. So set the attribute, so we can find the correct encoding later.
177
encoded_stdout.encoding = encoding
178
return TextUIOutputStream(self, encoded_stdout)
180
149
def note(self, msg):
181
150
"""Write an already-formatted message, clearing the progress bar if necessary."""
182
151
self.clear_term()
204
173
self._progress_view.show_transport_activity(transport,
205
174
direction, byte_count)
207
def log_transport_activity(self, display=False):
208
"""See UIFactory.log_transport_activity()"""
209
log = getattr(self._progress_view, 'log_transport_activity', None)
213
176
def show_error(self, msg):
214
177
self.clear_term()
215
178
self.stderr.write("bzr: error: %s\n" % msg)
255
218
self._term_file = term_file
256
219
# true when there's output on the screen we may need to clear
257
220
self._have_output = False
221
# XXX: We could listen for SIGWINCH and update the terminal width...
222
# https://launchpad.net/bugs/316357
223
self._width = osutils.terminal_width()
258
224
self._last_transport_msg = ''
259
225
self._spin_pos = 0
260
226
# time we last repainted the screen
264
230
self._last_task = None
265
231
self._total_byte_count = 0
266
232
self._bytes_since_update = 0
267
self._bytes_by_direction = {'unknown': 0, 'read': 0, 'write': 0}
268
self._first_byte_time = None
269
233
self._fraction = 0
270
# force the progress bar to be off, as at the moment it doesn't
271
# correspond reliably to overall command progress
272
self.enable_bar = False
274
235
def _show_line(self, s):
275
236
# sys.stderr.write("progress %r\n" % s)
276
width = osutils.terminal_width()
277
if width is not None:
278
# we need one extra space for terminals that wrap on last char
280
s = '%-*.*s' % (width, width, s)
281
self._term_file.write('\r' + s + '\r')
238
self._term_file.write('\r%-*.*s\r' % (n, n, s))
284
241
if self._have_output:
288
245
def _render_bar(self):
289
246
# return a string for the progress bar itself
290
if self.enable_bar and (
291
(self._last_task is None) or self._last_task.show_bar):
247
if (self._last_task is None) or self._last_task.show_bar:
292
248
# If there's no task object, we show space for the bar anyhow.
293
249
# That's because most invocations of bzr will end showing progress
294
250
# at some point, though perhaps only after doing some initial IO.
310
266
markers = int(round(float(cols) * completion_fraction)) - 1
311
267
bar_str = '[' + ('#' * markers + spin_str).ljust(cols) + '] '
313
elif (self._last_task is None) or self._last_task.show_spinner:
269
elif self._last_task.show_spinner:
314
270
# The last task wanted just a spinner, no bar
315
271
spin_str = r'/-\|'[self._spin_pos % 4]
316
272
self._spin_pos += 1
378
334
This may update a progress bar, spinner, or similar display.
379
335
By default it does nothing.
381
# XXX: there should be a transport activity model, and that too should
382
# be seen by the progress view, rather than being poked in here.
337
# XXX: Probably there should be a transport activity model, and that
338
# too should be seen by the progress view, rather than being poked in
340
if not self._have_output:
341
# As a workaround for <https://launchpad.net/bugs/321935> we only
342
# show transport activity when there's already a progress bar
343
# shown, which time the application code is expected to know to
344
# clear off the progress bar when it's going to send some other
345
# output. Eventually it would be nice to have that automatically
383
348
self._total_byte_count += byte_count
384
349
self._bytes_since_update += byte_count
385
if self._first_byte_time is None:
386
# Note that this isn't great, as technically it should be the time
387
# when the bytes started transferring, not when they completed.
388
# However, we usually start with a small request anyway.
389
self._first_byte_time = time.time()
390
if direction in self._bytes_by_direction:
391
self._bytes_by_direction[direction] += byte_count
393
self._bytes_by_direction['unknown'] += byte_count
394
if 'no_activity' in debug.debug_flags:
395
# Can be used as a workaround if
396
# <https://launchpad.net/bugs/321935> reappears and transport
397
# activity is cluttering other output. However, thanks to
398
# TextUIOutputStream this shouldn't be a problem any more.
400
350
now = time.time()
401
351
if self._total_byte_count < 2000:
402
352
# a little resistance at first, so it doesn't stay stuck at 0
415
365
self._bytes_since_update = 0
416
366
self._last_transport_msg = msg
419
def _format_bytes_by_direction(self):
420
if self._first_byte_time is None:
423
transfer_time = time.time() - self._first_byte_time
424
if transfer_time < 0.001:
425
transfer_time = 0.001
426
bps = self._total_byte_count / transfer_time
428
msg = ('Transferred: %.0fKiB'
429
' (%.1fK/s r:%.0fK w:%.0fK'
430
% (self._total_byte_count / 1024.,
432
self._bytes_by_direction['read'] / 1024.,
433
self._bytes_by_direction['write'] / 1024.,
435
if self._bytes_by_direction['unknown'] > 0:
436
msg += ' u:%.0fK)' % (
437
self._bytes_by_direction['unknown'] / 1024.
443
def log_transport_activity(self, display=False):
444
msg = self._format_bytes_by_direction()
446
if display and self._total_byte_count > 0:
448
self._term_file.write(msg + '\n')
451
class TextUIOutputStream(object):
452
"""Decorates an output stream so that the terminal is cleared before writing.
454
This is supposed to ensure that the progress bar does not conflict with bulk
457
# XXX: this does not handle the case of writing part of a line, then doing
458
# progress bar output: the progress bar will probably write over it.
459
# one option is just to buffer that text until we have a full line;
460
# another is to save and restore it
462
# XXX: might need to wrap more methods
464
def __init__(self, ui_factory, wrapped_stream):
465
self.ui_factory = ui_factory
466
self.wrapped_stream = wrapped_stream
467
# this does no transcoding, but it must expose the underlying encoding
468
# because some callers need to know what can be written - see for
469
# example unescape_for_display.
470
self.encoding = getattr(wrapped_stream, 'encoding', None)
473
self.ui_factory.clear_term()
474
self.wrapped_stream.flush()
476
def write(self, to_write):
477
self.ui_factory.clear_term()
478
self.wrapped_stream.write(to_write)
480
def writelines(self, lines):
481
self.ui_factory.clear_term()
482
self.wrapped_stream.writelines(lines)