starcluster.progressbar

Package starcluster :: Module progressbar |
[hide private]
[frames] | no frames]

# Source Code for Module starcluster.progressbar

      1  #!/usr/bin/python
      2  # -*- coding: iso-8859-1 -*-
      3  #
      4  # progressbar  - Text progressbar library for python.
      5  # Copyright (c) 2005 Nilton Volpato
      6  #
      7  # This library is free software; you can redistribute it and/or
      8  # modify it under the terms of the GNU Lesser General Public
      9  # License as published by the Free Software Foundation; either
     10  # version 2.1 of the License, or (at your option) any later version.
     11  #
     12  # This library is distributed in the hope that it will be useful,
     13  # but WITHOUT ANY WARRANTY; without even the implied warranty of
     14  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     15  # Lesser General Public License for more details.
     16  #
     17  # You should have received a copy of the GNU Lesser General Public
     18  # License along with this library; if not, write to the Free Software
     19  # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     20
     21
     22  """Text progressbar library for python.
     23
     24  This library provides a text mode progressbar. This is typically used
     25  to display the progress of a long running operation, providing a
     26  visual clue that processing is underway.
     27
     28  The ProgressBar class manages the progress, and the format of the line
     29  is given by a number of widgets. A widget is an object that may
     30  display diferently depending on the state of the progress. There are
     31  three types of widget:
     32  - a string, which always shows itself;
     33  - a ProgressBarWidget, which may return a diferent value every time
     34  it's update method is called; and
     35  - a ProgressBarWidgetHFill, which is like ProgressBarWidget, except it
     36  expands to fill the remaining width of the line.
     37
     38  The progressbar module is very easy to use, yet very powerful. And
     39  automatically supports features like auto-resizing when available.
     40  """
     41
     42  __author__ = "Nilton Volpato"
     43  __author_email__ = "first-name dot last-name @ gmail.com"
     44  __date__ = "2006-05-07"
     45  __version__ = "2.2"
     46
     47  # Changelog
     48  #
     49  # 2006-05-07: v2.2 fixed bug in windows
     50  # 2005-12-04: v2.1 autodetect terminal width, added start method
     51  # 2005-12-04: v2.0 everything is now a widget (wow!)
     52  # 2005-12-03: v1.0 rewrite using widgets
     53  # 2005-06-02: v0.5 rewrite
     54  # 2004-??-??: v0.1 first version
     55
     56  import sys
     57  import time
     58  from array import array
     59  try:
     60      from fcntl import ioctl
     61      import termios
     62  except ImportError:
     63      pass
     64  import signal
     65
     66
     67 -class ProgressBarWidget(object):
     68      """This is an element of ProgressBar formatting.
     69
     70      The ProgressBar object will call it's update value when an update
     71      is needed. It's size may change between call, but the results will
     72      not be good if the size changes drastically and repeatedly.
     73      """
     74 -    def update(self, pbar):
     75          """Returns the string representing the widget.
     76
     77          The parameter pbar is a reference to the calling ProgressBar,
     78          where one can access attributes of the class for knowing how
     79          the update must be made.
     80
     81          At least this function must be overriden."""
     82          pass
     83
     84
     85 -class ProgressBarWidgetHFill(object):
     86      """This is a variable width element of ProgressBar formatting.
     87
     88      The ProgressBar object will call it's update value, informing the
     89      width this object must the made. This is like TeX \\hfill, it will
     90      expand to fill the line. You can use more than one in the same
     91      line, and they will all have the same width, and together will
     92      fill the line.
     93      """
     94 -    def update(self, pbar, width):
     95          """Returns the string representing the widget.
     96
     97          The parameter pbar is a reference to the calling ProgressBar,
     98          where one can access attributes of the class for knowing how
     99          the update must be made. The parameter width is the total
    100          horizontal width the widget must have.
    101
    102          At least this function must be overriden."""
    103          pass
    104
    105
    106 -class ETA(ProgressBarWidget):
    107      "Widget for the Estimated Time of Arrival"
    108 -    def format_time(self, seconds):
    109          return time.strftime('%H:%M:%S', time.gmtime(seconds))
    110
    111 -    def update(self, pbar):
    112          if pbar.currval == 0:
    113              return 'ETA:  --:--:--'
    114          elif pbar.finished:
    115              return 'Time: %s' % self.format_time(pbar.seconds_elapsed)
    116          else:
    117              elapsed = pbar.seconds_elapsed
    118              eta = elapsed * pbar.maxval / pbar.currval - elapsed
    119              return 'ETA:  %s' % self.format_time(eta)
    120
    121
    122 -class FileTransferSpeed(ProgressBarWidget):
    123      "Widget for showing the transfer speed (useful for file transfers)."
    124 -    def __init__(self):
    125          self.fmt = '%6.2f %s'
    126          self.units = ['B', 'K', 'M', 'G', 'T', 'P']
    127
    128 -    def update(self, pbar):
    129          if pbar.seconds_elapsed < 2e-6:  # == 0:
    130              bps = 0.0
    131          else:
    132              bps = float(pbar.currval) / pbar.seconds_elapsed
    133          spd = bps
    134          for u in self.units:
    135              if spd < 1000:
    136                  break
    137              spd /= 1000
    138          return self.fmt % (spd, u + '/s')
    139
    140
    141 -class RotatingMarker(ProgressBarWidget):
    142      "A rotating marker for filling the bar of progress."
    143 -    def __init__(self, markers='|/-\\'):
    144          self.markers = markers
    145          self.curmark = -1
    146
    147 -    def update(self, pbar):
    148          if pbar.finished:
    149              return self.markers[0]
    150          self.curmark = (self.curmark + 1) % len(self.markers)
    151          return self.markers[self.curmark]
    152
    153
    154 -class Percentage(ProgressBarWidget):
    155      "Just the percentage done."
    156 -    def update(self, pbar):
    157          return '%3d%%' % pbar.percentage()
    158
    159
    160 -class Fraction(ProgressBarWidget):
    161      "Just the fraction done."
    162 -    def update(self, pbar):
    163          return "%d/%d" % (pbar.currval, pbar.maxval)
    164
    165
    166 -class Bar(ProgressBarWidgetHFill):
    167      "The bar of progress. It will strech to fill the line."
    168 -    def __init__(self, marker='#', left='|', right='|'):
    169          self.marker = marker
    170          self.left = left
    171          self.right = right
    172
    173 -    def _format_marker(self, pbar):
    174          if isinstance(self.marker, (str, unicode)):
    175              return self.marker
    176          else:
    177              return self.marker.update(pbar)
    178
    179 -    def update(self, pbar, width):
    180          percent = pbar.percentage()
    181          cwidth = width - len(self.left) - len(self.right)
    182          marked_width = int(percent * cwidth / 100)
    183          m = self._format_marker(pbar)
    184          bar = (self.left + (m * marked_width).ljust(cwidth) + self.right)
    185          return bar
    186
    187
    188 -class ReverseBar(Bar):
    189      "The reverse bar of progress, or bar of regress. :)"
    190 -    def update(self, pbar, width):
    191          percent = pbar.percentage()
    192          cwidth = width - len(self.left) - len(self.right)
    193          marked_width = int(percent * cwidth / 100)
    194          m = self._format_marker(pbar)
    195          bar = (self.left + (m * marked_width).rjust(cwidth) + self.right)
    196          return bar
    197
    198  default_widgets = [Percentage(), ' ', Bar()]
    199
    200
    201 -class ProgressBar(object):
    202      """This is the ProgressBar class, it updates and prints the bar.
    203
    204      The term_width parameter may be an integer. Or None, in which case
    205      it will try to guess it, if it fails it will default to 80 columns.
    206
    207      The simple use is like this:
    208      >>> pbar = ProgressBar().start()
    209      >>> for i in xrange(100):
    210      ...    # do something
    211      ...    pbar.update(i+1)
    212      ...
    213      >>> pbar.finish()
    214
    215      But anything you want to do is possible (well, almost anything).
    216      You can supply different widgets of any type in any order. And you
    217      can even write your own widgets! There are many widgets already
    218      shipped and you should experiment with them.
    219
    220      When implementing a widget update method you may access any
    221      attribute or function of the ProgressBar object calling the
    222      widget's update method. The most important attributes you would
    223      like to access are:
    224      - currval: current value of the progress, 0 <= currval <= maxval
    225      - maxval: maximum (and final) value of the progress
    226      - finished: True if the bar is have finished (reached 100%), False o/w
    227      - start_time: first time update() method of ProgressBar was called
    228      - seconds_elapsed: seconds elapsed since start_time
    229      - percentage(): percentage of the progress (this is a method)
    230      """
    231 -    def __init__(self, maxval=100, widgets=default_widgets, term_width=79,
    232                   fd=sys.stderr, force_update=False):
    233          assert maxval > 0
    234          self.maxval = maxval
    235          self.widgets = widgets
    236          self.fd = fd
    237          self.signal_set = False
    238          if term_width is None:
    239              try:
    240                  self.handle_resize(None, None)
    241                  signal.signal(signal.SIGWINCH, self.handle_resize)
    242                  self.signal_set = True
    243              except:
    244                  self.term_width = 79
    245          else:
    246              self.term_width = term_width
    247
    248          self.currval = 0
    249          self.finished = False
    250          self.prev_percentage = -1
    251          self.start_time = None
    252          self.seconds_elapsed = 0
    253          self.force_update = force_update
    254
    255 -    def handle_resize(self, signum, frame):
    256          h, w = array('h', ioctl(self.fd, termios.TIOCGWINSZ, '\0' * 8))[:2]
    257          self.term_width = w
    258
    259 -    def percentage(self):
    260          "Returns the percentage of the progress."
    261          return self.currval * 100.0 / self.maxval
    262
    263 -    def _format_widgets(self):
    264          r = []
    265          hfill_inds = []
    266          num_hfill = 0
    267          currwidth = 0
    268          for i, w in enumerate(self.widgets):
    269              if isinstance(w, ProgressBarWidgetHFill):
    270                  r.append(w)
    271                  hfill_inds.append(i)
    272                  num_hfill += 1
    273              elif isinstance(w, (str, unicode)):
    274                  r.append(w)
    275                  currwidth += len(w)
    276              else:
    277                  weval = w.update(self)
    278                  currwidth += len(weval)
    279                  r.append(weval)
    280          for iw in hfill_inds:
    281              r[iw] = r[iw].update(self,
    282                                   (self.term_width - currwidth) / num_hfill)
    283          return r
    284
    285 -    def _format_line(self):
    286          return ''.join(self._format_widgets()).ljust(self.term_width)
    287
    288 -    def _need_update(self):
    289          if self.force_update:
    290              return True
    291          return int(self.percentage()) != int(self.prev_percentage)
    292
    293 -    def reset(self):
    294          if not self.finished and self.start_time:
    295              self.finish()
    296          self.finished = False
    297          self.currval = 0
    298          self.start_time = None
    299          self.seconds_elapsed = None
    300          self.prev_percentage = None
    301          return self
    302
    303 -    def update(self, value):
    304          "Updates the progress bar to a new value."
    305          assert 0 <= value <= self.maxval
    306          self.currval = value
    307          if not self._need_update() or self.finished:
    308              return
    309          if not self.start_time:
    310              self.start_time = time.time()
    311          self.seconds_elapsed = time.time() - self.start_time
    312          self.prev_percentage = self.percentage()
    313          if value != self.maxval:
    314              self.fd.write(self._format_line() + '\r')
    315          else:
    316              self.finished = True
    317              self.fd.write(self._format_line() + '\n')
    318
    319 -    def start(self):
    320          """Start measuring time, and prints the bar at 0%.
    321
    322          It returns self so you can use it like this:
    323          >>> pbar = ProgressBar().start()
    324          >>> for i in xrange(100):
    325          ...    # do something
    326          ...    pbar.update(i+1)
    327          ...
    328          >>> pbar.finish()
    329          """
    330          self.update(0)
    331          return self
    332
    333 -    def finish(self):
    334          """Used to tell the progress is finished."""
    335          self.update(self.maxval)
    336          if self.signal_set:
    337              signal.signal(signal.SIGWINCH, signal.SIG_DFL)
    338
    339
    340 -def example1():
    341      widgets = ['Test: ', Percentage(), ' ', Bar(marker=RotatingMarker()),
    342                 ' ', ETA(), ' ', FileTransferSpeed()]
    343      pbar = ProgressBar(widgets=widgets, maxval=10000000).start()
    344      for i in range(1000000):
    345          # do something
    346          pbar.update(10 * i + 1)
    347      pbar.finish()
    348      return pbar
    349
    350
    351 -def example2():
    352      class CrazyFileTransferSpeed(FileTransferSpeed):
    353          "It's bigger between 45 and 80 percent"
    354          def update(self, pbar):
    355              if 45 < pbar.percentage() < 80:
    356                  return 'Bigger Now ' + FileTransferSpeed.update(self, pbar)
    357              else:
    358                  return FileTransferSpeed.update(self, pbar)
    359
    360      widgets = [CrazyFileTransferSpeed(), ' <<<',
    361                 Bar(), '>>> ', Percentage(), ' ', ETA()]
    362      pbar = ProgressBar(widgets=widgets, maxval=10000000)
    363      # maybe do something
    364      pbar.start()
    365      for i in range(2000000):
    366          # do something
    367          pbar.update(5 * i + 1)
    368      pbar.finish()
    369      return pbar
    370
    371
    372 -def example3():
    373      widgets = [Bar('>'), ' ', ETA(), ' ', ReverseBar('<')]
    374      pbar = ProgressBar(widgets=widgets, maxval=10000000).start()
    375      for i in range(1000000):
    376          # do something
    377          pbar.update(10 * i + 1)
    378      pbar.finish()
    379      return pbar
    380
    381
    382 -def example4():
    383      widgets = ['Test: ', Percentage(), ' ',
    384                 Bar(marker='0', left='[', right=']'),
    385                 ' ', ETA(), ' ', FileTransferSpeed()]
    386      pbar = ProgressBar(widgets=widgets, maxval=500)
    387      pbar.start()
    388      for i in range(100, 500 + 1, 50):
    389          time.sleep(0.2)
    390          pbar.update(i)
    391      pbar.finish()
    392      return pbar
    393
    394
    395 -def example5():
    396      widgets = ['Test: ', Fraction(), ' ', Bar(marker=RotatingMarker()),
    397                 ' ', ETA(), ' ', FileTransferSpeed()]
    398      pbar = ProgressBar(widgets=widgets, maxval=10, force_update=True).start()
    399      for i in range(1, 11):
    400          # do something
    401          time.sleep(0.5)
    402          pbar.update(i)
    403      pbar.finish()
    404      return pbar
    405
    406
    407 -def main():
    408      example1()
    409      print
    410      example2()
    411      print
    412      example3()
    413      print
    414      example4()
    415      print
    416      example5()
    417      print
    418
    419  if __name__ == '__main__':
    420      main()
    421