~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/progress.py

  • Committer: Martin Pool
  • Date: 2006-05-17 04:01:38 UTC
  • mto: (1704.2.17 bzr.mbp.integration)
  • mto: This revision was merged to the branch mainline in revision 1710.
  • Revision ID: mbp@sourcefrog.net-20060517040138-2cdf4e74bbc40afc
Fix setup.py to install launchpad plugin

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