~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/progress.py

  • Committer: Matt Nordhoff
  • Date: 2009-04-04 02:50:01 UTC
  • mfrom: (4253 +trunk)
  • mto: This revision was merged to the branch mainline in revision 4256.
  • Revision ID: mnordhoff@mattnordhoff.com-20090404025001-z1403k0tatmc8l91
Merge bzr.dev, fixing conflicts.

Show diffs side-by-side

added added

removed removed

Lines of Context:
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
 
18
18
"""Progress indicators.
44
44
def _supports_progress(f):
45
45
    """Detect if we can use pretty progress bars on the output stream f.
46
46
 
47
 
    If this returns true we expect that a human may be looking at that 
 
47
    If this returns true we expect that a human may be looking at that
48
48
    output, and that we can repaint a line to update it.
49
49
    """
50
50
    isatty = getattr(f, 'isatty', None)
61
61
class ProgressTask(object):
62
62
    """Model component of a progress indicator.
63
63
 
64
 
    Most code that needs to indicate progress should update one of these, 
 
64
    Most code that needs to indicate progress should update one of these,
65
65
    and it will in turn update the display, if one is present.
66
66
 
67
67
    Code updating the task may also set fields as hints about how to display
70
70
    """
71
71
 
72
72
    def __init__(self, parent_task=None, ui_factory=None):
 
73
        """Construct a new progress task.
 
74
 
 
75
        Normally you should not call this directly but rather through
 
76
        `ui_factory.nested_progress_bar`.
 
77
        """
73
78
        self._parent_task = parent_task
74
79
        self._last_update = 0
75
80
        self.total_cnt = None
112
117
        if self.current_cnt is not None and self.total_cnt:
113
118
            own_fraction = (float(self.current_cnt) + child_fraction) / self.total_cnt
114
119
        else:
115
 
            own_fraction = None
 
120
            # if this task has no estimation, it just passes on directly
 
121
            # whatever the child has measured...
 
122
            own_fraction = child_fraction
116
123
        if self._parent_task is None:
117
124
            return own_fraction
118
125
        else:
158
165
 
159
166
class ProgressBarStack(object):
160
167
    """A stack of progress bars.
161
 
    
 
168
 
162
169
    This class is deprecated: instead, ask the ui factory for a new progress
163
170
    task and finish it when it's done.
164
171
    """
224
231
        else:
225
232
            self._stack.pop()
226
233
 
227
 
 
 
234
 
228
235
class _BaseProgressBar(object):
229
236
 
230
237
    def __init__(self,
292
299
 
293
300
    def clear(self):
294
301
        pass
295
 
        
 
302
 
296
303
    def note(self, fmt_string, *args, **kwargs):
297
304
        """See _BaseProgressBar.note()."""
298
305
 
306
313
        _BaseProgressBar.__init__(self, **kwargs)
307
314
        self.last_msg = None
308
315
        self.need_nl = False
309
 
        
 
316
 
310
317
    def tick(self):
311
318
        self.update()
312
 
        
 
319
 
313
320
    def update(self, msg=None, current_cnt=None, total_cnt=None):
314
321
        if msg and msg != self.last_msg:
315
322
            if self.need_nl:
318
325
            self.last_msg = msg
319
326
        self.need_nl = True
320
327
        self.to_file.write('.')
321
 
        
 
328
 
322
329
    def clear(self):
323
330
        if self.need_nl:
324
331
            self.to_file.write('\n')
325
332
        self.need_nl = False
326
 
        
 
333
 
327
334
    def child_update(self, message, current, total):
328
335
        self.tick()
329
336
 
330
337
 
331
338
 
332
 
    
 
339
 
333
340
class TTYProgressBar(_BaseProgressBar):
334
341
    """Progress bar display object.
335
342
 
362
369
        self._max_last_updates = 10
363
370
        self.child_fraction = 0
364
371
        self._have_output = False
365
 
    
 
372
 
366
373
    def throttle(self, old_msg):
367
374
        """Return True if the bar was updated too recently"""
368
375
        # time.time consistently takes 40/4000 ms = 0.01 ms.
382
389
        self.last_updates = self.last_updates[-self._max_last_updates:]
383
390
        self.last_update = now
384
391
        return False
385
 
        
 
392
 
386
393
    def tick(self):
387
394
        self.update(self.last_msg, self.last_cnt, self.last_total,
388
395
                    self.child_fraction)
410
417
 
411
418
        if current_cnt < 0:
412
419
            current_cnt = 0
413
 
            
 
420
 
414
421
        if current_cnt > total_cnt:
415
422
            total_cnt = current_cnt
416
 
        
417
 
        ## # optional corner case optimisation 
 
423
 
 
424
        ## # optional corner case optimisation
418
425
        ## # currently does not seem to fire so costs more than saved.
419
426
        ## # trivial optimal case:
420
427
        ## # NB if callers are doing a clear and restore with
437
444
        self.last_total = total_cnt
438
445
        self.child_fraction = child_fraction
439
446
 
440
 
        # each function call takes 20ms/4000 = 0.005 ms, 
 
447
        # each function call takes 20ms/4000 = 0.005 ms,
441
448
        # but multiple that by 4000 calls -> starts to cost.
442
449
        # so anything to make this function call faster
443
450
        # will improve base 'diff' time by up to 0.1 seconds.
445
452
            return
446
453
 
447
454
        if self.show_eta and self.start_time and self.last_total:
448
 
            eta = get_eta(self.start_time, self.last_cnt + self.child_fraction, 
 
455
            eta = get_eta(self.start_time, self.last_cnt + self.child_fraction,
449
456
                    self.last_total, last_updates = self.last_updates)
450
457
            eta_str = " " + str_tdelta(eta)
451
458
        else:
452
459
            eta_str = ""
453
460
 
454
461
        if self.show_spinner:
455
 
            spin_str = self.SPIN_CHARS[self.spin_pos % 4] + ' '            
 
462
            spin_str = self.SPIN_CHARS[self.spin_pos % 4] + ' '
456
463
        else:
457
464
            spin_str = ''
458
465
 
484
491
 
485
492
            if self.last_total:
486
493
                # number of markers highlighted in bar
487
 
                markers = int(round(float(cols) * 
 
494
                markers = int(round(float(cols) *
488
495
                              (self.last_cnt + self.child_fraction) / self.last_total))
489
496
                bar_str = '[' + ('=' * markers).ljust(cols) + '] '
490
497
            elif False:
492
499
                # so just show an expanded spinning thingy
493
500
                m = self.spin_pos % cols
494
501
                ms = (' ' * m + '*').ljust(cols)
495
 
                
 
502
 
496
503
                bar_str = '[' + ms + '] '
497
504
            else:
498
505
                bar_str = ''
504
511
        self.to_file.write('\r%-*.*s' % (self.width - 1, self.width - 1, m))
505
512
        self._have_output = True
506
513
        #self.to_file.flush()
507
 
            
 
514
 
508
515
    def clear(self):
509
516
        if self._have_output:
510
517
            self.to_file.write('\r%s\r' % (' ' * (self.width - 1)))
511
518
        self._have_output = False
512
 
        #self.to_file.flush()        
 
519
        #self.to_file.flush()
513
520
 
514
521
 
515
522
 
601
608
 
602
609
    if elapsed < 2.0:                   # not enough time to estimate
603
610
        return None
604
 
    
 
611
 
605
612
    total_duration = float(elapsed) * float(total) / float(current)
606
613
 
607
614
    if last_updates and len(last_updates) >= n_recent: