1
1
# Copyright (C) 2005 Aaron Bentley <aaron.bentley@utoronto.ca>
2
# Copyright (C) 2005 Canonical <canonical.com>
2
# Copyright (C) 2005, 2006 Canonical <canonical.com>
4
4
# This program is free software; you can redistribute it and/or modify
5
5
# it under the terms of the GNU General Public License as published by
42
42
from collections import deque
45
import bzrlib.errors as errors
46
from bzrlib.trace import mutter
45
49
def _supports_progress(f):
46
50
if not hasattr(f, 'isatty'):
62
66
return DotsProgressBar(to_file=to_file, **kwargs)
69
class ProgressBarStack(object):
70
"""A stack of progress bars."""
79
to_messages_file=sys.stdout,
81
"""Setup the stack with the parameters the progress bars should have."""
82
self._to_file = to_file
83
self._show_pct = show_pct
84
self._show_spinner = show_spinner
85
self._show_eta = show_eta
86
self._show_bar = show_bar
87
self._show_count = show_count
88
self._to_messages_file = to_messages_file
90
self._klass = klass or TTYProgressBar
93
if len(self._stack) != 0:
94
return self._stack[-1]
99
"""Return a nested progress bar."""
100
if len(self._stack) == 0:
103
func = self.top().child_progress
104
new_bar = func(to_file=self._to_file,
105
show_pct=self._show_pct,
106
show_spinner=self._show_spinner,
107
show_eta=self._show_eta,
108
show_bar=self._show_bar,
109
show_count=self._show_count,
110
to_messages_file=self._to_messages_file,
112
self._stack.append(new_bar)
115
def return_pb(self, bar):
116
"""Return bar after its been used."""
117
if bar is not self._stack[-1]:
118
raise errors.MissingProgressBarFinish()
65
122
class _BaseProgressBar(object):
66
124
def __init__(self,
67
125
to_file=sys.stderr,
82
141
self.show_eta = show_eta
83
142
self.show_bar = show_bar
84
143
self.show_count = show_count
147
"""Return this bar to its progress stack."""
149
assert self._stack is not None
150
self._stack.return_pb(self)
86
152
def note(self, fmt_string, *args, **kwargs):
87
153
"""Record a note without disrupting the progress bar."""
89
155
self.to_messages_file.write(fmt_string % args)
90
156
self.to_messages_file.write('\n')
158
def child_progress(self, **kwargs):
159
return ChildProgress(**kwargs)
93
162
class DummyProgress(_BaseProgressBar):
94
163
"""Progress-bar standin that does nothing.
101
170
def update(self, msg=None, current=None, total=None):
173
def child_update(self, message, current, total):
107
179
def note(self, fmt_string, *args, **kwargs):
108
180
"""See _BaseProgressBar.note()."""
182
def child_progress(self, **kwargs):
183
return DummyProgress(**kwargs)
111
185
class DotsProgressBar(_BaseProgressBar):
112
187
def __init__(self, **kwargs):
113
188
_BaseProgressBar.__init__(self, **kwargs)
114
189
self.last_msg = None
132
207
self.to_file.write('\n')
209
def child_update(self, message, current, total):
135
212
class TTYProgressBar(_BaseProgressBar):
136
213
"""Progress bar display object.
186
self.update(self.last_msg, self.last_cnt, self.last_total)
190
def update(self, msg, current_cnt=None, total_cnt=None):
264
self.update(self.last_msg, self.last_cnt, self.last_total,
267
def child_update(self, message, current, total):
268
if current is not None and total != 0:
269
child_fraction = float(current) / total
270
if self.last_cnt is None:
272
elif self.last_cnt + child_fraction <= self.last_total:
273
self.child_fraction = child_fraction
275
mutter('not updating child fraction')
276
if self.last_msg is None:
281
def update(self, msg, current_cnt=None, total_cnt=None,
191
283
"""Update and redraw progress bar."""
284
self.child_fraction = child_fraction
193
286
if current_cnt < 0:
208
301
if self.show_eta and self.start_time and total_cnt:
209
eta = get_eta(self.start_time, current_cnt, total_cnt,
210
last_updates = self.last_updates)
302
eta = get_eta(self.start_time, current_cnt+child_fraction,
303
total_cnt, last_updates = self.last_updates)
211
304
eta_str = " " + str_tdelta(eta)
247
340
# number of markers highlighted in bar
248
markers = int(round(float(cols) * current_cnt / total_cnt))
341
markers = int(round(float(cols) *
342
(current_cnt + child_fraction) / total_cnt))
249
343
bar_str = '[' + ('=' * markers).ljust(cols) + '] '
251
345
# don't know total, so can't show completion.
269
363
self.to_file.write('\r%s\r' % (' ' * (self.width - 1)))
270
364
#self.to_file.flush()
367
class ChildProgress(_BaseProgressBar):
368
"""A progress indicator that pushes its data to the parent"""
369
def __init__(self, _stack, **kwargs):
370
_BaseProgressBar.__init__(self, _stack=_stack, **kwargs)
371
self.parent = _stack.top()
374
self.child_fraction = 0
377
def update(self, msg, current_cnt=None, total_cnt=None):
378
self.current = current_cnt
379
self.total = total_cnt
381
self.child_fraction = 0
384
def child_update(self, message, current, total):
385
if current is None or total == 0:
386
self.child_fraction = 0
388
self.child_fraction = float(current) / total
392
if self.current is None:
395
count = self.current+self.child_fraction
396
if count > self.total:
397
mutter('clamping count of %d to %d' % (count, self.total))
399
self.parent.child_update(self.message, count, self.total)
273
405
def str_tdelta(delt):
315
447
return total_duration - elapsed
450
class ProgressPhase(object):
451
"""Update progress object with the current phase"""
452
def __init__(self, message, total, pb):
453
object.__init__(self)
455
self.message = message
457
self.cur_phase = None
459
def next_phase(self):
460
if self.cur_phase is None:
464
assert self.cur_phase < self.total
465
self.pb.update(self.message, self.cur_phase, self.total)
320
470
result = doctest.testmod()