~bzr-pqm/bzr/bzr.dev

3948.2.1 by Martin Pool
Add ProgressTask repr
1
# Copyright (C) 2005, 2006, 2008, 2009 Canonical Ltd
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
648 by Martin Pool
- import aaron's progress-indicator code
16
649 by Martin Pool
- some cleanups for the progressbar method
17
3006.3.3 by Robert Collins
Docstring improvement and remove TODO's from progres.py.
18
"""Progress indicators.
19
20
The usual way to use this is via bzrlib.ui.ui_factory.nested_progress_bar which
3948.2.4 by Martin Pool
Remove some obsolete progress docstring
21
will manage a conceptual stack of nested activities.
649 by Martin Pool
- some cleanups for the progressbar method
22
"""
23
934 by Martin Pool
todo
24
648 by Martin Pool
- import aaron's progress-indicator code
25
import sys
660 by Martin Pool
- use plain unix time, not datetime module
26
import time
964 by Martin Pool
- show progress on dumb terminals by printing dots
27
import os
3882.8.12 by Martin Pool
Give a warning, not an error, if a progress bar is not finished in order
28
import warnings
649 by Martin Pool
- some cleanups for the progressbar method
29
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
30
1996.3.32 by John Arbash Meinel
from bzrlib.ui lazy import progress, and make progress import lazily
31
from bzrlib import (
32
    errors,
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
33
    osutils,
34
    trace,
35
    ui,
1996.3.32 by John Arbash Meinel
from bzrlib.ui lazy import progress, and make progress import lazily
36
    )
1843.3.7 by John Arbash Meinel
new env var 'BZR_PROGRESS_BAR' to select the exact progress type
37
from bzrlib.trace import mutter
3948.2.6 by Martin Pool
ProgressBarStack is deprecated
38
from bzrlib.symbol_versioning import (
39
    deprecated_in,
40
    deprecated_method,
41
    )
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
42
43
649 by Martin Pool
- some cleanups for the progressbar method
44
def _supports_progress(f):
2599.1.1 by Martin Pool
Don't show dots progress indicatiors in noninteractive mode
45
    """Detect if we can use pretty progress bars on the output stream f.
46
47
    If this returns true we expect that a human may be looking at that 
48
    output, and that we can repaint a line to update it.
49
    """
1843.3.7 by John Arbash Meinel
new env var 'BZR_PROGRESS_BAR' to select the exact progress type
50
    isatty = getattr(f, 'isatty', None)
51
    if isatty is None:
695 by Martin Pool
- don't display progress bars on really dumb terminals
52
        return False
1843.3.7 by John Arbash Meinel
new env var 'BZR_PROGRESS_BAR' to select the exact progress type
53
    if not isatty():
695 by Martin Pool
- don't display progress bars on really dumb terminals
54
        return False
55
    if os.environ.get('TERM') == 'dumb':
56
        # e.g. emacs compile window
57
        return False
58
    return True
649 by Martin Pool
- some cleanups for the progressbar method
59
60
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
61
class ProgressTask(object):
62
    """Model component of a progress indicator.
63
64
    Most code that needs to indicate progress should update one of these, 
65
    and it will in turn update the display, if one is present.
3882.8.5 by Martin Pool
Progress tasks can indicate what kind of display is useful
66
67
    Code updating the task may also set fields as hints about how to display
68
    it: show_pct, show_spinner, show_eta, show_count, show_bar.  UIs
69
    will not necessarily respect all these fields.
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
70
    """
71
3882.8.2 by Martin Pool
ProgressTask holds a reference to the ui that displays it
72
    def __init__(self, parent_task=None, ui_factory=None):
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
73
        self._parent_task = parent_task
74
        self._last_update = 0
75
        self.total_cnt = None
76
        self.current_cnt = None
77
        self.msg = ''
3882.8.2 by Martin Pool
ProgressTask holds a reference to the ui that displays it
78
        self.ui_factory = ui_factory
3882.8.5 by Martin Pool
Progress tasks can indicate what kind of display is useful
79
        self.show_pct = False
80
        self.show_spinner = True
81
        self.show_eta = False,
82
        self.show_count = True
83
        self.show_bar = True
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
84
3948.2.1 by Martin Pool
Add ProgressTask repr
85
    def __repr__(self):
86
        return '%s(%r/%r, msg=%r)' % (
87
            self.__class__.__name__,
88
            self.current_cnt,
89
            self.total_cnt,
90
            self.msg)
91
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
92
    def update(self, msg, current_cnt=None, total_cnt=None):
93
        self.msg = msg
