[Pkg-bazaar-commits] ./bzr/unstable r658: - clean up and add a bunch of options to the progress indicator

Martin Pool mbp at sourcefrog.net
Fri Apr 10 08:20:37 UTC 2009


------------------------------------------------------------
revno: 658
committer: Martin Pool <mbp at sourcefrog.net>
timestamp: Fri 2005-06-10 18:40:33 +1000
message:
  - clean up and add a bunch of options to the progress indicator
modified:
  bzrlib/check.py
  bzrlib/progress.py
-------------- next part --------------
=== modified file 'bzrlib/check.py'
--- a/bzrlib/check.py	2005-06-10 07:38:36 +0000
+++ b/bzrlib/check.py	2005-06-10 08:40:33 +0000
@@ -26,11 +26,11 @@
     from bzrlib.trace import mutter
     from bzrlib.errors import BzrCheckError
     from bzrlib.osutils import fingerprint_file
-    from bzrlib.progress import ProgressBar, Progress
+    from bzrlib.progress import ProgressBar
     
     out = sys.stdout
 
-    pb = ProgressBar()
+    pb = ProgressBar(show_spinner=True)
     last_ptr = None
     checked_revs = {}
     
@@ -42,7 +42,7 @@
     
     for rid in history:
         revno += 1
-        pb(Progress('revision', revno, revcount))
+        pb.update('checking revision', revno, revcount)
         mutter('    revision {%s}' % rid)
         rev = branch.get_revision(rid)
         if rev.revision_id != rid:
@@ -72,7 +72,8 @@
         len_inv = len(inv)
         for file_id in inv:
             i += 1
-            pb(Progress('file texts', i, len_inv))
+            if (i % 100) == 99:
+                pb.tick()
 
             ie = inv[file_id]
 
@@ -101,7 +102,7 @@
                     raise BzrCheckError('directory {%s} has text in revision {%s}'
                             % (file_id, rid))
 
