~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/progress.py

  • Committer: Aaron Bentley
  • Date: 2006-06-21 14:30:57 UTC
  • mfrom: (1801.1.1 bzr.dev)
  • mto: This revision was merged to the branch mainline in revision 1803.
  • Revision ID: abentley@panoramicfeedback.com-20060621143057-776e4b8d707e430e
Install benchmarks. (Jelmer Vernooij)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2008, 2009 Canonical Ltd
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
15
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
 
 
17
 
 
18
 
"""Progress indicators.
19
 
 
20
 
The usual way to use this is via bzrlib.ui.ui_factory.nested_progress_bar which
21
 
will manage a conceptual stack of nested activities.
 
1
# Copyright (C) 2005 Aaron Bentley <aaron.bentley@utoronto.ca>
 
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
 
 
18
 
 
19
"""Simple text-mode progress indicator.
 
20
 
 
21
To display an indicator, create a ProgressBar object.  Call it,
 
22
passing Progress objects indicating the current state.  When done,
 
23
call clear().
 
24
 
 
25
Progress is suppressed when output is not sent to a terminal, so as
 
26
not to clutter log files.
22
27
"""
23
28
 
 
29
# TODO: should be a global option e.g. --silent that disables progress
 
30
# indicators, preferably without needing to adjust all code that
 
31
# potentially calls them.
 
32
 
 
33
# TODO: If not on a tty perhaps just print '......' for the benefit of IDEs, etc
 
34
 
 
35
# TODO: Optionally show elapsed time instead/as well as ETA; nicer
 
36
# when the rate is unpredictable
 
37
 
24
38
 
25
39
import sys
26
40
import time
27
41
import os
28
 
 
29
 
 
30
 
from bzrlib import (
31
 
    errors,
32
 
    )
33
 
from bzrlib.trace import mutter
34
 
from bzrlib.symbol_versioning import (
35
 
    deprecated_function,
36
 
    deprecated_in,
37
 
    )
 
42
from collections import deque
 
43
 
 
44
 
 
45
import bzrlib.errors as errors
 
46
from bzrlib.trace import mutter 
38
47
 
39
48
 
40
49
def _supports_progress(f):
41
 
    """Detect if we can use pretty progress bars on file F.
42
 
 
43
 
    If this returns true we expect that a human may be looking at that
44
 
    output, and that we can repaint a line to update it.
45
 
 
46
 
    This doesn't check the policy for whether we *should* use them.
47
 
    """
48
 
    isatty = getattr(f, 'isatty', None)
49
 
    if isatty is None:
50
 
        return False
51
 
    if not isatty():
52
 
        return False
53
 
    # The following case also handles Win32 - on that platform $TERM is
54
 
    # typically never set, so the case None is treated as a smart terminal,
55
 
    # not dumb.  <https://bugs.launchpad.net/bugs/334808>  win32 files do have
56
 
    # isatty methods that return true.
 
50
    if not hasattr(f, 'isatty'):
 
51
        return False
 
52
    if not f.isatty():
 
53
        return False
57
54
    if os.environ.get('TERM') == 'dumb':
58
55
        # e.g. emacs compile window
59
56
        return False
60
57
    return True
61
58
 
62
59
 
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
 
    Code updating the task may also set fields as hints about how to display
70
 
    it: show_pct, show_spinner, show_eta, show_count, show_bar.  UIs
71
 
    will not necessarily respect all these fields.
72
 
    
73
 
    :ivar update_latency: The interval (in seconds) at which the PB should be
74
 
        updated.  Setting this to zero suggests every update should be shown
75
 
        synchronously.
76
 
 
77
 
    :ivar show_transport_activity: If true (default), transport activity
78
 
        will be shown when this task is drawn.  Disable it if you're sure 
79
 
        that only irrelevant or uninteresting transport activity can occur
80
 
        during this task.
81
 
    """
82
 
 
83
 
    def __init__(self, parent_task=None, ui_factory=None, progress_view=None):