94
        self.current_cnt = current_cnt
95
        if total_cnt:
96
            self.total_cnt = total_cnt
3948.2.3 by Martin Pool
Make the interface from ProgressTask to ui more private
97
        self.ui_factory._progress_updated(self)
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
98
3882.8.8 by Martin Pool
Progress and UI test cleanups
99
    def tick(self):
100
        self.update(self.msg)
101
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
102
    def finished(self):
3948.2.3 by Martin Pool
Make the interface from ProgressTask to ui more private
103
        self.ui_factory._progress_finished(self)
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
104
105
    def make_sub_task(self):
3882.8.3 by Martin Pool
Move display of transport throughput into TextProgressView
106
        return ProgressTask(self, self.ui_factory)
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
107
108
    def _overall_completion_fraction(self, child_fraction=0.0):
109
        """Return fractional completion of this task and its parents
110
        
111
        Returns None if no completion can be computed."""
112
        if self.total_cnt:
113
            own_fraction = (float(self.current_cnt) + child_fraction) / self.total_cnt
114
        else:
115
            own_fraction = None
116
        if self._parent_task is None:
117
            return own_fraction
118
        else:
119
            if own_fraction is None:
120
                own_fraction = 0.0
121
            return self._parent_task._overall_completion_fraction(own_fraction)
122
3882.8.4 by Martin Pool
All UI factories should support note()
123
    def note(self, fmt_string, *args):
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
124
        """Record a note without disrupting the progress bar."""
125
        # XXX: shouldn't be here; put it in mutter or the ui instead
3943.2.3 by Martin Pool
Don't do string interpolation if there are no arguments
126
        if args:
127
            self.ui_factory.note(fmt_string % args)
128
        else:
129
            self.ui_factory.note(fmt_string)
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
130
131
    def clear(self):
132
        # XXX: shouldn't be here; put it in mutter or the ui instead
3882.8.2 by Martin Pool
ProgressTask holds a reference to the ui that displays it
133
        self.ui_factory.clear_term()
1843.3.7 by John Arbash Meinel
new env var 'BZR_PROGRESS_BAR' to select the exact progress type
134
649 by Martin Pool
- some cleanups for the progressbar method
135
1681.1.2 by Robert Collins
* bzrlib.ui.text.TextUIFactory now accepts a bar_type parameter which
136
def ProgressBar(to_file=None, **kwargs):
964 by Martin Pool
- show progress on dumb terminals by printing dots
137
    """Abstract factory"""
1681.1.2 by Robert Collins
* bzrlib.ui.text.TextUIFactory now accepts a bar_type parameter which
138
    if to_file is None:
139
        to_file = sys.stderr
1843.3.7 by John Arbash Meinel
new env var 'BZR_PROGRESS_BAR' to select the exact progress type
140
    requested_bar_type = os.environ.get('BZR_PROGRESS_BAR')
141
    # An value of '' or not set reverts to standard processing
142
    if requested_bar_type in (None, ''):
143
        if _supports_progress(to_file):
144
            return TTYProgressBar(to_file=to_file, **kwargs)
145
        else:
2599.1.1 by Martin Pool
Don't show dots progress indicatiors in noninteractive mode
146
            return DummyProgress(to_file=to_file, **kwargs)
964 by Martin Pool
- show progress on dumb terminals by printing dots
147
    else:
1843.3.7 by John Arbash Meinel
new env var 'BZR_PROGRESS_BAR' to select the exact progress type
148
        # Minor sanitation to prevent spurious errors
149
        requested_bar_type = requested_bar_type.lower().strip()
150
        # TODO: jam 20060710 Arguably we shouldn't raise an exception
151
        #       but should instead just disable progress bars if we
152
        #       don't recognize the type
153
        if requested_bar_type not in _progress_bar_types:
154
            raise errors.InvalidProgressBarType(requested_bar_type,
155
                                                _progress_bar_types.keys())
156
        return _progress_bar_types[requested_bar_type](to_file=to_file, **kwargs)
157
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
158
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
159
class ProgressBarStack(object):
3948.2.6 by Martin Pool
ProgressBarStack is deprecated
160
    """A stack of progress bars.
161
    
162
    This class is deprecated: instead, ask the ui factory for a new progress
163
    task and finish it when it's done.
164
    """
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
165
3948.2.6 by Martin Pool
ProgressBarStack is deprecated
166
    @deprecated_method(deprecated_in((1, 12, 0)))
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
167
    def __init__(self,
1681.1.2 by Robert Collins
* bzrlib.ui.text.TextUIFactory now accepts a bar_type parameter which
168
                 to_file=None,
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
169
                 show_pct=False,
1551.2.33 by Aaron Bentley
Hide ETA, show spinner by default
170
                 show_spinner=True,
171
                 show_eta=False,
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
172
                 show_bar=True,
173
                 show_count=True,
1681.1.2 by Robert Collins
* bzrlib.ui.text.TextUIFactory now accepts a bar_type parameter which
174
                 to_messages_file=None,
1594.1.3 by Robert Collins
Fixup pb usage to use nested_progress_bar.
175
                 klass=None):
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
176
        """Setup the stack with the parameters the progress bars should have."""
