~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/progress.py

  • Committer: John Arbash Meinel
  • Date: 2006-07-13 13:11:06 UTC
  • mto: This revision was merged to the branch mainline in revision 1869.
  • Revision ID: john@arbash-meinel.com-20060713131106-4f059a8003d852bd
Move offset_to_http_ranges back onto HttpTransportBase, clarify tests.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
# Copyright (C) 2005 Aaron Bentley <aaron.bentley@utoronto.ca>
2
 
# Copyright (C) 2005, 2006 Canonical Ltd
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
 
2
# Copyright (C) 2005, 2006 Canonical <canonical.com>
 
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
17
17
 
18
18
 
19
19
"""Simple text-mode progress indicator.
35
35
# TODO: Optionally show elapsed time instead/as well as ETA; nicer
36
36
# when the rate is unpredictable
37
37
 
 
38
 
38
39
import sys
39
40
import time
40
41
import os
41
 
 
42
 
from bzrlib.lazy_import import lazy_import
43
 
lazy_import(globals(), """
44
 
from bzrlib import (
45
 
    errors,
46
 
    )
47
 
""")
48
 
 
49
 
from bzrlib.trace import mutter
 
42
from collections import deque
 
43
 
 
44
 
 
45
import bzrlib.errors as errors
 
46
from bzrlib.trace import mutter 
50
47
 
51
48
 
52
49
def _supports_progress(f):
53
 
    """Detect if we can use pretty progress bars on the output stream f.
54
 
 
55
 
    If this returns true we expect that a human may be looking at that 
56
 
    output, and that we can repaint a line to update it.
57
 
    """
58
 
    isatty = getattr(f, 'isatty', None)
59
 
    if isatty is None:
 
50
    if not hasattr(f, 'isatty'):
60
51
        return False
61
 
    if not isatty():
 
52
    if not f.isatty():
62
53
        return False
63
54
    if os.environ.get('TERM') == 'dumb':
64
55
        # e.g. emacs compile window
66
57
    return True
67
58
 
68
59
 
69
 
_progress_bar_types = {}
70
 
 
71
60
 
72
61
def ProgressBar(to_file=None, **kwargs):
73
62
    """Abstract factory"""
74
63
    if to_file is None:
75
64
        to_file = sys.stderr
76
 
    requested_bar_type = os.environ.get('BZR_PROGRESS_BAR')
77
 
    # An value of '' or not set reverts to standard processing
78
 
    if requested_bar_type in (None, ''):
79
 
        if _supports_progress(to_file):
80
 
            return TTYProgressBar(to_file=to_file, **kwargs)
81
 
        else:
82
 
            return DummyProgress(to_file=to_file, **kwargs)
 
65
    if _supports_progress(to_file):
 
66
        return TTYProgressBar(to_file=to_file, **kwargs)
83
67
    else:
84
 
        # Minor sanitation to prevent spurious errors
85
 
        requested_bar_type = requested_bar_type.lower().strip()
86
 
        # TODO: jam 20060710 Arguably we shouldn't raise an exception
87
 
        #       but should instead just disable progress bars if we
88
 
        #       don't recognize the type
89
 
        if requested_bar_type not in _progress_bar_types:
90
 
            raise errors.InvalidProgressBarType(requested_bar_type,
91
 
                                                _progress_bar_types.keys())
92
 
        return _progress_bar_types[requested_bar_type](to_file=to_file, **kwargs)
93
 
 
 
68
        return DotsProgressBar(to_file=to_file, **kwargs)
 
69
    
94
70
 
95
71
class ProgressBarStack(object):
96
72
    """A stack of progress bars."""
117
93
        self._show_count = show_count
118
94
        self._to_messages_file = to_messages_file
119
95
        self._stack = []
120
 
        self._klass = klass or ProgressBar
 
96
        self._klass = klass or TTYProgressBar
121
97
 
122
98
    def top(self):
123
99
        if len(self._stack) != 0:
184
160
        self._stack = _stack
185
161
        # seed throttler
186
162
        self.MIN_PAUSE = 0.1 # seconds
187
 
        now = time.time()
 
163
        now = time.clock()
188
164
        # starting now
189
165
        self.start_time = now
190
166
        # next update should not throttle
230
206
        return DummyProgress(**kwargs)
231
207
 
232
208
 
233
 
_progress_bar_types['dummy'] = DummyProgress
234
 
_progress_bar_types['none'] = DummyProgress
235
 
 
236
 
 
237
209
class DotsProgressBar(_BaseProgressBar):
238
210
 
239
211
    def __init__(self, **kwargs):
261
233
    def child_update(self, message, current, total):
262
234
        self.tick()
263
235
 
264
 
 
265
 
_progress_bar_types['dots'] = DotsProgressBar
266
 
 
267
236
    
268
237
class TTYProgressBar(_BaseProgressBar):
269
238
    """Progress bar display object.
