/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>
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
2
# Copyright (C) 2005, 2006 Canonical <canonical.com>
648 by Martin Pool
- import aaron's progress-indicator code
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
649 by Martin Pool
- some cleanups for the progressbar method
18
889 by Martin Pool
- show progress bar during inventory conversion to weave, and make profiling optional
19
"""Simple text-mode progress indicator.
649 by Martin Pool
- some cleanups for the progressbar method
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.
27
"""
28
652 by Martin Pool
doc
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
962 by Martin Pool
todo
33
# TODO: If not on a tty perhaps just print '......' for the benefit of IDEs, etc
655 by Martin Pool
- better calculation of progress bar position
34
934 by Martin Pool
todo
35
# TODO: Optionally show elapsed time instead/as well as ETA; nicer
36
# when the rate is unpredictable
37
649 by Martin Pool
- some cleanups for the progressbar method
38
648 by Martin Pool
- import aaron's progress-indicator code
39
import sys
660 by Martin Pool
- use plain unix time, not datetime module
40
import time
964 by Martin Pool
- show progress on dumb terminals by printing dots
41
import os
1185.16.75 by Martin Pool
- improved eta estimation for progress bar
42
from collections import deque
648 by Martin Pool
- import aaron's progress-indicator code
43
649 by Martin Pool
- some cleanups for the progressbar method
44
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
45
import bzrlib.errors as errors
1551.2.27 by Aaron Bentley
Got propogation under test
46
from bzrlib.trace import mutter 
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
47
48
649 by Martin Pool
- some cleanups for the progressbar method
49
def _supports_progress(f):
695 by Martin Pool
- don't display progress bars on really dumb terminals
50
    if not hasattr(f, 'isatty'):
51
        return False
52
    if not f.isatty():
53
        return False
54
    if os.environ.get('TERM') == 'dumb':
55
        # e.g. emacs compile window
56
        return False
57
    return True
649 by Martin Pool
- some cleanups for the progressbar method
58
59
60
964 by Martin Pool
- show progress on dumb terminals by printing dots
61
def ProgressBar(to_file=sys.stderr, **kwargs):
62
    """Abstract factory"""
63
    if _supports_progress(to_file):
64
        return TTYProgressBar(to_file=to_file, **kwargs)
65
    else:
66
        return DotsProgressBar(to_file=to_file, **kwargs)
67
    
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
68
 
69
class ProgressBarStack(object):
70
    """A stack of progress bars."""
71
72
    def __init__(self,
73
                 to_file=sys.stderr,
74
                 show_pct=False,
1551.2.33 by Aaron Bentley
Hide ETA, show spinner by default
75
                 show_spinner=True,
76
                 show_eta=False,
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
77
                 show_bar=True,
78
                 show_count=True,
1594.1.3 by Robert Collins
Fixup pb usage to use nested_progress_bar.
79
                 to_messages_file=sys.stdout,
80
                 klass=None):
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
81
        """Setup the stack with the parameters the progress bars should have."""
82
        self._to_file = to_file
83
        self._show_pct = show_pct
84
        self._show_spinner = show_spinner
85
        self._show_eta = show_eta
86
        self._show_bar = show_bar
87
        self._show_count = show_count
88
        self._to_messages_file = to_messages_file
89
        self._stack = []
1594.1.3 by Robert Collins
Fixup pb usage to use nested_progress_bar.
90
        self._klass = klass or TTYProgressBar
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
91
1551.2.29 by Aaron Bentley
Got stack handling under test
92
    def top(self):
93
        if len(self._stack) != 0:
94
            return self._stack[-1]
95
        else:
96
            return None
97
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
98
    def get_nested(self):
99
        """Return a nested progress bar."""
1551.2.29 by Aaron Bentley
Got stack handling under test
100
        if len(self._stack) == 0:
101
            func = self._klass
102
        else:
103
            func = self.top().child_progress
104
        new_bar = func(to_file=self._to_file,
105
                       show_pct=self._show_pct,
106
                       show_spinner=self._show_spinner,
107
                       show_eta=self._show_eta,
108
                       show_bar=self._show_bar,
109
                       show_count=self._show_count,
110
                       to_messages_file=self._to_messages_file,
111
                       _stack=self)
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
112
        self._stack.append(new_bar)