1681.1.2 by Robert Collins
* bzrlib.ui.text.TextUIFactory now accepts a bar_type parameter which
177
        if to_file is None:
178
            to_file = sys.stderr
179
        if to_messages_file is None:
180
            to_messages_file = sys.stdout
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
181
        self._to_file = to_file
182
        self._show_pct = show_pct
183
        self._show_spinner = show_spinner
184
        self._show_eta = show_eta
185
        self._show_bar = show_bar
186
        self._show_count = show_count
187
        self._to_messages_file = to_messages_file
188
        self._stack = []
1843.3.5 by John Arbash Meinel
Add tests to assert we fall back to DotsProgressBar when appropriate.
189
        self._klass = klass or ProgressBar
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
190
1551.2.29 by Aaron Bentley
Got stack handling under test
191
    def top(self):
192
        if len(self._stack) != 0:
193
            return self._stack[-1]
194
        else:
195
            return None
196
1558.8.1 by Aaron Bentley
Fix overall progress bar's interaction with 'note' and 'warning'
197
    def bottom(self):
198
        if len(self._stack) != 0:
199
            return self._stack[0]
200
        else:
201
            return None
202
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
203
    def get_nested(self):
204
        """Return a nested progress bar."""
1551.2.29 by Aaron Bentley
Got stack handling under test
205
        if len(self._stack) == 0:
206
            func = self._klass
207
        else:
208
            func = self.top().child_progress
209
        new_bar = func(to_file=self._to_file,
210
                       show_pct=self._show_pct,
211
                       show_spinner=self._show_spinner,
212
                       show_eta=self._show_eta,
213
                       show_bar=self._show_bar,
214
                       show_count=self._show_count,
215
                       to_messages_file=self._to_messages_file,
216
                       _stack=self)
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
217
        self._stack.append(new_bar)
218
        return new_bar
219
220
    def return_pb(self, bar):
221
        """Return bar after its been used."""
1594.1.4 by Robert Collins
Fix identity test in ProgressBarStack.return_pb
222
        if bar is not self._stack[-1]:
3882.8.12 by Martin Pool
Give a warning, not an error, if a progress bar is not finished in order
223
            warnings.warn("%r is not currently active" % (bar,))
224
        else:
225
            self._stack.pop()
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
226
227
 
964 by Martin Pool
- show progress on dumb terminals by printing dots
228
class _BaseProgressBar(object):
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
229
964 by Martin Pool
- show progress on dumb terminals by printing dots
230
    def __init__(self,
1681.1.2 by Robert Collins
* bzrlib.ui.text.TextUIFactory now accepts a bar_type parameter which
231
                 to_file=None,
964 by Martin Pool
- show progress on dumb terminals by printing dots
232
                 show_pct=False,
233
                 show_spinner=False,
1793.1.1 by Aaron Bentley
Hide TTYProgressBars unless they last more than 1 second
234
                 show_eta=False,
964 by Martin Pool
- show progress on dumb terminals by printing dots
235
                 show_bar=True,
1534.5.6 by Robert Collins
split out converter logic into per-format objects.
236
                 show_count=True,
1681.1.2 by Robert Collins
* bzrlib.ui.text.TextUIFactory now accepts a bar_type parameter which
237
                 to_messages_file=None,
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
238
                 _stack=None):
964 by Martin Pool
- show progress on dumb terminals by printing dots
239
        object.__init__(self)
1681.1.2 by Robert Collins
* bzrlib.ui.text.TextUIFactory now accepts a bar_type parameter which
240
        if to_file is None:
241
            to_file = sys.stderr
242
        if to_messages_file is None:
243
            to_messages_file = sys.stdout
964 by Martin Pool
- show progress on dumb terminals by printing dots
244
        self.to_file = to_file
1534.5.6 by Robert Collins
split out converter logic into per-format objects.
245
        self.to_messages_file = to_messages_file
964 by Martin Pool
- show progress on dumb terminals by printing dots
246
        self.last_msg = None
247
        self.last_cnt = None
