82
84
# end-of-file; possibly should raise an error here instead
87
def get_integer(self, prompt):
90
line = self.stdin.readline()
85
96
def get_non_echoed_password(self):
86
97
isatty = getattr(self.stdin, 'isatty', None)
87
98
if isatty is not None and isatty():
147
158
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)
149
180
def note(self, msg):
150
181
"""Write an already-formatted message, clearing the progress bar if necessary."""
151
182
self.clear_term()
173
204
self._progress_view.show_transport_activity(transport,
174
205
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)
176
213
def show_error(self, msg):
177
214
self.clear_term()
178
215
self.stderr.write("bzr: error: %s\n" % msg)
218
255
self._term_file = term_file
219
256
# true when there's output on the screen we may need to clear
220
257
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()
224
258
self._last_transport_msg = ''
225
259
self._spin_pos = 0
226
260
# time we last repainted the screen
230
264
self._last_task = None
231
265
self._total_byte_count = 0
232
266
self._bytes_since_update = 0
267
self._bytes_by_direction = {'unknown': 0, 'read': 0, 'write': 0}
268
self._first_byte_time = None
233
269
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
235
274
def _show_line(self, s):
236
275
# sys.stderr.write("progress %r\n" % s)
237
if self._width is not None:
239
s = '%-*.*s' % (n, 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)
240
281
self._term_file.write('\r' + s + '\r')
247
288
def _render_bar(self):
248
289
# return a string for the progress bar itself
249
if (self._last_task is None) or self._last_task.show_bar:
290
if self.enable_bar and (
291
(self._last_task is None) or self._last_task.show_bar):
250
292
# If there's no task object, we show space for the bar anyhow.
251
293
# That's because most invocations of bzr will end showing progress
252
294
# at some point, though perhaps only after doing some initial IO.
268
310
markers = int(round(float(cols) * completion_fraction)) - 1
269
311
bar_str = '[' + ('#' * markers + spin_str).ljust(cols) + '] '
271
elif self._last_task.show_spinner:
313
elif (self._last_task is None) or self._last_task.show_spinner:
272
314
# The last task wanted just a spinner, no bar
273
315
spin_str = r'/-\|'[self._spin_pos % 4]
274
316
self._spin_pos += 1
336
378
This may update a progress bar, spinner, or similar display.
337
379
By default it does nothing.
339
# XXX: Probably there should be a transport activity model, and that
340
# too should be seen by the progress view, rather than being poked in
342
if not self._have_output:
343
# As a workaround for <https://launchpad.net/bugs/321935> we only
344
# show transport activity when there's already a progress bar
345
# shown, which time the application code is expected to know to
346
# clear off the progress bar when it's going to send some other
347
# output. Eventually it would be nice to have that automatically
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.
350
383
self._total_byte_count += byte_count
351
384
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.
352
400
now = time.time()
353
401
if self._total_byte_count < 2000:
354
402
# a little resistance at first, so it doesn't stay stuck at 0
367
415
self._bytes_since_update = 0
368
416
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)