/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
649 by Martin Pool
- some cleanups for the progressbar method
1
# Copyright (C) 2005 Aaron Bentley <aaron.bentley@utoronto.ca>
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
2
# Copyright (C) 2005, 2006, 2008 Canonical Ltd
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
3
#
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
648 by Martin Pool
- import aaron's progress-indicator code
17
649 by Martin Pool
- some cleanups for the progressbar method
18
3006.3.3 by Robert Collins
Docstring improvement and remove TODO's from progres.py.
19
"""Progress indicators.
20
21
The usual way to use this is via bzrlib.ui.ui_factory.nested_progress_bar which
22
will maintain a ProgressBarStack for you.
23
24
For direct use, the factory ProgressBar will return an auto-detected progress
25
bar that should match your terminal type. You can manually create a
26
ProgressBarStack too if you need multiple levels of cooperating progress bars.
27
Note that bzrlib's internal functions use the ui module, so if you are using
28
bzrlib it really is best to use bzrlib.ui.ui_factory.
649 by Martin Pool
- some cleanups for the progressbar method
29
"""
30
934 by Martin Pool
todo
31
648 by Martin Pool
- import aaron's progress-indicator code
32
import sys
660 by Martin Pool
- use plain unix time, not datetime module
33
import time
964 by Martin Pool
- show progress on dumb terminals by printing dots
34
import os
649 by Martin Pool
- some cleanups for the progressbar method
35
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
36
1996.3.32 by John Arbash Meinel
from bzrlib.ui lazy import progress, and make progress import lazily
37
from bzrlib import (
38
    errors,
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
39
    osutils,
40
    trace,
41
    ui,
1996.3.32 by John Arbash Meinel
from bzrlib.ui lazy import progress, and make progress import lazily
42
    )
1843.3.7 by John Arbash Meinel
new env var 'BZR_PROGRESS_BAR' to select the exact progress type
43
from bzrlib.trace import mutter
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
44
45
649 by Martin Pool
- some cleanups for the progressbar method
46
def _supports_progress(f):
2599.1.1 by Martin Pool
Don't show dots progress indicatiors in noninteractive mode
47
    """Detect if we can use pretty progress bars on the output stream f.
48
49
    If this returns true we expect that a human may be looking at that 
50
    output, and that we can repaint a line to update it.
51
    """
1843.3.7 by John Arbash Meinel
new env var 'BZR_PROGRESS_BAR' to select the exact progress type
52
    isatty = getattr(f, 'isatty', None)
53
    if isatty is None:
695 by Martin Pool
- don't display progress bars on really dumb terminals
54
        return False
1843.3.7 by John Arbash Meinel
new env var 'BZR_PROGRESS_BAR' to select the exact progress type
55
    if not isatty():
695 by Martin Pool
- don't display progress bars on really dumb terminals
56
        return False
57
    if os.environ.get('TERM') == 'dumb':
58
        # e.g. emacs compile window
59
        return False
60
    return True
649 by Martin Pool
- some cleanups for the progressbar method
61
62
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
63
class ProgressTask(object):
64
    """Model component of a progress indicator.
65
66
    Most code that needs to indicate progress should update one of these, 
67
    and it will in turn update the display, if one is present.
68
    """
69
70
    def __init__(self, parent_task=None):
71
        self._parent_task = parent_task
72
        self._last_update = 0
73
        self.total_cnt = None
74
        self.current_cnt = None
75
        self.msg = ''
76
77
    def update(self, msg, current_cnt=None, total_cnt=None):
78
        self.msg = msg
79
        self.current_cnt = current_cnt
80
        if total_cnt:
81
            self.total_cnt = total_cnt
82
        ui.ui_factory.show_progress(self)
83
84
    def finished(self):
85
        ui.ui_factory.progress_finished(self)
86
87
    def make_sub_task(self):
88
        return ProgressTask(parent_task=self)
89
90
    def _overall_completion_fraction(self, child_fraction=0.0):
91
        """Return fractional completion of this task and its parents
92
        
93
        Returns None if no completion can be computed."""
94
        if self.total_cnt:
95
            own_fraction = (float(self.current_cnt) + child_fraction) / self.total_cnt
96
        else:
97
            own_fraction = None
98
        if self._parent_task is None:
99
            return own_fraction
100
        else:
101
            if own_fraction is None:
102
                own_fraction = 0.0
103
            return self._parent_task._overall_completion_fraction(own_fraction)
104
105
    def note(self, fmt_string, *args, **kwargs):