248
        self.last_total = None
249
        self.show_pct = show_pct
250
        self.show_spinner = show_spinner
251
        self.show_eta = show_eta
252
        self.show_bar = show_bar
253
        self.show_count = show_count
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
254
        self._stack = _stack
1596.2.16 by Robert Collins
Microprofiling: progress.update was costing 0.01 ms per call in time.time.
255
        # seed throttler
256
        self.MIN_PAUSE = 0.1 # seconds
2120.1.1 by John Arbash Meinel
Use time.time() because time.clock() is CPU time, not wall time
257
        now = time.time()
1596.2.16 by Robert Collins
Microprofiling: progress.update was costing 0.01 ms per call in time.time.
258
        # starting now
2745.6.52 by Andrew Bennetts
Revert bad change to bzrlib/progress.py
259
        self.start_time = now
1596.2.16 by Robert Collins
Microprofiling: progress.update was costing 0.01 ms per call in time.time.
260
        # next update should not throttle
261
        self.last_update = now - self.MIN_PAUSE - 1
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
262
263
    def finished(self):
264
        """Return this bar to its progress stack."""
265
        self.clear()
266
        self._stack.return_pb(self)
1104 by Martin Pool
- Add a simple UIFactory
267
1534.5.6 by Robert Collins
split out converter logic into per-format objects.
268
    def note(self, fmt_string, *args, **kwargs):
269
        """Record a note without disrupting the progress bar."""
1558.8.5 by Aaron Bentley
Pass note up the stack instead of using bzrlib.ui_factory
270
        self.clear()
1558.7.9 by Aaron Bentley
Bad change. (broke tests). Reverted.
271
        self.to_messages_file.write(fmt_string % args)
272
        self.to_messages_file.write('\n')
1104 by Martin Pool
- Add a simple UIFactory
273
1551.2.29 by Aaron Bentley
Got stack handling under test
274
    def child_progress(self, **kwargs):
275
        return ChildProgress(**kwargs)
276
1534.11.7 by Robert Collins
Test and correct the problem with nested test logs breaking further in-test logs.
277
1104 by Martin Pool
- Add a simple UIFactory
278
class DummyProgress(_BaseProgressBar):
279
    """Progress-bar standin that does nothing.
280
281
    This can be used as the default argument for methods that
282
    take an optional progress indicator."""
3882.8.8 by Martin Pool
Progress and UI test cleanups
283
1104 by Martin Pool
- Add a simple UIFactory
284
    def tick(self):
285
        pass
286
287
    def update(self, msg=None, current=None, total=None):
288
        pass
289
1551.2.27 by Aaron Bentley
Got propogation under test
290
    def child_update(self, message, current, total):
291
        pass
292
1104 by Martin Pool
- Add a simple UIFactory
293
    def clear(self):
294
        pass
964 by Martin Pool
- show progress on dumb terminals by printing dots
295
        
1534.5.6 by Robert Collins
split out converter logic into per-format objects.
296
    def note(self, fmt_string, *args, **kwargs):
297
        """See _BaseProgressBar.note()."""
1534.5.9 by Robert Collins
Advise users running upgrade on a checkout to also run it on the branch.
298
1551.2.29 by Aaron Bentley
Got stack handling under test
299
    def child_progress(self, **kwargs):
300
        return DummyProgress(**kwargs)
1534.5.9 by Robert Collins
Advise users running upgrade on a checkout to also run it on the branch.
301
1681.1.2 by Robert Collins
* bzrlib.ui.text.TextUIFactory now accepts a bar_type parameter which
302
964 by Martin Pool
- show progress on dumb terminals by printing dots
303
class DotsProgressBar(_BaseProgressBar):
1594.1.3 by Robert Collins
Fixup pb usage to use nested_progress_bar.
304
964 by Martin Pool
- show progress on dumb terminals by printing dots
305
    def __init__(self, **kwargs):
306
        _BaseProgressBar.__init__(self, **kwargs)
307
        self.last_msg = None
308
        self.need_nl = False
309
        
310
    def tick(self):
311
        self.update()
312
        
313
    def update(self, msg=None, current_cnt=None, total_cnt=None):
314
        if msg and msg != self.last_msg:
315
            if self.need_nl:
316
                self.to_file.write('\n')
317
            self.to_file.write(msg + ': ')
318
            self.last_msg = msg
319
        self.need_nl = True
320
        self.to_file.write('.')
321
        
322
    def clear(self):
323
        if self.need_nl:
324
            self.to_file.write('\n')
