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