113
        return new_bar
114
115
    def return_pb(self, bar):
116
        """Return bar after its been used."""
1594.1.4 by Robert Collins
Fix identity test in ProgressBarStack.return_pb
117
        if bar is not self._stack[-1]:
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
118
            raise errors.MissingProgressBarFinish()
119
        self._stack.pop()
120
121
 
964 by Martin Pool
- show progress on dumb terminals by printing dots
122
class _BaseProgressBar(object):
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
123
964 by Martin Pool
- show progress on dumb terminals by printing dots
124
    def __init__(self,
125
                 to_file=sys.stderr,
126
                 show_pct=False,
127
                 show_spinner=False,
128
                 show_eta=True,
129
                 show_bar=True,
1534.5.6 by Robert Collins
split out converter logic into per-format objects.
130
                 show_count=True,
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
131
                 to_messages_file=sys.stdout,
132
                 _stack=None):
964 by Martin Pool
- show progress on dumb terminals by printing dots
133
        object.__init__(self)
134
        self.to_file = to_file
1534.5.6 by Robert Collins
split out converter logic into per-format objects.
135
        self.to_messages_file = to_messages_file
964 by Martin Pool
- show progress on dumb terminals by printing dots
136
        self.last_msg = None
137
        self.last_cnt = None
138
        self.last_total = None
139
        self.show_pct = show_pct
140
        self.show_spinner = show_spinner
141
        self.show_eta = show_eta
142
        self.show_bar = show_bar
143
        self.show_count = show_count
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
144
        self._stack = _stack
1596.2.16 by Robert Collins
Microprofiling: progress.update was costing 0.01 ms per call in time.time.
145
        # seed throttler
146
        self.MIN_PAUSE = 0.1 # seconds
147
        now = time.clock()
148
        # starting now
149
        self.start_time = now
150
        # next update should not throttle
151
        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.
152
153
    def finished(self):
154
        """Return this bar to its progress stack."""
155
        self.clear()
1594.1.3 by Robert Collins
Fixup pb usage to use nested_progress_bar.
156
        assert self._stack is not None
1594.1.1 by Robert Collins
Introduce new bzr progress bar api. ui_factory.nested_progress_bar.
157
        self._stack.return_pb(self)
1104 by Martin Pool
- Add a simple UIFactory
158
1534.5.6 by Robert Collins
split out converter logic into per-format objects.
159
    def note(self, fmt_string, *args, **kwargs):
160
        """Record a note without disrupting the progress bar."""
161
        self.clear()
162
        self.to_messages_file.write(fmt_string % args)
163
        self.to_messages_file.write('\n')
1104 by Martin Pool
- Add a simple UIFactory
164
1551.2.29 by Aaron Bentley
Got stack handling under test
165
    def child_progress(self, **kwargs):
166
        return ChildProgress(**kwargs)
167
1104 by Martin Pool
- Add a simple UIFactory
168
169
class DummyProgress(_BaseProgressBar):
170
    """Progress-bar standin that does nothing.
171
172
    This can be used as the default argument for methods that
173
    take an optional progress indicator."""
174
    def tick(self):
175
        pass
176
177
    def update(self, msg=None, current=None, total=None):
178
        pass
179
1551.2.27 by Aaron Bentley
Got propogation under test
180
    def child_update(self, message, current, total):
181
        pass
182
1104 by Martin Pool
- Add a simple UIFactory
183
    def clear(self):
184
        pass
964 by Martin Pool
- show progress on dumb terminals by printing dots
185
        
1534.5.6 by Robert Collins
split out converter logic into per-format objects.
186
    def note(self, fmt_string, *args, **kwargs):
187
        """See _BaseProgressBar.note()."""
1534.5.9 by Robert Collins
Advise users running upgrade on a checkout to also run it on the branch.
188
1551.2.29 by Aaron Bentley
Got stack handling under test
189
    def child_progress(self, **kwargs):
190
        return DummyProgress(**kwargs)