1681.1.2 by Robert Collins
* bzrlib.ui.text.TextUIFactory now accepts a bar_type parameter which
325
        self.need_nl = False
964 by Martin Pool
- show progress on dumb terminals by printing dots
326
        
1551.2.28 by Aaron Bentley
Ensure all ProgressBar implementations can be used as parents
327
    def child_update(self, message, current, total):
328
        self.tick()
1681.1.2 by Robert Collins
* bzrlib.ui.text.TextUIFactory now accepts a bar_type parameter which
329
1843.3.7 by John Arbash Meinel
new env var 'BZR_PROGRESS_BAR' to select the exact progress type
330
331
964 by Martin Pool
- show progress on dumb terminals by printing dots
332
    
333
class TTYProgressBar(_BaseProgressBar):
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
334
    """Progress bar display object.
335
336
    Several options are available to control the display.  These can
337
    be passed as parameters to the constructor or assigned at any time:
338
339
    show_pct
340
        Show percentage complete.
341
    show_spinner
342
        Show rotating baton.  This ticks over on every update even
343
        if the values don't change.
344
    show_eta
345
        Show predicted time-to-completion.
346
    show_bar
347
        Show bar graph.
348
    show_count
349
        Show numerical counts.
350
351
    The output file should be in line-buffered or unbuffered mode.
352
    """
353
    SPIN_CHARS = r'/-\|'
661 by Martin Pool
- limit rate at which progress bar is updated
354
964 by Martin Pool
- show progress on dumb terminals by printing dots
355
356
    def __init__(self, **kwargs):
1185.33.60 by Martin Pool
Use full terminal width for verbose test output.
357
        from bzrlib.osutils import terminal_width
964 by Martin Pool
- show progress on dumb terminals by printing dots
358
        _BaseProgressBar.__init__(self, **kwargs)
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
359
        self.spin_pos = 0
1185.33.60 by Martin Pool
Use full terminal width for verbose test output.
360
        self.width = terminal_width()
1843.3.3 by John Arbash Meinel
Don't let the last_updates list grow without bound.
361
        self.last_updates = []
1843.3.4 by John Arbash Meinel
Remove get_eta's ability to modify last_updates.
362
        self._max_last_updates = 10
1551.2.28 by Aaron Bentley
Ensure all ProgressBar implementations can be used as parents
363
        self.child_fraction = 0
1843.3.1 by John Arbash Meinel
Don't clear anything if nothing has been written.
364
        self._have_output = False
964 by Martin Pool
- show progress on dumb terminals by printing dots
365
    
1793.1.1 by Aaron Bentley
Hide TTYProgressBars unless they last more than 1 second
366
    def throttle(self, old_msg):
964 by Martin Pool
- show progress on dumb terminals by printing dots
367
        """Return True if the bar was updated too recently"""
1596.2.16 by Robert Collins
Microprofiling: progress.update was costing 0.01 ms per call in time.time.
368
        # time.time consistently takes 40/4000 ms = 0.01 ms.
2120.1.1 by John Arbash Meinel
Use time.time() because time.clock() is CPU time, not wall time
369
        # time.clock() is faster, but gives us CPU time, not wall-clock time
370
        now = time.time()
1793.1.1 by Aaron Bentley
Hide TTYProgressBars unless they last more than 1 second
371
        if self.start_time is not None and (now - self.start_time) < 1:
372
            return True
373
        if old_msg != self.last_msg:
374
            return False
1596.2.16 by Robert Collins
Microprofiling: progress.update was costing 0.01 ms per call in time.time.
375
        interval = now - self.last_update
376
        # if interval > 0
377
        if interval < self.MIN_PAUSE:
378
            return True
964 by Martin Pool
- show progress on dumb terminals by printing dots
379
1185.16.75 by Martin Pool
- improved eta estimation for progress bar
380
        self.last_updates.append(now - self.last_update)
1843.3.3 by John Arbash Meinel
Don't let the last_updates list grow without bound.
381
        # Don't let the queue grow without bound
382
        self.last_updates = self.last_updates[-self._max_last_updates:]
964 by Martin Pool
- show progress on dumb terminals by printing dots
383
        self.last_update = now
384
        return False
929 by Martin Pool
- progress bar: avoid repeatedly checking screen width
385
        
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
386
    def tick(self):
3006.3.2 by Robert Collins
More formatting corrections.
387
        self.update(self.last_msg, self.last_cnt, self.last_total,
1551.2.27 by Aaron Bentley
Got propogation under test
388
                    self.child_fraction)
389
1551.2.28 by Aaron Bentley
Ensure all ProgressBar implementations can be used as parents
390
    def child_update(self, message, current, total):
