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