84
 
        """Construct a new progress task.
85
 
 
86
 
        :param parent_task: Enclosing ProgressTask or None.
87
 
 
88
 
        :param progress_view: ProgressView to display this ProgressTask.
89
 
 
90
 
        :param ui_factory: The UI factory that will display updates; 
91
 
            deprecated in favor of passing progress_view directly.
92
 
 
93
 
        Normally you should not call this directly but rather through
94
 
        `ui_factory.nested_progress_bar`.
95
 
        """
96
 
        self._parent_task = parent_task
97
 
        self._last_update = 0
98
 
        self.total_cnt = None
99
 
        self.current_cnt = None
100
 
        self.msg = ''
101
 
        # TODO: deprecate passing ui_factory
102
 
        self.ui_factory = ui_factory
103
 
        self.progress_view = progress_view
104
 
        self.show_pct = False
105
 
        self.show_spinner = True
106
 
        self.show_eta = False,
107
 
        self.show_count = True
108
 
        self.show_bar = True
109
 
        self.update_latency = 0.1
110
 
        self.show_transport_activity = True
111
 
 
112
 
    def __repr__(self):
113
 
        return '%s(%r/%r, msg=%r)' % (
114
 
            self.__class__.__name__,
115
 
            self.current_cnt,
116
 
            self.total_cnt,
117
 
            self.msg)
118
 
 
119
 
    def update(self, msg, current_cnt=None, total_cnt=None):
120
 
        self.msg = msg
121
 
        self.current_cnt = current_cnt
122
 
        if total_cnt:
123
 
            self.total_cnt = total_cnt
124
 
        if self.progress_view:
125
 
            self.progress_view.show_progress(self)
126
 
        else:
127
 
            self.ui_factory._progress_updated(self)
128
 
 
129
 
    def tick(self):
130
 
        self.update(self.msg)
131
 
 
132
 
    def finished(self):
133
 
        if self.progress_view:
134
 
            self.progress_view.task_finished(self)
135
 
        else:
136
 
            self.ui_factory._progress_finished(self)
137
 
 
138
 
    def make_sub_task(self):
139
 
        return ProgressTask(self, ui_factory=self.ui_factory,
140
 
            progress_view=self.progress_view)
141
 
 
142
 
    def _overall_completion_fraction(self, child_fraction=0.0):
143
 
        """Return fractional completion of this task and its parents
144
 
 
145
 
        Returns None if no completion can be computed."""
146
 
        if self.current_cnt is not None and self.total_cnt:
147
 
            own_fraction = (float(self.current_cnt) + child_fraction) / self.total_cnt
148
 
        else:
149
 
            # if this task has no estimation, it just passes on directly
150
 
            # whatever the child has measured...
151
 
            own_fraction = child_fraction
152
 
        if self._parent_task is None:
153
 
            return own_fraction
154
 
        else:
155
 
            if own_fraction is None:
156
 
                own_fraction = 0.0
157
 
            return self._parent_task._overall_completion_fraction(own_fraction)
158
 
 
159
 
    def note(self, fmt_string, *args):
160
 
        """Record a note without disrupting the progress bar."""
161
 
        # XXX: shouldn't be here; put it in mutter or the ui instead
162
 
        if args:
163
 
            self.ui_factory.note(fmt_string % args)
164
 
        else:
165
 
            self.ui_factory.note(fmt_string)
166
 
 
167
 
    def clear(self):
168
 
        # XXX: shouldn't be here; put it in mutter or the ui instead
169
 
        if self.progress_view:
170
 
            self.progress_view.clear()
171
 
        else:
172
 
            self.ui_factory.clear_term()
173
 
 
174
 
 
175
 
@deprecated_function(deprecated_in((1, 16, 0)))
 
60
 
176
61
def ProgressBar(to_file=None, **kwargs):
177
 
    """Construct a progress bar.
178
 
 
179
 
    Deprecated; ask the ui_factory for a progress task instead.
180
 
    """
 
62
    """Abstract factory"""
181
63
    if to_file is None:
182
64
        to_file = sys.stderr
183
 
    requested_bar_type = os.environ.get('BZR_PROGRESS_BAR')
184
 
    # An value of '' or not set reverts to standard processing
185
 
    if requested_bar_type in (None, ''):
186
 
        if _supports_progress(to_file):
187
 
            return TTYProgressBar(to_file=to_file, **kwargs)
188
 
        else:
189
 
            return DummyProgress(to_file=to_file, **kwargs)
 
65
    if _supports_progress(to_file):
 
66
        return TTYProgressBar(to_file=to_file, **kwargs)
190
67
    else:
191
 
        # Minor sanitation to prevent spurious errors
192
 
        requested_bar_type = requested_bar_type.lower().strip()
193
 
        # TODO: jam 20060710 Arguably we shouldn't raise an exception
194
 
        #       but should instead just disable progress bars if we
195
 
        #       don't recognize the type
196
 
        if requested_bar_type not in _progress_bar_types:
197
 
            raise errors.InvalidProgressBarType(requested_bar_type,
198
 
                                                _progress_bar_types.keys())
199
 
        return _progress_bar_types[requested_bar_type](to_file=to_file, **kwargs)
200
 
 
201
 
 
202
 
# NOTE: This is also deprecated; you should provide a ProgressView instead.
 
68
        return DotsProgressBar(to_file=to_file, **kwargs)
 
69
    
 
70
 
 
71
class ProgressBarStack(object):
 
72
    """A stack of progress bars."""
 
73
 
 
74
    def __init__(self,
 
75
                 to_file=None,
 
76
                 show_pct=False,
 
77
                 show_spinner=True,
 
78
                 show_eta=False,
 
79
                 show_bar=True,
 
80
                 show_count=True,
 
81
                 to_messages_file=None,
 
82
                 klass=None):
 
83
        """Setup the stack with the parameters the progress bars should have."""
 
84
        if to_file is None:
 
85
            to_file = sys.stderr
 
86
        if to_messages_file is None:
 
87
            to_messages_file = sys.stdout
 
88
        self._to_file = to_file
 
89
        self._show_pct = show_pct
 
90
        self._show_spinner = show_spinner
 
91
        self._show_eta = show_eta
 
92
        self._show_bar = show_bar
 
93
        self._show_count = show_count
 
94
        self._to_messages_file = to_messages_file
 
95
        self._stack = []
 
96
        self._klass = klass or TTYProgressBar
 
97
 
 
98
    def top(self):
 
99
        if len(self._stack) != 0:
 
100
            return self._stack[-1]
 
101
        else:
 
102
            return None
 
103
 
 
104
    def bottom(self):
 
105
        if len(self._stack) != 0:
 
106
            return self._stack[0]
 
107
        else:
 
108
            return None
 
109
 
 
110
    def get_nested(self):
 
111
        """Return a nested progress bar."""
 
112
        if len(self._stack) == 0:
 
113
            func = self._klass
 
114
        else:
 
115
            func = self.top().child_progress
 
116
        new_bar = func(to_file=self._to_file,
 
117
                       show_pct=self._show_pct,
 
118
                       show_spinner=self._show_spinner,
 
119
                       show_eta=self._show_eta,
 
120
                       show_bar=self._show_bar,
 
121
                       show_count=self._show_count,
 
122
                       to_messages_file=self._to_messages_file,
 
123
                       _stack=self)
 
124
        self._stack.append(new_bar)
 
125
        return new_bar
 
126
 
 
127
    def return_pb(self, bar):
 
128
        """Return bar after its been used."""
 
129
        if bar is not self._stack[-1]:
 
130
            raise errors.MissingProgressBarFinish()
 
131
        self._stack.pop()
 
132
 
 
133
 
203
134
class _BaseProgressBar(object):
204
135
 