1551.2.35 by Aaron Bentley
Fix division-by-zero
391
        if current is not None and total != 0:
1551.2.30 by Aaron Bentley
Bugfixes to progress stuff
392
            child_fraction = float(current) / total
393
            if self.last_cnt is None:
394
                pass
395
            elif self.last_cnt + child_fraction <= self.last_total:
396
                self.child_fraction = child_fraction
397
        if self.last_msg is None:
398
            self.last_msg = ''
1551.2.28 by Aaron Bentley
Ensure all ProgressBar implementations can be used as parents
399
        self.tick()
400
3006.3.1 by Robert Collins
Minor PEP8 changes.
401
    def update(self, msg, current_cnt=None, total_cnt=None,
3882.8.1 by Martin Pool
Remove experimental transport display from TTYProgressBar
402
            child_fraction=0):
3882.7.6 by Martin Pool
Preliminary support for drawing network io into the progress bar
403
        """Update and redraw progress bar.
3882.8.1 by Martin Pool
Remove experimental transport display from TTYProgressBar
404
        """
1534.11.1 by Robert Collins
Teach bzr selftest to use a progress bar in non verbose mode.
405
        if msg is None:
406
            msg = self.last_msg
407
408
        if total_cnt is None:
409
            total_cnt = self.last_total
410
1308 by Martin Pool
- make progress bar more tolerant of out-of-range values
411
        if current_cnt < 0:
412
            current_cnt = 0
413
            
414
        if current_cnt > total_cnt:
415
            total_cnt = current_cnt
1570.1.9 by Robert Collins
Do not throttle updates to progress bars that change the message.
416
        
1596.2.17 by Robert Collins
Notes on further progress tuning.
417
        ## # optional corner case optimisation 
418
        ## # currently does not seem to fire so costs more than saved.
419
        ## # trivial optimal case:
420
        ## # NB if callers are doing a clear and restore with
421
        ## # the saved values, this will prevent that:
422
        ## # in that case add a restore method that calls
423
        ## # _do_update or some such
424
        ## if (self.last_msg == msg and
425
        ##     self.last_cnt == current_cnt and
426
        ##     self.last_total == total_cnt and
427
        ##     self.child_fraction == child_fraction):
428
        ##     return
429
3882.7.6 by Martin Pool
Preliminary support for drawing network io into the progress bar
430
        if msg is None:
431
            msg = ''
432
1570.1.9 by Robert Collins
Do not throttle updates to progress bars that change the message.
433
        old_msg = self.last_msg
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
434
        # save these for the tick() function
435
        self.last_msg = msg
436
        self.last_cnt = current_cnt
437
        self.last_total = total_cnt
1596.2.17 by Robert Collins
Notes on further progress tuning.
438
        self.child_fraction = child_fraction
439
440
        # each function call takes 20ms/4000 = 0.005 ms, 
441
        # but multiple that by 4000 calls -> starts to cost.
442
        # so anything to make this function call faster
443
        # will improve base 'diff' time by up to 0.1 seconds.
1793.1.1 by Aaron Bentley
Hide TTYProgressBars unless they last more than 1 second
444
        if self.throttle(old_msg):
1596.2.17 by Robert Collins
Notes on further progress tuning.
445
            return
446
447
        if self.show_eta and self.start_time and self.last_total:
448
            eta = get_eta(self.start_time, self.last_cnt + self.child_fraction, 
449
                    self.last_total, last_updates = self.last_updates)
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
450
            eta_str = " " + str_tdelta(eta)
451
        else:
452
            eta_str = ""
453
454
        if self.show_spinner:
455
            spin_str = self.SPIN_CHARS[self.spin_pos % 4] + ' '            
456
        else:
457
            spin_str = ''
458
459
        # always update this; it's also used for the bar
460
        self.spin_pos += 1
461
1596.2.17 by Robert Collins
Notes on further progress tuning.
462
        if self.show_pct and self.last_total and self.last_cnt:
463
            pct = 100.0 * ((self.last_cnt + self.child_fraction) / self.last_total)
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
464
            pct_str = ' (%5.1f%%)' % pct
465
        else:
466
            pct_str = ''
467
468
        if not self.show_count:
469
            count_str = ''
1596.2.17 by Robert Collins
Notes on further progress tuning.
470
        elif self.last_cnt is None:
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
471
            count_str = ''
1596.2.17 by Robert Collins
Notes on further progress tuning.
472
        elif self.last_total is None:
473
            count_str = ' %i' % (self.last_cnt)
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
474
        else:
475
            # make both fields the same size