-        pb(Progress('file paths', revno, revcount))
+        pb.tick()
         for path, ie in inv.iter_entries():
             if path in seen_names:
                 raise BzrCheckError('duplicated path %r '

=== modified file 'bzrlib/progress.py'
--- a/bzrlib/progress.py	2005-06-10 07:22:19 +0000
+++ b/bzrlib/progress.py	2005-06-10 08:40:33 +0000
@@ -63,60 +63,138 @@
 
 
 
-class Progress(object):
-    """Description of progress through a task.
-
-    Basically just a fancy tuple holding:
-
-    units
-        noun string describing what is being traversed, e.g.
-        "balloons", "kB"
-
-    current
-        how many objects have been processed so far
-
-    total
-        total number of objects to process, if known.
-    """
-    
-    def __init__(self, units, current, total=None):
-        self.units = units
-        self.current = current
-        self.total = total
-
-    def _get_percent(self):
-        if self.total is not None and self.current is not None:
-            return 100.0 * self.current / self.total
-
-    percent = property(_get_percent)
-
-    def __str__(self):
-        if self.total is not None:
-            return "%i of %i %s %.1f%%" % (self.current, self.total, self.units,
-                                         self.percent)
-        else:
-            return "%i %s" (self.current, self.units)
-
-
-
 class ProgressBar(object):
-    def __init__(self, to_file=sys.stderr):
+    """Progress bar display object.
+
+    Several options are available to control the display.  These can
+    be passed as parameters to the constructor or assigned at any time:
+
+    show_pct
+        Show percentage complete.
+    show_spinner
+        Show rotating baton.  This ticks over on every update even
+        if the values don't change.
+    show_eta
+        Show predicted time-to-completion.
+    show_bar
+        Show bar graph.
+    show_count
+        Show numerical counts.
+
+    The output file should be in line-buffered or unbuffered mode.
+    """
+    SPIN_CHARS = r'/-\|'
+    
+    def __init__(self,
+                 to_file=sys.stderr,
+                 show_pct=False,
+                 show_spinner=False,
+                 show_eta=True,
+                 show_bar=True,
+                 show_count=True):
         object.__init__(self)
-        self.start = None
+        self.start_time = None
         self.to_file = to_file
         self.suppressed = not _supports_progress(self.to_file)
-
-
-    def __call__(self, progress):
-        if self.start is None:
-            self.start = datetime.datetime.now()
-        if not self.suppressed:
-            draw_progress_bar(progress, start_time=self.start,
-                              to_file=self.to_file)
+        self.spin_pos = 0
+ 
+        self.show_pct = show_pct
+        self.show_spinner = show_spinner
+        self.show_eta = show_eta
+        self.show_bar = show_bar
+        self.show_count = show_count
+
+
+    def tick(self):
+        self.update(self.last_msg, self.last_cnt, self.last_total)
+                 
+
+
+    def update(self, msg, current_cnt, total_cnt=None):
+        """Update and redraw progress bar."""
+        if self.start_time is None:
+            self.start_time = datetime.datetime.now()
+
+        # save these for the tick() function
+        self.last_msg = msg
+        self.last_cnt = current_cnt
+        self.last_total = total_cnt
+            
+        if self.suppressed:
+            return
+
+        width = _width()
+
+        if total_cnt:
+            assert current_cnt <= total_cnt
+        if current_cnt:
+            assert current_cnt >= 0
+        
+        if self.show_eta and self.start_time and total_cnt:
+            eta = get_eta(self.start_time, current_cnt, total_cnt)
+            eta_str = " " + str_tdelta(eta)
+        else:
+            eta_str = ""
+
+        if self.show_spinner:
+            spin_str = self.SPIN_CHARS[self.spin_pos % 4] + ' '            
+        else:
+            spin_str = ''
+
+        # always update this; it's also used for the bar
+        self.spin_pos += 1
+
+        if self.show_pct and total_cnt and current_cnt:
+            pct = 100.0 * current_cnt / total_cnt
+            pct_str = ' (%5.1f%%)' % pct
+        else:
+            pct_str = ''
+
+        if not self.show_count:
+            count_str = ''
+        elif current_cnt is None:
+            count_str = ''
+        elif total_cnt is None:
+            count_str = ' %i' % (current_cnt)
+        else:
+            # make both fields the same size
+            t = '%i' % (total_cnt)
+            c = '%*i' % (len(t), current_cnt)
+            count_str = ' ' + c + '/' + t 
+
+        if self.show_bar:
+            # progress bar, if present, soaks up all remaining space
+            cols = width - 1 - len(msg) - len(spin_str) - len(pct_str) \
+                   - len(eta_str) - len(count_str) - 3
+
+            if total_cnt:
+                # number of markers highlighted in bar
+                markers = int(round(float(cols) * current_cnt / total_cnt))
+                bar_str = '[' + ('=' * markers).ljust(cols) + '] '
+            else:
+                # don't know total, so can't show completion.
+                # so just show an expanded spinning thingy
+                m = self.spin_pos % cols
+                ms = ' ' * cols
+                ms[m] = '*'
+                
+                bar_str = '[' + ms + '] '
+        else:
+            bar_str = ''
+
+        m = spin_str + bar_str + msg + count_str + pct_str + eta_str
+
+        assert len(m) < width
+        self.to_file.write('\r' + m.ljust(width - 1))
+        #self.to_file.flush()
+            
 
     def clear(self):
-        if not self.suppressed:
-            clear_progress_bar(self.to_file)
+        if self.suppressed:
+            return
+        
+        self.to_file.write('\r%s\r' % (' ' * (_width() - 1)))
+        #self.to_file.flush()        
     
 
         
@@ -132,14 +210,15 @@
     return str(datetime.timedelta(delt.days, delt.seconds))
 
 
-def get_eta(start_time, progress, enough_samples=20):
-    if start_time is None or progress.current == 0:
+def get_eta(start_time, current, total, enough_samples=20):
+    if start_time is None or current == 0:
         return None
-    elif progress.current < enough_samples:
+    elif current < enough_samples:
+        # FIXME: No good if it's not a count
         return None
     elapsed = datetime.datetime.now() - start_time
-    total_duration = divide_timedelta((elapsed) * long(progress.total), 
-                                      progress.current)
+    total_duration = divide_timedelta(elapsed * long(total), 
+                                      current)
     if elapsed < total_duration:
         eta = total_duration - elapsed
     else:
@@ -147,62 +226,6 @@
     return eta
 
 
-def draw_progress_bar(progress, start_time=None, to_file=sys.stderr):
-    eta = get_eta(start_time, progress)
-    if start_time is not None:
-        eta_str = " "+str_tdelta(eta)
-    else:
-        eta_str = ""
-
-    fmt = " %i of %i %s (%.1f%%)"
-    f = fmt % (progress.total, progress.total, progress.units, 100.0)
-    cols = _width() - 3 - len(f)
-    if start_time is not None:
-        cols -= len(eta_str)
-    markers = int(round(float(cols) * progress.current / progress.total))
-    txt = fmt % (progress.current, progress.total, progress.units,
-                 progress.percent)
-    to_file.write("\r[%s%s]%s%s" % ('='*markers, ' '*(cols-markers), txt, 
-                                       eta_str))
-
-def clear_progress_bar(to_file=sys.stderr):
-    to_file.write('\r%s\r' % (' '*79))
-
-
-def spinner_str(progress, show_text=False):
-    """
-    Produces the string for a textual "spinner" progress indicator
-    :param progress: an object represinting current progress
-    :param show_text: If true, show progress text as well
-    :return: The spinner string
-
-    >>> spinner_str(Progress("baloons", 0))
-    '|'
-    >>> spinner_str(Progress("baloons", 5))
-    '/'
-    >>> spinner_str(Progress("baloons", 6), show_text=True)
-    '- 6 baloons'
-    """
-    positions = ('|', '/', '-', '\\')
-    text = positions[progress.current % 4]
-    if show_text:
-        text+=" %i %s" % (progress.current, progress.units)
-    return text
-
-
-def spinner(progress, show_text=False, output=sys.stderr):
-    """
-    Update a spinner progress indicator on an output
-    :param progress: The progress to display
-    :param show_text: If true, show text as well as spinner
-    :param output: The output to write to
-
-    >>> spinner(Progress("baloons", 6), show_text=True, output=sys.stdout)
-    \r- 6 baloons
-    """
-    output.write('\r%s' % spinner_str(progress, show_text))
-
-
 def run_tests():
     import doctest
     result = doctest.testmod()
@@ -215,10 +238,13 @@
 
 def demo():
     from time import sleep
-    pb = ProgressBar()
+    pb = ProgressBar(show_pct=True, show_bar=True, show_spinner=False)
     for i in range(100):
-        pb(Progress('Elephanten', i, 100))
-        sleep(0.3)
+        pb.update('Elephanten', i, 99)
+        sleep(0.1)
+    sleep(2)
+    pb.clear()
+    sleep(1)
     print 'done!'
 
 if __name__ == "__main__":



More information about the Pkg-bazaar-commits mailing list