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