1596.2.17 by Robert Collins
Notes on further progress tuning.
476
            t = '%i' % (self.last_total)
477
            c = '%*i' % (len(t), self.last_cnt)
3882.7.6 by Martin Pool
Preliminary support for drawing network io into the progress bar
478
            count_str = ' ' + c + '/' + t
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
479
480
        if self.show_bar:
481
            # progress bar, if present, soaks up all remaining space
1596.2.17 by Robert Collins
Notes on further progress tuning.
482
            cols = self.width - 1 - len(self.last_msg) - len(spin_str) - len(pct_str) \
3882.8.1 by Martin Pool
Remove experimental transport display from TTYProgressBar
483
                   - len(eta_str) - len(count_str) - 3
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
484
1596.2.17 by Robert Collins
Notes on further progress tuning.
485
            if self.last_total:
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
486
                # number of markers highlighted in bar
1551.2.30 by Aaron Bentley
Bugfixes to progress stuff
487
                markers = int(round(float(cols) * 
1596.2.17 by Robert Collins
Notes on further progress tuning.
488
                              (self.last_cnt + self.child_fraction) / self.last_total))
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
489
                bar_str = '[' + ('=' * markers).ljust(cols) + '] '
669 by Martin Pool
- don't show progress bar unless completion is known
490
            elif False:
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
491
                # don't know total, so can't show completion.
492
                # so just show an expanded spinning thingy
493
                m = self.spin_pos % cols
668 by Martin Pool
- fix sweeping bar progress indicator
494
                ms = (' ' * m + '*').ljust(cols)
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
495
                
496
                bar_str = '[' + ms + '] '
669 by Martin Pool
- don't show progress bar unless completion is known
497
            else:
498
                bar_str = ''
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
499
        else:
500
            bar_str = ''
501
3882.8.1 by Martin Pool
Remove experimental transport display from TTYProgressBar
502
        m = spin_str + bar_str + self.last_msg + count_str \
3882.7.6 by Martin Pool
Preliminary support for drawing network io into the progress bar
503
            + pct_str + eta_str
2095.4.4 by mbp at sourcefrog
Truncate progress bar rather than complaining if it's too long
504
        self.to_file.write('\r%-*.*s' % (self.width - 1, self.width - 1, m))
1843.3.1 by John Arbash Meinel
Don't clear anything if nothing has been written.
505
        self._have_output = True
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
506
        #self.to_file.flush()
507
            
3006.3.2 by Robert Collins
More formatting corrections.
508
    def clear(self):
1843.3.1 by John Arbash Meinel
Don't clear anything if nothing has been written.
509
        if self._have_output:
510
            self.to_file.write('\r%s\r' % (' ' * (self.width - 1)))
511
        self._have_output = False
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
512
        #self.to_file.flush()        
649 by Martin Pool
- some cleanups for the progressbar method
513
1551.2.27 by Aaron Bentley
Got propogation under test
514
1843.3.7 by John Arbash Meinel
new env var 'BZR_PROGRESS_BAR' to select the exact progress type
515
516
1551.2.28 by Aaron Bentley
Ensure all ProgressBar implementations can be used as parents
517
class ChildProgress(_BaseProgressBar):
1551.2.27 by Aaron Bentley
Got propogation under test
518
    """A progress indicator that pushes its data to the parent"""
1681.1.2 by Robert Collins
* bzrlib.ui.text.TextUIFactory now accepts a bar_type parameter which
519
1551.2.29 by Aaron Bentley
Got stack handling under test
520
    def __init__(self, _stack, **kwargs):
521
        _BaseProgressBar.__init__(self, _stack=_stack, **kwargs)
522
        self.parent = _stack.top()
1551.2.27 by Aaron Bentley
Got propogation under test
523
        self.current = None
524
        self.total = None
525
        self.child_fraction = 0
526
        self.message = None
527
528
    def update(self, msg, current_cnt=None, total_cnt=None):
529
        self.current = current_cnt
2592.6.11 by Robert Collins
* A progress bar has been added for knitpack -> knitpack fetching.
530
        if total_cnt is not None:
531
            self.total = total_cnt
1551.2.27 by Aaron Bentley
Got propogation under test
532
        self.message = msg
533
        self.child_fraction = 0
534
        self.tick()
535
536
    def child_update(self, message, current, total):
1551.2.35 by Aaron Bentley
Fix division-by-zero
537
        if current is None or total == 0:
1551.2.30 by Aaron Bentley
Bugfixes to progress stuff
538
            self.child_fraction = 0
539
        else:
540
            self.child_fraction = float(current) / total
