~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/progress.py

merge bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
39
39
import sys
40
40
import time
41
41
import os
42
 
from collections import deque
43
 
 
44
42
 
45
43
import bzrlib.errors as errors
46
 
from bzrlib.trace import mutter 
 
44
from bzrlib.trace import mutter
47
45
 
48
46
 
49
47
def _supports_progress(f):
50
 
    if not hasattr(f, 'isatty'):
 
48
    isatty = getattr(f, 'isatty', None)
 
49
    if isatty is None:
51
50
        return False
52
 
    if not f.isatty():
 
51
    if not isatty():
53
52
        return False
54
53
    if os.environ.get('TERM') == 'dumb':
55
54
        # e.g. emacs compile window
57
56
    return True
58
57
 
59
58
 
 
59
_progress_bar_types = {}
 
60
 
60
61
 
61
62
def ProgressBar(to_file=None, **kwargs):
62
63
    """Abstract factory"""
63
64
    if to_file is None:
64
65
        to_file = sys.stderr
65
 
    if _supports_progress(to_file):
66
 
        return TTYProgressBar(to_file=to_file, **kwargs)
 
66
    requested_bar_type = os.environ.get('BZR_PROGRESS_BAR')
 
67
    # An value of '' or not set reverts to standard processing
 
68
    if requested_bar_type in (None, ''):
 
69
        if _supports_progress(to_file):
 
70
            return TTYProgressBar(to_file=to_file, **kwargs)
 
71
        else:
 
72
            return DotsProgressBar(to_file=to_file, **kwargs)
67
73
    else:
68
 
        return DotsProgressBar(to_file=to_file, **kwargs)
69
 
    
 
74
        # Minor sanitation to prevent spurious errors
 
75
        requested_bar_type = requested_bar_type.lower().strip()
 
76
        # TODO: jam 20060710 Arguably we shouldn't raise an exception
 
77
        #       but should instead just disable progress bars if we
 
78
        #       don't recognize the type
 
79
        if requested_bar_type not in _progress_bar_types:
 
80
            raise errors.InvalidProgressBarType(requested_bar_type,
 
81
                                                _progress_bar_types.keys())
 
82
        return _progress_bar_types[requested_bar_type](to_file=to_file, **kwargs)
 
83
 
70
84
 
71
85
class ProgressBarStack(object):
72
86
    """A stack of progress bars."""
93
107
        self._show_count = show_count
94
108
        self._to_messages_file = to_messages_file
95
109
        self._stack = []
96
 
        self._klass = klass or TTYProgressBar
 
110
        self._klass = klass or ProgressBar
97
111
 
98
112
    def top(self):
99
113
        if len(self._stack) != 0:
137
151
                 to_file=None,
138
152
                 show_pct=False,
139
153
                 show_spinner=False,
140
 
                 show_eta=True,
 
154
                 show_eta=False,
141
155
                 show_bar=True,
142
156
                 show_count=True,
143
157
                 to_messages_file=None,
206
220
        return DummyProgress(**kwargs)
207
221
 
208
222
 
 
223
_progress_bar_types['dummy'] = DummyProgress
 
224
_progress_bar_types['none'] = DummyProgress
 
225
 
 
226
 
209
227
class DotsProgressBar(_BaseProgressBar):
210
228
 
211
229
    def __init__(self, **kwargs):
233
251
    def child_update(self, message, current, total):
234
252
        self.tick()
235
253
 
 
254
 
 
255
_progress_bar_types['dots'] = DotsProgressBar
 
256
 
236
257
    
237
258
class TTYProgressBar(_BaseProgressBar):
238
259
    """Progress bar display object.
262
283
        _BaseProgressBar.__init__(self, **kwargs)
263
284
        self.spin_pos = 0
264
285
        self.width = terminal_width()
265
 
        self.start_time = None
266
 
        self.last_updates = deque()
 
286
        self.last_updates = []
 
287
        self._max_last_updates = 10
267
288
        self.child_fraction = 0
 
289
        self._have_output = False
268
290
    
269
291
 
270
 
    def throttle(self):
 
292
    def throttle(self, old_msg):
271
293
        """Return True if the bar was updated too recently"""
272
294
        # time.time consistently takes 40/4000 ms = 0.01 ms.
273
295
        # but every single update to the pb invokes it.
274
296
        # so we use time.clock which takes 20/4000 ms = 0.005ms
275
297
        # on the downside, time.clock() appears to have approximately
276
298
        # 10ms granularity, so we treat a zero-time change as 'throttled.'
277
 
        
278
299
        now = time.clock()
 
300
        if self.start_time is not None and (now - self.start_time) < 1:
 
301
            return True
 
302
        if old_msg != self.last_msg:
 
303
            return False
279
304
        interval = now - self.last_update
280
305
        # if interval > 0
281
306
        if interval < self.MIN_PAUSE:
282
307
            return True
283
308
 
284
309
        self.last_updates.append(now - self.last_update)
 
310
        # Don't let the queue grow without bound
 
311
        self.last_updates = self.last_updates[-self._max_last_updates:]
285
312
        self.last_update = now
286
313
        return False
287
314
        
341
368
        # but multiple that by 4000 calls -> starts to cost.
342
369
        # so anything to make this function call faster
343
370
        # will improve base 'diff' time by up to 0.1 seconds.
344
 
        if old_msg == self.last_msg and self.throttle():
 
371
        if self.throttle(old_msg):
345
372
            return
346
373
 
347
374
        if self.show_eta and self.start_time and self.last_total:
403
430
 
404
431
        assert len(m) < self.width
405
432
        self.to_file.write('\r' + m.ljust(self.width - 1))
 
433
        self._have_output = True
406
434
        #self.to_file.flush()
407
435
            
408
436
    def clear(self):        
409
 
        self.to_file.write('\r%s\r' % (' ' * (self.width - 1)))
 
437
        if self._have_output:
 
438
            self.to_file.write('\r%s\r' % (' ' * (self.width - 1)))
 
439
        self._have_output = False
410
440
        #self.to_file.flush()        
411
441
 
412
442
 
 
443
_progress_bar_types['tty'] = TTYProgressBar
 
444
 
 
445
 
413
446
class ChildProgress(_BaseProgressBar):
414
447
    """A progress indicator that pushes its data to the parent"""
415
448
 
485
518
    assert total_duration >= elapsed
486
519
 
487
520
    if last_updates and len(last_updates) >= n_recent:
488
 
        while len(last_updates) > n_recent:
489
 
            last_updates.popleft()
490
521
        avg = sum(last_updates) / float(len(last_updates))
491
522
        time_left = avg * (total - current)
492
523