106
        """Record a note without disrupting the progress bar."""
107
        # XXX: shouldn't be here; put it in mutter or the ui instead
108
        ui.ui_factory.clear_term()
109
        trace.note(fmt_string % args)
110
111
    def clear(self):
112
        # XXX: shouldn't be here; put it in mutter or the ui instead
113
        ui.ui_factory.clear_term()
1843.3.7 by John Arbash Meinel
new env var 'BZR_PROGRESS_BAR' to select the exact progress type
114
649 by Martin Pool
- some cleanups for the progressbar method
115
1681.1.2 by Robert Collins
* bzrlib.ui.text.TextUIFactory now accepts a bar_type parameter which
116
def ProgressBar(to_file=None, **kwargs):
964 by Martin Pool
- show progress on dumb terminals by printing dots
117
    """Abstract factory"""
1681.1.2 by Robert Collins
* bzrlib.ui.text.TextUIFactory now accepts a bar_type parameter which
118
    if to_file is None:
119
        to_file = sys.stderr
1843.3.7 by John Arbash Meinel
new env var 'BZR_PROGRESS_BAR' to select the exact progress type
120
    requested_bar_type = os.environ.get('BZR_PROGRESS_BAR')
121
    # An value of '' or not set reverts to standard processing
122
    if requested_bar_type in (None, ''):
123
        if _supports_progress(to_file):
124
            return TTYProgressBar(to_file=to_file, **kwargs)
125
        else:
2599.1.1 by Martin Pool
Don't show dots progress indicatiors in noninteractive mode
126
            return DummyProgress(to_file=to_file, **kwargs)
964 by Martin Pool
- show progress on dumb terminals by printing dots
127
    else:
1843.3.7 by John Arbash Meinel
new env var 'BZR_PROGRESS_BAR' to select the exact progress type
128
        # Minor sanitation to prevent spurious errors
129
        requested_bar_type = requested_bar_type.lower().strip()
130
        # TODO: jam 20060710 Arguably we shouldn't raise an exception
131
        #       but should instead just disable progress bars if we
132
        #       don't recognize the type
133
        if requested_bar_type not in _progress_bar_types:
134
            raise errors.InvalidProgressBarType(requested_bar_type,
135
                                                _progress_bar_types.keys())
136
        return _progress_bar_types[requested_bar_type](to_file=to_file, **kwargs)
137
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
138
139
140
class TextProgressView(object):
141
    """Display of progress bar and other information on a tty.
142
    
143
    This shows one line of text, including possibly a network indicator, spinner, 
144
    progress bar, message, etc.
145
146
    One instance of this is created and held by the UI, and fed updates when a
147
    task wants to be painted.
148
    """
149
150
    def __init__(self, term_file):
151
        self._term_file = term_file
152
        # true when there's output on the screen we may need to clear
153
        self._have_output = False
154
        self._width = osutils.terminal_width()
155
        self._last_transport_msg = ''
156
        self._last_task_msg = ''
157
        self._spin_pos = 0
158
        self._last_update = 0
159
        self._transport_update_time = 0
160
        self._task_fraction = None
161
162
    def _show_line(self, s):
163
        n = self._width - 1
164
        self._term_file.write('\r%-*.*s\r' % (n, n, s))
165
166
    def clear(self):
167
        if self._have_output:
168
            self._show_line('')
169
        self._have_output = False
170
171
    def _render_bar(self):
172
        # return a string for the progress bar itself
173
        if self._task_fraction is not None:
174
            cols = 20
175
            # number of markers highlighted in bar
176
            markers = int(round(float(cols) * self._task_fraction))
177
            bar_str = ' [' + ('#' * markers).ljust(cols) + '] '
178
            return bar_str
179
        else:
180
            return ''
181
182
    def _format_task(self, task):
183
        if task.total_cnt is not None:
184
            s = ' %d/%d' % (task.current_cnt, task.total_cnt)
185
        elif task.current_cnt is not None:
186
            s = ' %d' % (task.current_cnt)
187
        else:
188
            s = ''
189
        self._task_fraction = task._overall_completion_fraction()
190
        # compose all the parent messages
191
        t = task
192
        m = task.msg
193
        while t._parent_task:
194
            t = t._parent_task
195
            if t.msg:
196
                m = t.msg + ':' + m
197
        return m + s
198
199
    def _repaint(self):
200
        now = time.time()
201
        if now < self._last_update + 0.1:
202
            return
203
        if now > self._transport_update_time + 5:
204
            # no recent activity; expire it
205
            self._last_transport_msg = ''