1551.2.27 by Aaron Bentley
Got propogation under test
541
        self.tick()
542
543
    def tick(self):
1551.2.30 by Aaron Bentley
Bugfixes to progress stuff
544
        if self.current is None:
545
            count = None
546
        else:
547
            count = self.current+self.child_fraction
548
            if count > self.total:
1596.2.35 by Robert Collins
Subclass SequenceMatcher to get a slightly faster (in our case) find_longest_match routine.
549
                if __debug__:
550
                    mutter('clamping count of %d to %d' % (count, self.total))
1551.2.30 by Aaron Bentley
Bugfixes to progress stuff
551
                count = self.total
1551.2.27 by Aaron Bentley
Got propogation under test
552
        self.parent.child_update(self.message, count, self.total)
553
1551.2.29 by Aaron Bentley
Got stack handling under test
554
    def clear(self):
1551.2.30 by Aaron Bentley
Bugfixes to progress stuff
555
        pass
1551.2.29 by Aaron Bentley
Got stack handling under test
556
1558.8.6 by Aaron Bentley
Fix note implementation
557
    def note(self, *args, **kwargs):
1558.8.5 by Aaron Bentley
Pass note up the stack instead of using bzrlib.ui_factory
558
        self.parent.note(*args, **kwargs)
559
3146.6.1 by Aaron Bentley
InterDifferingSerializer shows a progress bar
560
561
class InstrumentedProgress(TTYProgressBar):
562
    """TTYProgress variant that tracks outcomes"""
563
564
    def __init__(self, *args, **kwargs):
565
        self.always_throttled = True
566
        self.never_throttle = False
567
        TTYProgressBar.__init__(self, *args, **kwargs)
568
569
    def throttle(self, old_message):
570
        if self.never_throttle:
571
            result =  False
572
        else:
573
            result = TTYProgressBar.throttle(self, old_message)
574
        if result is False:
575
            self.always_throttled = False
576
577
648 by Martin Pool
- import aaron's progress-indicator code
578
def str_tdelta(delt):
579
    if delt is None:
580
        return "-:--:--"
660 by Martin Pool
- use plain unix time, not datetime module
581
    delt = int(round(delt))
582
    return '%d:%02d:%02d' % (delt/3600,
583
                             (delt/60) % 60,
584
                             delt % 60)
585
586
1185.16.75 by Martin Pool
- improved eta estimation for progress bar
587
def get_eta(start_time, current, total, enough_samples=3, last_updates=None, n_recent=10):
660 by Martin Pool
- use plain unix time, not datetime module
588
    if start_time is None:
589
        return None
590
591
    if not total:
592
        return None
593
594
    if current < enough_samples:
595
        return None
596
597
    if current > total:
598
        return None                     # wtf?
599
2120.1.1 by John Arbash Meinel
Use time.time() because time.clock() is CPU time, not wall time
600
    elapsed = time.time() - start_time
660 by Martin Pool
- use plain unix time, not datetime module
601
602
    if elapsed < 2.0:                   # not enough time to estimate
603
        return None
604
    
605
    total_duration = float(elapsed) * float(total) / float(current)
606
1185.16.75 by Martin Pool
- improved eta estimation for progress bar
607
    if last_updates and len(last_updates) >= n_recent:
608
        avg = sum(last_updates) / float(len(last_updates))
609
        time_left = avg * (total - current)
610
611
        old_time_left = total_duration - elapsed
612
613
        # We could return the average, or some other value here
614
        return (time_left + old_time_left) / 2
615
660 by Martin Pool
- use plain unix time, not datetime module
616
    return total_duration - elapsed
648 by Martin Pool
- import aaron's progress-indicator code
617
649 by Martin Pool
- some cleanups for the progressbar method
618
1551.2.32 by Aaron Bentley
Handle progress phases more nicely in merge
619
class ProgressPhase(object):
620
    """Update progress object with the current phase"""
621
    def __init__(self, message, total, pb):
622
        object.__init__(self)
623
        self.pb = pb
624
        self.message = message
625
        self.total = total
626
        self.cur_phase = None
627
628
    def next_phase(self):
629
        if self.cur_phase is None:
630
            self.cur_phase = 0
631
        else:
632
            self.cur_phase += 1
633
        self.pb.update(self.message, self.cur_phase, self.total)
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
634
635
636
_progress_bar_types = {}
637
_progress_bar_types['dummy'] = DummyProgress
638
_progress_bar_types['none'] = DummyProgress
639
_progress_bar_types['tty'] = TTYProgressBar
640
_progress_bar_types['dots'] = DotsProgressBar