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