206
        self._last_update = now
207
        spin_str =  r'/-\|'[self._spin_pos % 4]
208
        self._spin_pos += 1
209
        bar_string = self._render_bar()
210
        s = (self._last_transport_msg
211
             + ' ' + spin_str + ' ' +
212
             self._last_task_msg)
213
        s_max = self._width - 1 - len(bar_string)
214
        if len(s) > s_max:
215
            s = s[:s_max+1]
216
        elif len(s) < s_max:
217
            s = s.ljust(s_max)
218
        self._show_line(s + bar_string)
219
        self._have_output = True
220
221
    def show_progress(self, task):
222
        self._last_task_msg = self._format_task(task)
223
        self._repaint()
224
225
    def show_transport_activity(self, msg):
226
        self._last_transport_msg = msg
227
        self._transport_update_time = time.time()
228
        self._repaint()
229
230
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
231
class ProgressBarStack(object):
232
    """A stack of progress bars."""
233
234
    def __init__(self,
1681.1.2 by Robert Collins
* bzrlib.ui.text.TextUIFactory now accepts a bar_type parameter which
235
                 to_file=None,
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
236
                 show_pct=False,
1551.2.33 by Aaron Bentley
Hide ETA, show spinner by default
237
                 show_spinner=True,
238
                 show_eta=False,
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
239
                 show_bar=True,
240
                 show_count=True,
1681.1.2 by Robert Collins
* bzrlib.ui.text.TextUIFactory now accepts a bar_type parameter which
241
                 to_messages_file=None,
1594.1.3 by Robert Collins
Fixup pb usage to use nested_progress_bar.
242
                 klass=None):
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
243
        """Setup the stack with the parameters the progress bars should have."""
1681.1.2 by Robert Collins
* bzrlib.ui.text.TextUIFactory now accepts a bar_type parameter which
244
        if to_file is None:
245
            to_file = sys.stderr
246
        if to_messages_file is None:
247
            to_messages_file = sys.stdout
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
248
        self._to_file = to_file
249
        self._show_pct = show_pct
250
        self._show_spinner = show_spinner
251
        self._show_eta = show_eta
252
        self._show_bar = show_bar
253
        self._show_count = show_count
254
        self._to_messages_file = to_messages_file
255
        self._stack = []
1843.3.5 by John Arbash Meinel
Add tests to assert we fall back to DotsProgressBar when appropriate.
256
        self._klass = klass or ProgressBar
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
257
1551.2.29 by Aaron Bentley
Got stack handling under test
258
    def top(self):
259
        if len(self._stack) != 0:
260
            return self._stack[-1]
261
        else:
262
            return None
263
1558.8.1 by Aaron Bentley
Fix overall progress bar's interaction with 'note' and 'warning'
264
    def bottom(self):
265
        if len(self._stack) != 0:
266
            return self._stack[0]
267
        else:
268
            return None
269
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
270
    def get_nested(self):
271
        """Return a nested progress bar."""
1551.2.29 by Aaron Bentley
Got stack handling under test
272
        if len(self._stack) == 0:
273
            func = self._klass
274
        else:
275
            func = self.top().child_progress
276
        new_bar = func(to_file=self._to_file,
277
                       show_pct=self._show_pct,
278
                       show_spinner=self._show_spinner,
279
                       show_eta=self._show_eta,
280
                       show_bar=self._show_bar,
281
                       show_count=self._show_count,
282
                       to_messages_file=self._to_messages_file,
283
                       _stack=self)
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
284
        self._stack.append(new_bar)
285
        return new_bar
286
287
    def return_pb(self, bar):
288
        """Return bar after its been used."""
1594.1.4 by Robert Collins
Fix identity test in ProgressBarStack.return_pb
289
        if bar is not self._stack[-1]:
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
290
            raise errors.MissingProgressBarFinish()
291
        self._stack.pop()
292
293
 
964 by Martin Pool
- show progress on dumb terminals by printing dots
294
class _BaseProgressBar(object):
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
295
964 by Martin Pool
- show progress on dumb terminals by printing dots
296
    def __init__(self,
1681.1.2 by Robert Collins
* bzrlib.ui.text.TextUIFactory now accepts a bar_type parameter which
297
                 to_file=None,
964 by Martin Pool
- show progress on dumb terminals by printing dots
298
                 show_pct=False,
299
                 show_spinner=False,
1793.1.1 by Aaron Bentley
Hide TTYProgressBars unless they last more than 1 second
300
                 show_eta=False,
964 by Martin Pool
- show progress on dumb terminals by printing dots
301
                 show_bar=True,
1534.5.6 by Robert Collins
split out converter logic into per-format objects.
302
                 show_count=True,
1681.1.2 by Robert Collins
* bzrlib.ui.text.TextUIFactory now accepts a bar_type parameter which
303
                 to_messages_file=None,
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
304
                 _stack=None):