1534.5.9 by Robert Collins
Advise users running upgrade on a checkout to also run it on the branch.
191
964 by Martin Pool
- show progress on dumb terminals by printing dots
192
class DotsProgressBar(_BaseProgressBar):
1594.1.3 by Robert Collins
Fixup pb usage to use nested_progress_bar.
193
964 by Martin Pool
- show progress on dumb terminals by printing dots
194
    def __init__(self, **kwargs):
195
        _BaseProgressBar.__init__(self, **kwargs)
196
        self.last_msg = None
197
        self.need_nl = False
198
        
199
    def tick(self):
200
        self.update()
201
        
202
    def update(self, msg=None, current_cnt=None, total_cnt=None):
203
        if msg and msg != self.last_msg:
204
            if self.need_nl:
205
                self.to_file.write('\n')
206
            
207
            self.to_file.write(msg + ': ')
208
            self.last_msg = msg
209
        self.need_nl = True
210
        self.to_file.write('.')
211
        
212
    def clear(self):
213
        if self.need_nl:
214
            self.to_file.write('\n')
215
        
1551.2.28 by Aaron Bentley
Ensure all ProgressBar implementations can be used as parents
216
    def child_update(self, message, current, total):
217
        self.tick()
964 by Martin Pool
- show progress on dumb terminals by printing dots
218
    
219
class TTYProgressBar(_BaseProgressBar):
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
220
    """Progress bar display object.
221
222
    Several options are available to control the display.  These can
223
    be passed as parameters to the constructor or assigned at any time:
224
225
    show_pct
226
        Show percentage complete.
227
    show_spinner
228
        Show rotating baton.  This ticks over on every update even
229
        if the values don't change.
230
    show_eta
231
        Show predicted time-to-completion.
232
    show_bar
233
        Show bar graph.
234
    show_count
235
        Show numerical counts.
236
237
    The output file should be in line-buffered or unbuffered mode.
238
    """
239
    SPIN_CHARS = r'/-\|'
661 by Martin Pool
- limit rate at which progress bar is updated
240
964 by Martin Pool
- show progress on dumb terminals by printing dots
241
242
    def __init__(self, **kwargs):
1185.33.60 by Martin Pool
Use full terminal width for verbose test output.
243
        from bzrlib.osutils import terminal_width
964 by Martin Pool
- show progress on dumb terminals by printing dots
244
        _BaseProgressBar.__init__(self, **kwargs)
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
245
        self.spin_pos = 0
1185.33.60 by Martin Pool
Use full terminal width for verbose test output.
246
        self.width = terminal_width()
964 by Martin Pool
- show progress on dumb terminals by printing dots
247
        self.start_time = None
1185.16.75 by Martin Pool
- improved eta estimation for progress bar
248
        self.last_updates = deque()
1551.2.28 by Aaron Bentley
Ensure all ProgressBar implementations can be used as parents
249
        self.child_fraction = 0
964 by Martin Pool
- show progress on dumb terminals by printing dots
250
    
251
252
    def throttle(self):