205
136
    def __init__(self,
229
160
        self._stack = _stack
230
161
        # seed throttler
231
162
        self.MIN_PAUSE = 0.1 # seconds
232
 
        now = time.time()
 
163
        now = time.clock()
233
164
        # starting now
234
165
        self.start_time = now
235
166
        # next update should not throttle
238
169
    def finished(self):
239
170
        """Return this bar to its progress stack."""
240
171
        self.clear()
 
172
        assert self._stack is not None
241
173
        self._stack.return_pb(self)
242
174
 
243
175
    def note(self, fmt_string, *args, **kwargs):
246
178
        self.to_messages_file.write(fmt_string % args)
247
179
        self.to_messages_file.write('\n')
248
180
 
249
 
    @deprecated_function(deprecated_in((1, 16, 0)))
250
181
    def child_progress(self, **kwargs):
251
182
        return ChildProgress(**kwargs)
252
183
 
256
187
 
257
188
    This can be used as the default argument for methods that
258
189
    take an optional progress indicator."""
259
 
 
260
190
    def tick(self):
261
191
        pass
262
192
 
268
198
 
269
199
    def clear(self):
270
200
        pass
271
 
 
 
201
        
272
202
    def note(self, fmt_string, *args, **kwargs):
273
203
        """See _BaseProgressBar.note()."""
274
204
 
278
208
 
279
209
class DotsProgressBar(_BaseProgressBar):
280
210
 
281
 
    @deprecated_function(deprecated_in((1, 16, 0)))
282
211
    def __init__(self, **kwargs):
283
212
        _BaseProgressBar.__init__(self, **kwargs)
284
213
        self.last_msg = None
285
214
        self.need_nl = False
286
 
 
 
215
        
287
216
    def tick(self):
288
217
        self.update()
289
 
 
 
218
        
290
219
    def update(self, msg=None, current_cnt=None, total_cnt=None):
291
220
        if msg and msg != self.last_msg:
292
221
            if self.need_nl:
295
224
            self.last_msg = msg
296
225
        self.need_nl = True
297
226
        self.to_file.write('.')
298
 
 
 
227
        
299
228
    def clear(self):
300
229
        if self.need_nl:
301
230
            self.to_file.write('\n')
302
231
        self.need_nl = False
303
 
 
 
232
        
304
233
    def child_update(self, message, current, total):
305
234
        self.tick()
306
235
 
307
 
 
 
236
    
308
237
class TTYProgressBar(_BaseProgressBar):
309
238
    """Progress bar display object.
310
239
 
327
256
    """
328
257
    SPIN_CHARS = r'/-\|'
329
258
 
330
 
    @deprecated_function(deprecated_in((1, 16, 0)))
 
259
 
331
260
    def __init__(self, **kwargs):
332
261
        from bzrlib.osutils import terminal_width
333
262
        _BaseProgressBar.__init__(self, **kwargs)
334
263
        self.spin_pos = 0
335
264
        self.width = terminal_width()
336
 
        self.last_updates = []
337
 
        self._max_last_updates = 10
 
265
        self.last_updates = deque()
338
266
        self.child_fraction = 0
339
 
        self._have_output = False
 
267
    
340
268
 
341
269
    def throttle(self, old_msg):
342
270
        """Return True if the bar was updated too recently"""
343
271
        # time.time consistently takes 40/4000 ms = 0.01 ms.
344
 
        # time.clock() is faster, but gives us CPU time, not wall-clock time
345
 
        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()
346
277
        if self.start_time is not None and (now - self.start_time) < 1:
347
278
            return True
348
279
        if old_msg != self.last_msg:
353
284
            return True
354
285
 
355
286
        self.last_updates.append(now - self.last_update)
356
 
        # Don't let the queue grow without bound
357
 
        self.last_updates = self.last_updates[-self._max_last_updates:]
358
287
        self.last_update = now
359
288
        return False
360
 
 
 
289
        
361
290
    def tick(self):
362
 
        self.update(self.last_msg, self.last_cnt, self.last_total,
 
291
        self.update(self.last_msg, self.last_cnt, self.last_total, 
363
292
                    self.child_fraction)
364
293
 
365
294
    def child_update(self, message, current, total):
369
298
                pass
370
299
            elif self.last_cnt + child_fraction <= self.last_total:
371
300
                self.child_fraction = child_fraction
 
301
            else:
 
302
                mutter('not updating child fraction')
372
303
        if self.last_msg is None:
373
304
            self.last_msg = ''
374
305
        self.tick()
375
306
 
376
 
    def update(self, msg, current_cnt=None, total_cnt=None,
377
 
            child_fraction=0):
378
 
        """Update and redraw progress bar.
379
 
        """
 
307
    def update(self, msg, current_cnt=None, total_cnt=None, 
 
308
               child_fraction=0):
 
309
        """Update and redraw progress bar."""
380
310
        if msg is None:
381
311
            msg = self.last_msg
382
312
 
385
315
 
386
316
        if current_cnt < 0:
387
317
            current_cnt = 0
388
 
 
 
318
            
389
319
        if current_cnt > total_cnt:
390
320
            total_cnt = current_cnt
391
 
 
392
 
        ## # optional corner case optimisation
 
321
        
 
322
        ## # optional corner case optimisation 
393
323
        ## # currently does not seem to fire so costs more than saved.
394
324
        ## # trivial optimal case:
395
325
        ## # NB if callers are doing a clear and restore with
402
332
        ##     self.child_fraction == child_fraction):
403
333
        ##     return
404
334
 
405
 
        if msg is None:
406
 
            msg = ''
407
 
 
408
335
        old_msg = self.last_msg
409
336
        # save these for the tick() function
410
337
        self.last_msg = msg
412
339
        self.last_total = total_cnt
413
340
        self.child_fraction = child_fraction
414
341
 
415
 
        # each function call takes 20ms/4000 = 0.005 ms,
 
342
        # each function call takes 20ms/4000 = 0.005 ms, 
416
343
        # but multiple that by 4000 calls -> starts to cost.
417
344
        # so anything to make this function call faster
418
345
        # will improve base 'diff' time by up to 0.1 seconds.
420
347
            return
421
348
 
422
349
        if self.show_eta and self.start_time and self.last_total:
423
 
            eta = get_eta(self.start_time, self.last_cnt + self.child_fraction,
 
350
            eta = get_eta(self.start_time, self.last_cnt + self.child_fraction, 
424
351
                    self.last_total, last_updates = self.last_updates)
425
352
            eta_str = " " + str_tdelta(eta)
426
353
        else:
427
354
            eta_str = ""
428
355
 
429
356
        if self.show_spinner:
430
 
            spin_str = self.SPIN_CHARS[self.spin_pos % 4] + ' '
 
357
            spin_str = self.SPIN_CHARS[self.spin_pos % 4] + ' '            
431
358
        else:
432
359
            spin_str = ''
433
360
 
450
377
            # make both fields the same size
451
378
            t = '%i' % (self.last_total)
452
379
            c = '%*i' % (len(t), self.last_cnt)
453
 
            count_str = ' ' + c + '/' + t
 
380
            count_str = ' ' + c + '/' + t 
454
381
 
455
382
        if self.show_bar:
456
383
            # progress bar, if present, soaks up all remaining space
459
386
 
460
387
            if self.last_total:
461
388
                # number of markers highlighted in bar
462
 
                markers = int(round(float(cols) *
 
389
                markers = int(round(float(cols) * 
463
390
                              (self.last_cnt + self.child_fraction) / self.last_total))
464
391
                bar_str = '[' + ('=' * markers).ljust(cols) + '] '
465
392
            elif False:
467
394
                # so just show an expanded spinning thingy
468
395
                m = self.spin_pos % cols
469
396
                ms = (' ' * m + '*').ljust(cols)
470
 
 
 
397
                
471
398
                bar_str = '[' + ms + '] '
472
399
            else:
473
400
                bar_str = ''
474
401
        else:
475
402
            bar_str = ''
476
403
 
477
 
        m = spin_str + bar_str + self.last_msg + count_str \
478
 
            + pct_str + eta_str
479
 
        self.to_file.write('\r%-*.*s' % (self.width - 1, self.width - 1, m))
480
 
        self._have_output = True
481
 
        #self.to_file.flush()
482
 
 
483
 
    def clear(self):
484
 
        if self._have_output:
485
 
            self.to_file.write('\r%s\r' % (' ' * (self.width - 1)))
486
 
        self._have_output = False
487
 
        #self.to_file.flush()
488
 
 
489
 
 
490
 
 
491
 
# DEPRECATED
 
404
        m = spin_str + bar_str + self.last_msg + count_str + pct_str + eta_str
 
405
 
 
406
        assert len(m) < self.width
 
407
        self.to_file.write('\r' + m.ljust(self.width - 1))
 
408
        #self.to_file.flush()
 
409
            
 
410
    def clear(self):        
 
411
        self.to_file.write('\r%s\r' % (' ' * (self.width - 1)))
 
412
        #self.to_file.flush()        
 
413
 
 
414
 
492
415
class ChildProgress(_BaseProgressBar):
493
416
    """A progress indicator that pushes its data to the parent"""
494
417
 
495
 
    @deprecated_function(deprecated_in((1, 16, 0)))
496
418
    def __init__(self, _stack, **kwargs):
497
419
        _BaseProgressBar.__init__(self, _stack=_stack, **kwargs)
498
420
        self.parent = _stack.top()
503
425
 
504
426
    def update(self, msg, current_cnt=None, total_cnt=None):
505
427
        self.current = current_cnt
506
 
        if total_cnt is not None:
507
 
            self.total = total_cnt
 
428
        self.total = total_cnt
508
429
        self.message = msg
509
430
        self.child_fraction = 0
510
431
        self.tick()
533
454
    def note(self, *args, **kwargs):
534
455
        self.parent.note(*args, **kwargs)
535
456
 
536
 
 
 
457
 
537
458
def str_tdelta(delt):
538
459
    if delt is None:
539
460
        return "-:--:--"
556
477
    if current > total:
557
478
        return None                     # wtf?
558
479
 
559
 
    elapsed = time.time() - start_time
 
480
    elapsed = time.clock() - start_time
560
481
 
561
482
    if elapsed < 2.0:                   # not enough time to estimate
562
483
        return None
563
 
 
 
484
    
564
485
    total_duration = float(elapsed) * float(total) / float(current)
565
486
 
 
487
    assert total_duration >= elapsed
 
488
 
566
489
    if last_updates and len(last_updates) >= n_recent:
 
490
        while len(last_updates) > n_recent:
 
491
            last_updates.popleft()
567
492
        avg = sum(last_updates) / float(len(last_updates))
568
493
        time_left = avg * (total - current)
569
494
 
589
514
            self.cur_phase = 0
590
515
        else:
591
516
            self.cur_phase += 1
 
517
        assert self.cur_phase < self.total 
592
518
        self.pb.update(self.message, self.cur_phase, self.total)
593
519
 
594
520
 
595
 
_progress_bar_types = {}
596
 
_progress_bar_types['dummy'] = DummyProgress
597
 
_progress_bar_types['none'] = DummyProgress
598
 
_progress_bar_types['tty'] = TTYProgressBar
599
 
_progress_bar_types['dots'] = DotsProgressBar
 
521
def run_tests():
 
522
    import doctest
 
523
    result = doctest.testmod()
 
524
    if result[1] > 0:
 
525
        if result[0] == 0:
 
526
            print "All tests passed"
 
527
    else:
 
528
        print "No tests to run"
 
529
 
 
530
 
 
531
def demo():
 
532
    sleep = time.sleep
 
533
    
 
534
    print 'dumb-terminal test:'
 
535
    pb = DotsProgressBar()
 
536
    for i in range(100):
 
537
        pb.update('Leoparden', i, 99)
 
538
        sleep(0.1)
 
539
    sleep(1.5)
 
540
    pb.clear()
 
541
    sleep(1.5)
 
542
    
 
543
    print 'smart-terminal test:'
 
544
    pb = ProgressBar(show_pct=True, show_bar=True, show_spinner=False)
 
545
    for i in range(100):
 
546
        pb.update('Elephanten', i, 99)
 
547
        sleep(0.1)
 
548
    sleep(2)
 
549
    pb.clear()
 
550
    sleep(1)
 
551
 
 
552
    print 'done!'
 
553
 
 
554
if __name__ == "__main__":
 
555
    demo()