293
262
        _BaseProgressBar.__init__(self, **kwargs)
294
263
        self.spin_pos = 0
295
264
        self.width = terminal_width()
296
 
        self.last_updates = []
297
 
        self._max_last_updates = 10
 
265
        self.last_updates = deque()
298
266
        self.child_fraction = 0
299
 
        self._have_output = False
300
267
    
301
268
 
302
269
    def throttle(self, old_msg):
303
270
        """Return True if the bar was updated too recently"""
304
271
        # time.time consistently takes 40/4000 ms = 0.01 ms.
305
 
        # time.clock() is faster, but gives us CPU time, not wall-clock time
306
 
        now = time.time()
 
272
        # but every single update to the pb invokes it.
 
273
        # so we use time.clock which takes 20/4000 ms = 0.005ms
 
274
        # on the downside, time.clock() appears to have approximately
 
275
        # 10ms granularity, so we treat a zero-time change as 'throttled.'
 
276
        now = time.clock()
307
277
        if self.start_time is not None and (now - self.start_time) < 1:
308
278
            return True
309
279
        if old_msg != self.last_msg:
314
284
            return True
315
285
 
316
286
        self.last_updates.append(now - self.last_update)
317
 
        # Don't let the queue grow without bound
318
 
        self.last_updates = self.last_updates[-self._max_last_updates:]
319
287
        self.last_update = now
320
288
        return False
321
289
        
434
402
            bar_str = ''
435
403
 
436
404
        m = spin_str + bar_str + self.last_msg + count_str + pct_str + eta_str
437
 
        self.to_file.write('\r%-*.*s' % (self.width - 1, self.width - 1, m))
438
 
        self._have_output = True
 
405
 
 
406
        assert len(m) < self.width
 
407
        self.to_file.write('\r' + m.ljust(self.width - 1))
439
408
        #self.to_file.flush()
440
409
            
441
410
    def clear(self):        
442
 
        if self._have_output:
443
 
            self.to_file.write('\r%s\r' % (' ' * (self.width - 1)))
444
 
        self._have_output = False
 
411
        self.to_file.write('\r%s\r' % (' ' * (self.width - 1)))
445
412
        #self.to_file.flush()        
446
413
 
447
414
 
448
 
_progress_bar_types['tty'] = TTYProgressBar
449
 
 
450
 
 
451
415
class ChildProgress(_BaseProgressBar):
452
416
    """A progress indicator that pushes its data to the parent"""
453
417
 
513
477
    if current > total:
514
478
        return None                     # wtf?
515
479
 
516
 
    elapsed = time.time() - start_time
 
480
    elapsed = time.clock() - start_time
517
481
 
518
482
    if elapsed < 2.0:                   # not enough time to estimate
519
483
        return None
523
487
    assert total_duration >= elapsed
524
488
 
525
489
    if last_updates and len(last_updates) >= n_recent:
 
490
        while len(last_updates) > n_recent:
 
491
            last_updates.popleft()
526
492
        avg = sum(last_updates) / float(len(last_updates))
527
493
        time_left = avg * (total - current)
528
494