964 by Martin Pool
- show progress on dumb terminals by printing dots
305
        object.__init__(self)
1681.1.2 by Robert Collins
* bzrlib.ui.text.TextUIFactory now accepts a bar_type parameter which
306
        if to_file is None:
307
            to_file = sys.stderr
308
        if to_messages_file is None:
309
            to_messages_file = sys.stdout
964 by Martin Pool
- show progress on dumb terminals by printing dots
310
        self.to_file = to_file
1534.5.6 by Robert Collins
split out converter logic into per-format objects.
311
        self.to_messages_file = to_messages_file
964 by Martin Pool
- show progress on dumb terminals by printing dots
312
        self.last_msg = None
313
        self.last_cnt = None
314
        self.last_total = None
315
        self.show_pct = show_pct
316
        self.show_spinner = show_spinner
317
        self.show_eta = show_eta
318
        self.show_bar = show_bar
319
        self.show_count = show_count
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
320
        self._stack = _stack
1596.2.16 by Robert Collins
Microprofiling: progress.update was costing 0.01 ms per call in time.time.
321
        # seed throttler
322
        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
323
        now = time.time()
1596.2.16 by Robert Collins
Microprofiling: progress.update was costing 0.01 ms per call in time.time.
324
        # starting now
2745.6.52 by Andrew Bennetts
Revert bad change to bzrlib/progress.py
325
        self.start_time = now
1596.2.16 by Robert Collins
Microprofiling: progress.update was costing 0.01 ms per call in time.time.
326
        # next update should not throttle
327
        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.
328
329
    def finished(self):
330
        """Return this bar to its progress stack."""
331
        self.clear()
332
        self._stack.return_pb(self)
1104 by Martin Pool
- Add a simple UIFactory
333
1534.5.6 by Robert Collins
split out converter logic into per-format objects.
334
    def note(self, fmt_string, *args, **kwargs):
335
        """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
336
        self.clear()
1558.7.9 by Aaron Bentley
Bad change. (broke tests). Reverted.
337
        self.to_messages_file.write(fmt_string % args)
338
        self.to_messages_file.write('\n')
1104 by Martin Pool
- Add a simple UIFactory
339
1551.2.29 by Aaron Bentley
Got stack handling under test
340
    def child_progress(self, **kwargs):
341
        return ChildProgress(**kwargs)
342
1534.11.7 by Robert Collins
Test and correct the problem with nested test logs breaking further in-test logs.
343
1104 by Martin Pool
- Add a simple UIFactory
344
class DummyProgress(_BaseProgressBar):
345
    """Progress-bar standin that does nothing.
346
347
    This can be used as the default argument for methods that
348
    take an optional progress indicator."""
349
    def tick(self):
350
        pass
351
352
    def update(self, msg=None, current=None, total=None):
353
        pass
354
1551.2.27 by Aaron Bentley
Got propogation under test
355
    def child_update(self, message, current, total):
356
        pass
357
1104 by Martin Pool
- Add a simple UIFactory
358
    def clear(self):
359
        pass
964 by Martin Pool
- show progress on dumb terminals by printing dots
360
        
1534.5.6 by Robert Collins
split out converter logic into per-format objects.
361
    def note(self, fmt_string, *args, **kwargs):
362
        """See _BaseProgressBar.note()."""
1534.5.9 by Robert Collins
Advise users running upgrade on a checkout to also run it on the branch.
363
1551.2.29 by Aaron Bentley
Got stack handling under test
364
    def child_progress(self, **kwargs):
365
        return DummyProgress(**kwargs)
1534.5.9 by Robert Collins
Advise users running upgrade on a checkout to also run it on the branch.
366
1681.1.2 by Robert Collins
* bzrlib.ui.text.TextUIFactory now accepts a bar_type parameter which
367
964 by Martin Pool
- show progress on dumb terminals by printing dots
368
class DotsProgressBar(_BaseProgressBar):
1594.1.3 by Robert Collins
Fixup pb usage to use nested_progress_bar.
369
964 by Martin Pool
- show progress on dumb terminals by printing dots
370
    def __init__(self, **kwargs):
371
        _BaseProgressBar.__init__(self, **kwargs)
372
        self.last_msg = None
373
        self.need_nl = False
374
        
375
    def tick(self):
376
        self.update()
377
        
378
    def update(self, msg=None, current_cnt=None, total_cnt=None):
379
        if msg and msg != self.last_msg:
380
            if self.need_nl:
381
                self.to_file.write('\n')
382
            self.to_file.write(msg + ': ')
383
            self.last_msg = msg
384
        self.need_nl = True
385
        self.to_file.write('.')
386
        
387
    def clear(self):
388
        if self.need_nl:
389
            self.to_file.write('\n')
1681.1.2 by Robert Collins
* bzrlib.ui.text.TextUIFactory now accepts a bar_type parameter which
390
        self.need_nl = False
964 by Martin Pool
- show progress on dumb terminals by printing dots
391
        
1551.2.28 by Aaron Bentley
Ensure all ProgressBar implementations can be used as parents
392
    def child_update(self, message, current, total):
393
        self.tick()
1681.1.2 by Robert Collins
* bzrlib.ui.text.TextUIFactory now accepts a bar_type parameter which
394
1843.3.7 by John Arbash Meinel
new env var 'BZR_PROGRESS_BAR' to select the exact progress type
395
396
964 by Martin Pool
- show progress on dumb terminals by printing dots
397
    
398
class TTYProgressBar(_BaseProgressBar):
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
399
    """Progress bar display object.