253
        """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.
254
        # time.time consistently takes 40/4000 ms = 0.01 ms.
255
        # but every single update to the pb invokes it.
256
        # so we use time.clock which takes 20/4000 ms = 0.005ms
257
        # on the downside, time.clock() appears to have approximately
258
        # 10ms granularity, so we treat a zero-time change as 'throttled.'
259
        
260
        now = time.clock()
261
        interval = now - self.last_update
262
        # if interval > 0
263
        if interval < self.MIN_PAUSE:
264
            return True
964 by Martin Pool
- show progress on dumb terminals by printing dots
265
1185.16.75 by Martin Pool
- improved eta estimation for progress bar
266
        self.last_updates.append(now - self.last_update)
964 by Martin Pool
- show progress on dumb terminals by printing dots
267
        self.last_update = now
268
        return False
929 by Martin Pool
- progress bar: avoid repeatedly checking screen width
269
        
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
270
271
    def tick(self):
1551.2.27 by Aaron Bentley
Got propogation under test
272
        self.update(self.last_msg, self.last_cnt, self.last_total, 
273
                    self.child_fraction)
274
1551.2.28 by Aaron Bentley
Ensure all ProgressBar implementations can be used as parents
275
    def child_update(self, message, current, total):
1551.2.35 by Aaron Bentley
Fix division-by-zero
276
        if current is not None and total != 0:
1551.2.30 by Aaron Bentley
Bugfixes to progress stuff
277
            child_fraction = float(current) / total
278
            if self.last_cnt is None:
279
                pass
280
            elif self.last_cnt + child_fraction <= self.last_total:
281
                self.child_fraction = child_fraction
282
            else:
283
                mutter('not updating child fraction')
284
        if self.last_msg is None:
285
            self.last_msg = ''
1551.2.28 by Aaron Bentley
Ensure all ProgressBar implementations can be used as parents
286
        self.tick()
287
288
1551.2.27 by Aaron Bentley
Got propogation under test
289
    def update(self, msg, current_cnt=None, total_cnt=None, 
290
               child_fraction=0):
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
291
        """Update and redraw progress bar."""
292
1308 by Martin Pool
- make progress bar more tolerant of out-of-range values
293
        if current_cnt < 0:
294
            current_cnt = 0
295
            
296
        if current_cnt > total_cnt:
297
            total_cnt = current_cnt
1570.1.9 by Robert Collins
Do not throttle updates to progress bars that change the message.
298
        
1596.2.17 by Robert Collins
Notes on further progress tuning.
299
        ## # optional corner case optimisation 
300
        ## # currently does not seem to fire so costs more than saved.
301
        ## # trivial optimal case:
302
        ## # NB if callers are doing a clear and restore with
303
        ## # the saved values, this will prevent that:
304
        ## # in that case add a restore method that calls
305
        ## # _do_update or some such
306
        ## if (self.last_msg == msg and
307
        ##     self.last_cnt == current_cnt and
308
        ##     self.last_total == total_cnt and
309
        ##     self.child_fraction == child_fraction):
310
        ##     return
311
1570.1.9 by Robert Collins
Do not throttle updates to progress bars that change the message.
312
        old_msg = self.last_msg
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
313
        # save these for the tick() function
314
        self.last_msg = msg
315
        self.last_cnt = current_cnt
316
        self.last_total = total_cnt
1596.2.17 by Robert Collins
Notes on further progress tuning.
317
        self.child_fraction = child_fraction
318
319
        # each function call takes 20ms/4000 = 0.005 ms, 
320
        # but multiple that by 4000 calls -> starts to cost.
321
        # so anything to make this function call faster
322
        # will improve base 'diff' time by up to 0.1 seconds.
1570.1.9 by Robert Collins
Do not throttle updates to progress bars that change the message.
323
        if old_msg == self.last_msg and self.throttle():
1596.2.17 by Robert Collins
Notes on further progress tuning.
324
            return
325
326
        if self.show_eta and self.start_time and self.last_total:
327
            eta = get_eta(self.start_time, self.last_cnt + self.child_fraction, 
328
                    self.last_total, last_updates = self.last_updates)
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
329
            eta_str = " " + str_tdelta(eta)
330
        else:
331
            eta_str = ""
332
333
        if self.show_spinner:
334
            spin_str = self.SPIN_CHARS[self.spin_pos % 4] + ' '            
335
        else:
336
            spin_str = ''
337
338
        # always update this; it's also used for the bar
339
        self.spin_pos += 1
340
1596.2.17 by Robert Collins
Notes on further progress tuning.
341
        if self.show_pct and self.last_total and self.last_cnt:
342
            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
343
            pct_str = ' (%5.1f%%)' % pct
344
        else:
345
            pct_str = ''
346
347
        if not self.show_count:
348
            count_str = ''
1596.2.17 by Robert Collins
Notes on further progress tuning.
349
        elif self.last_cnt is None:
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
350
            count_str = ''
1596.2.17 by Robert Collins
Notes on further progress tuning.
351
        elif self.last_total is None:
352
            count_str = ' %i' % (self.last_cnt)
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
353
        else:
354
            # make both fields the same size
1596.2.17 by Robert Collins
Notes on further progress tuning.
355
            t = '%i' % (self.last_total)
356
            c = '%*i' % (len(t), self.last_cnt)
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
357
            count_str = ' ' + c + '/' + t 
358
359
        if self.show_bar:
360
            # progress bar, if present, soaks up all remaining space
1596.2.17 by Robert Collins
Notes on further progress tuning.
361
            cols = self.width - 1 - len(self.last_msg) - len(spin_str) - len(pct_str) \
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
362
                   - len(eta_str) - len(count_str) - 3
363
1596.2.17 by Robert Collins
Notes on further progress tuning.
364
            if self.last_total:
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
365
                # number of markers highlighted in bar
1551.2.30 by Aaron Bentley
Bugfixes to progress stuff
366
                markers = int(round(float(cols) * 
1596.2.17 by Robert Collins
Notes on further progress tuning.
367
                              (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
368
                bar_str = '[' + ('=' * markers).ljust(cols) + '] '
669 by Martin Pool
- don't show progress bar unless completion is known
369
            elif False:
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
370
                # don't know total, so can't show completion.
371
                # so just show an expanded spinning thingy
372
                m = self.spin_pos % cols
668 by Martin Pool
- fix sweeping bar progress indicator
373
                ms = (' ' * m + '*').ljust(cols)
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
374
                
375
                bar_str = '[' + ms + '] '
669 by Martin Pool
- don't show progress bar unless completion is known
376
            else:
377
                bar_str = ''
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
378
        else:
379
            bar_str = ''
380
1596.2.17 by Robert Collins
Notes on further progress tuning.
381
        m = spin_str + bar_str + self.last_msg + count_str + pct_str + eta_str
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
382
929 by Martin Pool
- progress bar: avoid repeatedly checking screen width
383
        assert len(m) < self.width
384
        self.to_file.write('\r' + m.ljust(self.width - 1))
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
385
        #self.to_file.flush()
386
            
964 by Martin Pool
- show progress on dumb terminals by printing dots
387
    def clear(self):        
929 by Martin Pool
- progress bar: avoid repeatedly checking screen width
388
        self.to_file.write('\r%s\r' % (' ' * (self.width - 1)))
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
389
        #self.to_file.flush()        
649 by Martin Pool
- some cleanups for the progressbar method
390
1551.2.27 by Aaron Bentley
Got propogation under test
391
1551.2.28 by Aaron Bentley
Ensure all ProgressBar implementations can be used as parents
392
class ChildProgress(_BaseProgressBar):
1551.2.27 by Aaron Bentley
Got propogation under test
393
    """A progress indicator that pushes its data to the parent"""
1551.2.29 by Aaron Bentley
Got stack handling under test
394
    def __init__(self, _stack, **kwargs):
395
        _BaseProgressBar.__init__(self, _stack=_stack, **kwargs)
396
        self.parent = _stack.top()
1551.2.27 by Aaron Bentley
Got propogation under test
397
        self.current = None
398
        self.total = None
399
        self.child_fraction = 0
400
        self.message = None
401
402
    def update(self, msg, current_cnt=None, total_cnt=None):
403
        self.current = current_cnt
404
        self.total = total_cnt
405
        self.message = msg
406
        self.child_fraction = 0
407
        self.tick()
408
409
    def child_update(self, message, current, total):
1551.2.35 by Aaron Bentley
Fix division-by-zero
410
        if current is None or total == 0:
1551.2.30 by Aaron Bentley
Bugfixes to progress stuff
411
            self.child_fraction = 0
412
        else:
413
            self.child_fraction = float(current) / total
1551.2.27 by Aaron Bentley
Got propogation under test
414
        self.tick()
415
416
    def tick(self):
1551.2.30 by Aaron Bentley
Bugfixes to progress stuff
417
        if self.current is None:
418
            count = None
419
        else:
420
            count = self.current+self.child_fraction
421
            if count > self.total:
1596.2.35 by Robert Collins
Subclass SequenceMatcher to get a slightly faster (in our case) find_longest_match routine.
422
                if __debug__:
423
                    mutter('clamping count of %d to %d' % (count, self.total))
1551.2.30 by Aaron Bentley
Bugfixes to progress stuff
424
                count = self.total
1551.2.27 by Aaron Bentley
Got propogation under test
425
        self.parent.child_update(self.message, count, self.total)
426
1551.2.29 by Aaron Bentley
Got stack handling under test
427
    def clear(self):
1551.2.30 by Aaron Bentley
Bugfixes to progress stuff
428
        pass
1551.2.29 by Aaron Bentley
Got stack handling under test
429
1551.2.27 by Aaron Bentley
Got propogation under test
430
 
648 by Martin Pool
- import aaron's progress-indicator code
431
def str_tdelta(delt):
432
    if delt is None:
433
        return "-:--:--"
660 by Martin Pool
- use plain unix time, not datetime module
434
    delt = int(round(delt))
435
    return '%d:%02d:%02d' % (delt/3600,
436
                             (delt/60) % 60,
437
                             delt % 60)
438
439
1185.16.75 by Martin Pool
- improved eta estimation for progress bar
440
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
441
    if start_time is None:
442
        return None
443
444
    if not total:
445
        return None
446
447
    if current < enough_samples:
448
        return None
449
450
    if current > total:
451
        return None                     # wtf?
452
1596.2.16 by Robert Collins
Microprofiling: progress.update was costing 0.01 ms per call in time.time.
453
    elapsed = time.clock() - start_time
660 by Martin Pool
- use plain unix time, not datetime module
454
455
    if elapsed < 2.0:                   # not enough time to estimate
456
        return None
457
    
458
    total_duration = float(elapsed) * float(total) / float(current)
459
460
    assert total_duration >= elapsed
461
1185.16.75 by Martin Pool
- improved eta estimation for progress bar
462
    if last_updates and len(last_updates) >= n_recent:
463
        while len(last_updates) > n_recent:
464
            last_updates.popleft()
465
        avg = sum(last_updates) / float(len(last_updates))
466
        time_left = avg * (total - current)
467
468
        old_time_left = total_duration - elapsed
469
470
        # We could return the average, or some other value here
471
        return (time_left + old_time_left) / 2
472
660 by Martin Pool
- use plain unix time, not datetime module
473
    return total_duration - elapsed
648 by Martin Pool
- import aaron's progress-indicator code
474
649 by Martin Pool
- some cleanups for the progressbar method
475
1551.2.32 by Aaron Bentley
Handle progress phases more nicely in merge
476
class ProgressPhase(object):
477
    """Update progress object with the current phase"""
478
    def __init__(self, message, total, pb):
479
        object.__init__(self)
480
        self.pb = pb
481
        self.message = message
482
        self.total = total
483
        self.cur_phase = None
484
485
    def next_phase(self):
486
        if self.cur_phase is None:
487
            self.cur_phase = 0
488
        else:
489
            self.cur_phase += 1
490
        assert self.cur_phase < self.total 
491
        self.pb.update(self.message, self.cur_phase, self.total)
492
493
648 by Martin Pool
- import aaron's progress-indicator code
494
def run_tests():
495
    import doctest
496
    result = doctest.testmod()
497
    if result[1] > 0:
498
        if result[0] == 0:
499
            print "All tests passed"
500
    else:
501
        print "No tests to run"
649 by Martin Pool
- some cleanups for the progressbar method
502
503
504
def demo():
964 by Martin Pool
- show progress on dumb terminals by printing dots
505
    sleep = time.sleep
506
    
507
    print 'dumb-terminal test:'
508
    pb = DotsProgressBar()
509
    for i in range(100):
510
        pb.update('Leoparden', i, 99)
511
        sleep(0.1)
512
    sleep(1.5)
513
    pb.clear()
514
    sleep(1.5)
515
    
516
    print 'smart-terminal test:'
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
517
    pb = ProgressBar(show_pct=True, show_bar=True, show_spinner=False)
649 by Martin Pool
- some cleanups for the progressbar method
518
    for i in range(100):
658 by Martin Pool
- clean up and add a bunch of options to the progress indicator
519
        pb.update('Elephanten', i, 99)
520
        sleep(0.1)
521
    sleep(2)
522
    pb.clear()
523
    sleep(1)
964 by Martin Pool
- show progress on dumb terminals by printing dots
524
649 by Martin Pool
- some cleanups for the progressbar method
525
    print 'done!'
526
648 by Martin Pool
- import aaron's progress-indicator code
527
if __name__ == "__main__":
649 by Martin Pool
- some cleanups for the progressbar method
528
    demo()