@@ -137,7 +137,7 @@ def __init__(self, markers='|/-\\'):
137137 def update (self , pbar ):
138138 if pbar .finished :
139139 return self .markers [0 ]
140- self .curmark = (self .curmark + 1 )% len (self .markers )
140+ self .curmark = (self .curmark + 1 ) % len (self .markers )
141141 return self .markers [self .curmark ]
142142
143143class Percentage (ProgressBarWidget ):
@@ -147,8 +147,10 @@ def update(self, pbar):
147147
148148class SimpleProgress (ProgressBarWidget ):
149149 "Returns what is already done and the total, e.g.: '5 of 47'"
150+ def __init__ (self , sep = ' of ' ):
151+ self .sep = sep
150152 def update (self , pbar ):
151- return '%d of % d' % (pbar .currval , pbar .maxval )
153+ return '%d%s% d' % (pbar .currval , self . sep , pbar .maxval )
152154
153155class Bar (ProgressBarWidgetHFill ):
154156 "The bar of progress. It will stretch to fill the line."
@@ -183,22 +185,28 @@ def update(self, pbar, width):
183185class ProgressBar (object ):
184186 """This is the ProgressBar class, it updates and prints the bar.
185187
186- The term_width parameter must be an integer or None. In the latter case
187- it will try to guess it, if it fails it will default to 80 columns.
188-
189- The simple use is like this:
188+ A common way of using it is like:
190189 >>> pbar = ProgressBar().start()
191190 >>> for i in xrange(100):
192191 ... # do something
193192 ... pbar.update(i+1)
194193 ...
195194 >>> pbar.finish()
196195
196+ You can also use a progressbar as an iterator:
197+ >>> progress = ProgressBar()
198+ >>> for i in progress(some_iterable):
199+ ... # do something
200+ ...
201+
197202 But anything you want to do is possible (well, almost anything).
198203 You can supply different widgets of any type in any order. And you
199204 can even write your own widgets! There are many widgets already
200205 shipped and you should experiment with them.
201206
207+ The term_width parameter must be an integer or None. In the latter case
208+ it will try to guess it, if it fails it will default to 80 columns.
209+
202210 When implementing a widget update method you may access any
203211 attribute or function of the ProgressBar object calling the
204212 widget's update method. The most important attributes you would
@@ -218,11 +226,12 @@ class ProgressBar(object):
218226 __slots__ = ('currval' , 'fd' , 'finished' , 'last_update_time' , 'maxval' ,
219227 'next_update' , 'num_intervals' , 'seconds_elapsed' ,
220228 'signal_set' , 'start_time' , 'term_width' , 'update_interval' ,
221- 'widgets' )
229+ 'widgets' , '_iterable' )
222230
223- def __init__ (self , maxval = 100 , widgets = default_widgets , term_width = None ,
231+ _DEFAULT_MAXVAL = 100
232+
233+ def __init__ (self , maxval = None , widgets = default_widgets , term_width = None ,
224234 fd = sys .stderr ):
225- assert maxval > 0
226235 self .maxval = maxval
227236 self .widgets = widgets
228237 self .fd = fd
@@ -239,15 +248,37 @@ def __init__(self, maxval=100, widgets=default_widgets, term_width=None,
239248 except :
240249 self .term_width = int (os .environ .get ('COLUMNS' , 80 )) - 1
241250
242- self .num_intervals = max (100 , self .term_width )
243- self .update_interval = self .maxval / self .num_intervals
244- self .next_update = 0
245-
246251 self .currval = 0
247252 self .finished = False
248253 self .start_time = None
249254 self .last_update_time = None
250255 self .seconds_elapsed = 0
256+ self ._iterable = None
257+
258+ def __call__ (self , iterable ):
259+ try :
260+ self .maxval = len (iterable )
261+ except TypeError :
262+ # If the iterable has no length, then rely on the value provided
263+ # by the user, otherwise fail.
264+ if not (isinstance (self .maxval , (int , long )) and self .maxval > 0 ):
265+ raise RuntimeError ('Could not determine maxval from iterable. '
266+ 'You must explicitly provide a maxval.' )
267+ self ._iterable = iter (iterable )
268+ self .start ()
269+ return self
270+
271+ def __iter__ (self ):
272+ return self
273+
274+ def next (self ):
275+ try :
276+ next = self ._iterable .next ()
277+ self .update (self .currval + 1 )
278+ return next
279+ except StopIteration :
280+ self .finish ()
281+ raise
251282
252283 def _handle_resize (self , signum , frame ):
253284 h , w = array ('h' , ioctl (self .fd , termios .TIOCGWINSZ , '\0 ' * 8 ))[:2 ]
@@ -309,7 +340,7 @@ def _need_update(self):
309340
310341 def update (self , value ):
311342 "Updates the progress bar to a new value."
312- assert 0 <= value <= self .maxval
343+ assert 0 <= value <= self .maxval , '0 <= %d <= %d' % ( value , self . maxval )
313344 self .currval = value
314345 if not self ._need_update ():
315346 return
@@ -322,7 +353,7 @@ def update(self, value):
322353 self .last_update_time = now
323354
324355 def start (self ):
325- """Start measuring time, and prints the bar at 0%.
356+ """Starts measuring time, and prints the bar at 0%.
326357
327358 It returns self so you can use it like this:
328359 >>> pbar = ProgressBar().start()
@@ -332,6 +363,14 @@ def start(self):
332363 ...
333364 >>> pbar.finish()
334365 """
366+ if self .maxval is None :
367+ self .maxval = self ._DEFAULT_MAXVAL
368+ assert self .maxval > 0
369+
370+ self .num_intervals = max (100 , self .term_width )
371+ self .update_interval = self .maxval / self .num_intervals
372+ self .next_update = 0
373+
335374 self .start_time = self .last_update_time = time .time ()
336375 self .update (0 )
337376 return self
0 commit comments