400
401
    Several options are available to control the display.  These can
402
    be passed as parameters to the constructor or assigned at any time:
403
404
    show_pct
405
        Show percentage complete.
406
    show_spinner
407
        Show rotating baton.  This ticks over on every update even
408
        if the values don't change.
409
    show_eta
410
        Show predicted time-to-completion.
411
    show_bar
412
        Show bar graph.
413
    show_count
414
        Show numerical counts.
415
416
    The output file should be in line-buffered or unbuffered mode.
417
    """
418
    SPIN_CHARS = r'/-\|'
661 by Martin Pool
- limit rate at which progress bar is updated
419
964 by Martin Pool
- show progress on dumb terminals by printing dots
420
421
    def __init__(self, **kwargs):
1185.33.60 by Martin Pool
Use full terminal width for verbose test output.
422
        from bzrlib.osutils import terminal_width
964 by Martin Pool
- show progress on dumb terminals by printing dots
423
        _BaseProgressBar.__init__(self, **kwargs)
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
424
        self.spin_pos = 0
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
425
        # XXX: We could listen for SIGWINCH and update the terminal width...
1185.33.60 by Martin Pool
Use full terminal width for verbose test output.
426
        self.width = terminal_width()
1843.3.3 by John Arbash Meinel
Don't let the last_updates list grow without bound.
427
        self.last_updates = []
1843.3.4 by John Arbash Meinel
Remove get_eta's ability to modify last_updates.
428
        self._max_last_updates = 10
1551.2.28 by Aaron Bentley
Ensure all ProgressBar implementations can be used as parents
429
        self.child_fraction = 0
1843.3.1 by John Arbash Meinel
Don't clear anything if nothing has been written.
430
        self._have_output = False
3882.7.6 by Martin Pool
Preliminary support for drawing network io into the progress bar
431
        self._last_transport_msg = ''
964 by Martin Pool
- show progress on dumb terminals by printing dots
432
    
1793.1.1 by Aaron Bentley
Hide TTYProgressBars unless they last more than 1 second
433
    def throttle(self, old_msg):
964 by Martin Pool
- show progress on dumb terminals by printing dots
434
        """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.
435
        # 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
436
        # time.clock() is faster, but gives us CPU time, not wall-clock time
437
        now = time.time()
1793.1.1 by Aaron Bentley
Hide TTYProgressBars unless they last more than 1 second
438
        if self.start_time is not None and (now - self.start_time) < 1:
439
            return True
440
        if old_msg != self.last_msg:
441
            return False
1596.2.16 by Robert Collins
Microprofiling: progress.update was costing 0.01 ms per call in time.time.
442
        interval = now - self.last_update
443
        # if interval > 0
444
        if interval < self.MIN_PAUSE:
445
            return True
964 by Martin Pool
- show progress on dumb terminals by printing dots
446
1185.16.75 by Martin Pool
- improved eta estimation for progress bar
447
        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.
448
        # Don't let the queue grow without bound
449
        self.last_updates = self.last_updates[-self._max_last_updates:]
964 by Martin Pool
- show progress on dumb terminals by printing dots
450
        self.last_update = now
