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