451
        return False
929 by Martin Pool
- progress bar: avoid repeatedly checking screen width
452
        
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
453
    def tick(self):
3006.3.2 by Robert Collins
More formatting corrections.
454
        self.update(self.last_msg, self.last_cnt, self.last_total,
1551.2.27 by Aaron Bentley
Got propogation under test
455
                    self.child_fraction)
456
1551.2.28 by Aaron Bentley
Ensure all ProgressBar implementations can be used as parents
457
    def child_update(self, message, current, total):
1551.2.35 by Aaron Bentley
Fix division-by-zero
458
        if current is not None and total != 0:
1551.2.30 by Aaron Bentley
Bugfixes to progress stuff
459
            child_fraction = float(current) / total
460
            if self.last_cnt is None:
461
                pass
462
            elif self.last_cnt + child_fraction <= self.last_total:
463
                self.child_fraction = child_fraction
464
        if self.last_msg is None:
465
            self.last_msg = ''
1551.2.28 by Aaron Bentley
Ensure all ProgressBar implementations can be used as parents
466
        self.tick()
467
3006.3.1 by Robert Collins
Minor PEP8 changes.
468
    def update(self, msg, current_cnt=None, total_cnt=None,
3882.7.6 by Martin Pool
Preliminary support for drawing network io into the progress bar
469
               child_fraction=0, transport_msg=None):
470
        """Update and redraw progress bar.
471
        
472
        :param transport_msg: A short message inserted into the string to 
473
            show the state of network activity.
474
            """
1534.11.1 by Robert Collins
Teach bzr selftest to use a progress bar in non verbose mode.
475
        if msg is None:
476
            msg = self.last_msg
477
478
        if total_cnt is None:
479
            total_cnt = self.last_total
480
1308 by Martin Pool
- make progress bar more tolerant of out-of-range values
481
        if current_cnt < 0:
482
            current_cnt = 0
483
            
484
        if current_cnt > total_cnt:
485
            total_cnt = current_cnt
1570.1.9 by Robert Collins
Do not throttle updates to progress bars that change the message.
486
        
1596.2.17 by Robert Collins
Notes on further progress tuning.
487
        ## # optional corner case optimisation 
488
        ## # currently does not seem to fire so costs more than saved.
489
        ## # trivial optimal case:
490
        ## # NB if callers are doing a clear and restore with
491
        ## # the saved values, this will prevent that:
492
        ## # in that case add a restore method that calls
493
        ## # _do_update or some such
494
        ## if (self.last_msg == msg and
495
        ##     self.last_cnt == current_cnt and
496
        ##     self.last_total == total_cnt and
497
        ##     self.child_fraction == child_fraction):
498
        ##     return
499
3882.7.6 by Martin Pool
Preliminary support for drawing network io into the progress bar
500
        if transport_msg is None:
501
            transport_msg = self._last_transport_msg
502
        if msg is None:
503
            msg = ''
504
1570.1.9 by Robert Collins
Do not throttle updates to progress bars that change the message.
505
        old_msg = self.last_msg
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
506
        # save these for the tick() function
507
        self.last_msg = msg
508
        self.last_cnt = current_cnt
509
        self.last_total = total_cnt
1596.2.17 by Robert Collins
Notes on further progress tuning.
510
        self.child_fraction = child_fraction
511
512
        # each function call takes 20ms/4000 = 0.005 ms, 
513
        # but multiple that by 4000 calls -> starts to cost.
514
        # so anything to make this function call faster
515
        # 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
516
        if self.throttle(old_msg):
1596.2.17 by Robert Collins
Notes on further progress tuning.
517
            return
518
519
        if self.show_eta and self.start_time and self.last_total:
520
            eta = get_eta(self.start_time, self.last_cnt + self.child_fraction, 
521
                    self.last_total, last_updates = self.last_updates)
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
522
            eta_str = " " + str_tdelta(eta)
523
        else:
524
            eta_str = ""
525
526
        if self.show_spinner:
527
            spin_str = self.SPIN_CHARS[self.spin_pos % 4] + ' '            
528
        else:
529
            spin_str = ''
530
531
        # always update this; it's also used for the bar
532
        self.spin_pos += 1
533
1596.2.17 by Robert Collins
Notes on further progress tuning.
534
        if self.show_pct and self.last_total and self.last_cnt:
535
            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
536
            pct_str = ' (%5.1f%%)' % pct
537
        else:
538
            pct_str = ''
539
540
        if not self.show_count:
541
            count_str = ''
1596.2.17 by Robert Collins
Notes on further progress tuning.
542
        elif self.last_cnt is None:
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
543
            count_str = ''
1596.2.17 by Robert Collins
Notes on further progress tuning.
544
        elif self.last_total is None:
545
            count_str = ' %i' % (self.last_cnt)
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
546
        else:
547
            # make both fields the same size
1596.2.17 by Robert Collins
Notes on further progress tuning.
548
            t = '%i' % (self.last_total)
549
            c = '%*i' % (len(t), self.last_cnt)
3882.7.6 by Martin Pool
Preliminary support for drawing network io into the progress bar
550
            count_str = ' ' + c + '/' + t
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
551
552
        if self.show_bar:
553
            # progress bar, if present, soaks up all remaining space
1596.2.17 by Robert Collins
Notes on further progress tuning.
554
            cols = self.width - 1 - len(self.last_msg) - len(spin_str) - len(pct_str) \
3882.7.6 by Martin Pool
Preliminary support for drawing network io into the progress bar
555
                   - len(eta_str) - len(count_str) - 3 - len(transport_msg)
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
556
1596.2.17 by Robert Collins
Notes on further progress tuning.
557
            if self.last_total:
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
558
                # number of markers highlighted in bar
1551.2.30 by Aaron Bentley
Bugfixes to progress stuff
559
                markers = int(round(float(cols) * 
1596.2.17 by Robert Collins
Notes on further progress tuning.
560
                              (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
561
                bar_str = '[' + ('=' * markers).ljust(cols) + '] '
669 by Martin Pool
- don't show progress bar unless completion is known
562
            elif False:
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
563
                # don't know total, so can't show completion.
564
                # so just show an expanded spinning thingy
565
                m = self.spin_pos % cols
668 by Martin Pool
- fix sweeping bar progress indicator
566
                ms = (' ' * m + '*').ljust(cols)
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
567
                
568
                bar_str = '[' + ms + '] '
669 by Martin Pool
- don't show progress bar unless completion is known
569
            else:
570
                bar_str = ''
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
571
        else:
572
            bar_str = ''
573
3882.7.6 by Martin Pool
Preliminary support for drawing network io into the progress bar
574
        m = spin_str + transport_msg + bar_str + self.last_msg + count_str \
575
            + pct_str + eta_str
2095.4.4 by mbp at sourcefrog
Truncate progress bar rather than complaining if it's too long
576
        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.
577
        self._have_output = True
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
578
        #self.to_file.flush()
579
            
3006.3.2 by Robert Collins
More formatting corrections.
580
    def clear(self):
1843.3.1 by John Arbash Meinel
Don't clear anything if nothing has been written.
581
        if self._have_output:
582
            self.to_file.write('\r%s\r' % (' ' * (self.width - 1)))
583
        self._have_output = False
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
584
        #self.to_file.flush()        
649 by Martin Pool
- some cleanups for the progressbar method
585
1551.2.27 by Aaron Bentley
Got propogation under test
586
1843.3.7 by John Arbash Meinel
new env var 'BZR_PROGRESS_BAR' to select the exact progress type
587
588
1551.2.28 by Aaron Bentley
Ensure all ProgressBar implementations can be used as parents
589
class ChildProgress(_BaseProgressBar):
1551.2.27 by Aaron Bentley
Got propogation under test
590
    """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
591
1551.2.29 by Aaron Bentley
Got stack handling under test
592
    def __init__(self, _stack, **kwargs):
593
        _BaseProgressBar.__init__(self, _stack=_stack, **kwargs)
594
        self.parent = _stack.top()
1551.2.27 by Aaron Bentley
Got propogation under test
595
        self.current = None
596
        self.total = None
597
        self.child_fraction = 0
598
        self.message = None
599
600
    def update(self, msg, current_cnt=None, total_cnt=None):
601
        self.current = current_cnt
2592.6.11 by Robert Collins
* A progress bar has been added for knitpack -> knitpack fetching.
602
        if total_cnt is not None:
603
            self.total = total_cnt
1551.2.27 by Aaron Bentley
Got propogation under test
604
        self.message = msg
605
        self.child_fraction = 0
606
        self.tick()
607
608
    def child_update(self, message, current, total):
1551.2.35 by Aaron Bentley
Fix division-by-zero
609
        if current is None or total == 0:
1551.2.30 by Aaron Bentley
Bugfixes to progress stuff
610
            self.child_fraction = 0
611
        else:
612
            self.child_fraction = float(current) / total
1551.2.27 by Aaron Bentley
Got propogation under test
613
        self.tick()
614
615
    def tick(self):
1551.2.30 by Aaron Bentley
Bugfixes to progress stuff
616
        if self.current is None:
617
            count = None
618
        else:
619
            count = self.current+self.child_fraction
620
            if count > self.total:
1596.2.35 by Robert Collins
Subclass SequenceMatcher to get a slightly faster (in our case) find_longest_match routine.
621
                if __debug__:
622
                    mutter('clamping count of %d to %d' % (count, self.total))
1551.2.30 by Aaron Bentley
Bugfixes to progress stuff
623
                count = self.total
1551.2.27 by Aaron Bentley
Got propogation under test
624
        self.parent.child_update(self.message, count, self.total)
625
1551.2.29 by Aaron Bentley
Got stack handling under test
626
    def clear(self):
1551.2.30 by Aaron Bentley
Bugfixes to progress stuff
627
        pass
1551.2.29 by Aaron Bentley
Got stack handling under test
628
1558.8.6 by Aaron Bentley
Fix note implementation
629
    def note(self, *args, **kwargs):
1558.8.5 by Aaron Bentley
Pass note up the stack instead of using bzrlib.ui_factory
630
        self.parent.note(*args, **kwargs)
631
3146.6.1 by Aaron Bentley
InterDifferingSerializer shows a progress bar
632
633
class InstrumentedProgress(TTYProgressBar):
634
    """TTYProgress variant that tracks outcomes"""
635
636
    def __init__(self, *args, **kwargs):
637
        self.always_throttled = True
638
        self.never_throttle = False
639
        TTYProgressBar.__init__(self, *args, **kwargs)
640
641
    def throttle(self, old_message):
642
        if self.never_throttle:
643
            result =  False
644
        else:
645
            result = TTYProgressBar.throttle(self, old_message)
646
        if result is False:
647
            self.always_throttled = False
648
649
648 by Martin Pool
- import aaron's progress-indicator code
650
def str_tdelta(delt):
651
    if delt is None:
652
        return "-:--:--"
660 by Martin Pool
- use plain unix time, not datetime module
653
    delt = int(round(delt))
654
    return '%d:%02d:%02d' % (delt/3600,
655
                             (delt/60) % 60,
656
                             delt % 60)
657
658
1185.16.75 by Martin Pool
- improved eta estimation for progress bar
659
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
660
    if start_time is None:
661
        return None
662
663
    if not total:
664
        return None
665
666
    if current < enough_samples:
667
        return None
668
669
    if current > total:
670
        return None                     # wtf?
671
2120.1.1 by John Arbash Meinel
Use time.time() because time.clock() is CPU time, not wall time
672
    elapsed = time.time() - start_time
660 by Martin Pool
- use plain unix time, not datetime module
673
674
    if elapsed < 2.0:                   # not enough time to estimate
675
        return None
676
    
677
    total_duration = float(elapsed) * float(total) / float(current)
678
1185.16.75 by Martin Pool
- improved eta estimation for progress bar
679
    if last_updates and len(last_updates) >= n_recent:
680
        avg = sum(last_updates) / float(len(last_updates))
681
        time_left = avg * (total - current)
682
683
        old_time_left = total_duration - elapsed
684
685
        # We could return the average, or some other value here
686
        return (time_left + old_time_left) / 2
687
660 by Martin Pool
- use plain unix time, not datetime module
688
    return total_duration - elapsed
648 by Martin Pool
- import aaron's progress-indicator code
689
649 by Martin Pool
- some cleanups for the progressbar method
690
1551.2.32 by Aaron Bentley
Handle progress phases more nicely in merge
691
class ProgressPhase(object):
692
    """Update progress object with the current phase"""
693
    def __init__(self, message, total, pb):
694
        object.__init__(self)
695
        self.pb = pb
696
        self.message = message
697
        self.total = total
698
        self.cur_phase = None
699
700
    def next_phase(self):
701
        if self.cur_phase is None:
702
            self.cur_phase = 0
703
        else:
704
            self.cur_phase += 1
705
        self.pb.update(self.message, self.cur_phase, self.total)
3882.7.7 by Martin Pool
Change progress bars to a more MVC style
706
707
708
_progress_bar_types = {}
709
_progress_bar_types['dummy'] = DummyProgress
710
_progress_bar_types['none'] = DummyProgress
711
_progress_bar_types['tty'] = TTYProgressBar
712
_progress_bar_types['dots'] = DotsProgressBar