diff options
| -rw-r--r-- | bitbake/lib/bb/ui/knotty.py | 2 | ||||
| -rw-r--r-- | bitbake/lib/progressbar.py | 384 | ||||
| -rw-r--r-- | bitbake/lib/progressbar/LICENSE.txt | 52 | ||||
| -rw-r--r-- | bitbake/lib/progressbar/__init__.py | 49 | ||||
| -rw-r--r-- | bitbake/lib/progressbar/compat.py | 44 | ||||
| -rw-r--r-- | bitbake/lib/progressbar/progressbar.py | 307 | ||||
| -rw-r--r-- | bitbake/lib/progressbar/widgets.py | 355 |
7 files changed, 808 insertions, 385 deletions
diff --git a/bitbake/lib/bb/ui/knotty.py b/bitbake/lib/bb/ui/knotty.py index ddd36d50f6..6a6f6888e3 100644 --- a/bitbake/lib/bb/ui/knotty.py +++ b/bitbake/lib/bb/ui/knotty.py | |||
| @@ -51,7 +51,7 @@ class BBProgress(progressbar.ProgressBar): | |||
| 51 | self._resize_default = None | 51 | self._resize_default = None |
| 52 | progressbar.ProgressBar.__init__(self, maxval, [self.msg + ": "] + widgets, fd=sys.stdout) | 52 | progressbar.ProgressBar.__init__(self, maxval, [self.msg + ": "] + widgets, fd=sys.stdout) |
| 53 | 53 | ||
| 54 | def _handle_resize(self, signum, frame): | 54 | def _handle_resize(self, signum=None, frame=None): |
| 55 | progressbar.ProgressBar._handle_resize(self, signum, frame) | 55 | progressbar.ProgressBar._handle_resize(self, signum, frame) |
| 56 | if self._resize_default: | 56 | if self._resize_default: |
| 57 | self._resize_default(signum, frame) | 57 | self._resize_default(signum, frame) |
diff --git a/bitbake/lib/progressbar.py b/bitbake/lib/progressbar.py deleted file mode 100644 index 114cdc16ba..0000000000 --- a/bitbake/lib/progressbar.py +++ /dev/null | |||
| @@ -1,384 +0,0 @@ | |||
| 1 | #!/usr/bin/env 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 | from __future__ import division | ||
| 43 | |||
| 44 | __author__ = "Nilton Volpato" | ||
| 45 | __author_email__ = "first-name dot last-name @ gmail.com" | ||
| 46 | __date__ = "2006-05-07" | ||
| 47 | __version__ = "2.3-dev" | ||
| 48 | |||
| 49 | import sys, time, os | ||
| 50 | from array import array | ||
| 51 | try: | ||
| 52 | from fcntl import ioctl | ||
| 53 | import termios | ||
| 54 | except ImportError: | ||
| 55 | pass | ||
| 56 | import signal | ||
| 57 | try: | ||
| 58 | basestring | ||
| 59 | except NameError: | ||
| 60 | basestring = (str,) | ||
| 61 | |||
| 62 | class ProgressBarWidget(object): | ||
| 63 | """This is an element of ProgressBar formatting. | ||
| 64 | |||
| 65 | The ProgressBar object will call it's update value when an update | ||
| 66 | is needed. It's size may change between call, but the results will | ||
| 67 | not be good if the size changes drastically and repeatedly. | ||
| 68 | """ | ||
| 69 | def update(self, pbar): | ||
| 70 | """Returns the string representing the widget. | ||
| 71 | |||
| 72 | The parameter pbar is a reference to the calling ProgressBar, | ||
| 73 | where one can access attributes of the class for knowing how | ||
| 74 | the update must be made. | ||
| 75 | |||
| 76 | At least this function must be overriden.""" | ||
| 77 | pass | ||
| 78 | |||
| 79 | class ProgressBarWidgetHFill(object): | ||
| 80 | """This is a variable width element of ProgressBar formatting. | ||
| 81 | |||
| 82 | The ProgressBar object will call it's update value, informing the | ||
| 83 | width this object must the made. This is like TeX \\hfill, it will | ||
| 84 | expand to fill the line. You can use more than one in the same | ||
| 85 | line, and they will all have the same width, and together will | ||
| 86 | fill the line. | ||
| 87 | """ | ||
| 88 | def update(self, pbar, width): | ||
| 89 | """Returns the string representing the widget. | ||
| 90 | |||
| 91 | The parameter pbar is a reference to the calling ProgressBar, | ||
| 92 | where one can access attributes of the class for knowing how | ||
| 93 | the update must be made. The parameter width is the total | ||
| 94 | horizontal width the widget must have. | ||
| 95 | |||
| 96 | At least this function must be overriden.""" | ||
| 97 | pass | ||
| 98 | |||
| 99 | |||
| 100 | class ETA(ProgressBarWidget): | ||
| 101 | "Widget for the Estimated Time of Arrival" | ||
| 102 | def format_time(self, seconds): | ||
| 103 | return time.strftime('%H:%M:%S', time.gmtime(seconds)) | ||
| 104 | def update(self, pbar): | ||
| 105 | if pbar.currval == 0: | ||
| 106 | return 'ETA: --:--:--' | ||
| 107 | elif pbar.finished: | ||
| 108 | return 'Time: %s' % self.format_time(pbar.seconds_elapsed) | ||
| 109 | else: | ||
| 110 | elapsed = pbar.seconds_elapsed | ||
| 111 | eta = elapsed * pbar.maxval / pbar.currval - elapsed | ||
| 112 | return 'ETA: %s' % self.format_time(eta) | ||
| 113 | |||
| 114 | class FileTransferSpeed(ProgressBarWidget): | ||
| 115 | "Widget for showing the transfer speed (useful for file transfers)." | ||
| 116 | def __init__(self, unit='B'): | ||
| 117 | self.unit = unit | ||
| 118 | self.fmt = '%6.2f %s' | ||
| 119 | self.prefixes = ['', 'K', 'M', 'G', 'T', 'P'] | ||
| 120 | def update(self, pbar): | ||
| 121 | if pbar.seconds_elapsed < 2e-6:#== 0: | ||
| 122 | bps = 0.0 | ||
| 123 | else: | ||
| 124 | bps = pbar.currval / pbar.seconds_elapsed | ||
| 125 | spd = bps | ||
| 126 | for u in self.prefixes: | ||
| 127 | if spd < 1000: | ||
| 128 | break | ||
| 129 | spd /= 1000 | ||
| 130 | return self.fmt % (spd, u + self.unit + '/s') | ||
| 131 | |||
| 132 | class RotatingMarker(ProgressBarWidget): | ||
| 133 | "A rotating marker for filling the bar of progress." | ||
| 134 | def __init__(self, markers='|/-\\'): | ||
| 135 | self.markers = markers | ||
| 136 | self.curmark = -1 | ||
| 137 | def update(self, pbar): | ||
| 138 | if pbar.finished: | ||
| 139 | return self.markers[0] | ||
| 140 | self.curmark = (self.curmark + 1) % len(self.markers) | ||
| 141 | return self.markers[self.curmark] | ||
| 142 | |||
| 143 | class Percentage(ProgressBarWidget): | ||
| 144 | "Just the percentage done." | ||
| 145 | def update(self, pbar): | ||
| 146 | return '%3d%%' % pbar.percentage() | ||
| 147 | |||
| 148 | class SimpleProgress(ProgressBarWidget): | ||
| 149 | "Returns what is already done and the total, e.g.: '5 of 47'" | ||
| 150 | def __init__(self, sep=' of '): | ||
| 151 | self.sep = sep | ||
| 152 | def update(self, pbar): | ||
| 153 | return '%d%s%d' % (pbar.currval, self.sep, pbar.maxval) | ||
| 154 | |||
| 155 | class Bar(ProgressBarWidgetHFill): | ||
| 156 | "The bar of progress. It will stretch to fill the line." | ||
| 157 | def __init__(self, marker='#', left='|', right='|'): | ||
| 158 | self.marker = marker | ||
| 159 | self.left = left | ||
| 160 | self.right = right | ||
| 161 | def _format_marker(self, pbar): | ||
| 162 | if isinstance(self.marker, basestring): | ||
| 163 | return self.marker | ||
| 164 | else: | ||
| 165 | return self.marker.update(pbar) | ||
| 166 | def update(self, pbar, width): | ||
| 167 | percent = pbar.percentage() | ||
| 168 | cwidth = width - len(self.left) - len(self.right) | ||
| 169 | marked_width = int(percent * cwidth // 100) | ||
| 170 | m = self._format_marker(pbar) | ||
| 171 | bar = (self.left + (m * marked_width).ljust(cwidth) + self.right) | ||
| 172 | return bar | ||
| 173 | |||
| 174 | class ReverseBar(Bar): | ||
| 175 | "The reverse bar of progress, or bar of regress. :)" | ||
| 176 | def update(self, pbar, width): | ||
| 177 | percent = pbar.percentage() | ||
| 178 | cwidth = width - len(self.left) - len(self.right) | ||
| 179 | marked_width = int(percent * cwidth // 100) | ||
| 180 | m = self._format_marker(pbar) | ||
| 181 | bar = (self.left + (m*marked_width).rjust(cwidth) + self.right) | ||
| 182 | return bar | ||
| 183 | |||
| 184 | default_widgets = [Percentage(), ' ', Bar()] | ||
| 185 | class ProgressBar(object): | ||
| 186 | """This is the ProgressBar class, it updates and prints the bar. | ||
| 187 | |||
| 188 | A common way of using it is like: | ||
| 189 | >>> pbar = ProgressBar().start() | ||
| 190 | >>> for i in xrange(100): | ||
| 191 | ... # do something | ||
| 192 | ... pbar.update(i+1) | ||
| 193 | ... | ||
| 194 | >>> pbar.finish() | ||
| 195 | |||
| 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 | |||
| 202 | But anything you want to do is possible (well, almost anything). | ||
| 203 | You can supply different widgets of any type in any order. And you | ||
| 204 | can even write your own widgets! There are many widgets already | ||
| 205 | shipped and you should experiment with them. | ||
| 206 | |||
| 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 | |||
| 210 | When implementing a widget update method you may access any | ||
| 211 | attribute or function of the ProgressBar object calling the | ||
| 212 | widget's update method. The most important attributes you would | ||
| 213 | like to access are: | ||
| 214 | - currval: current value of the progress, 0 <= currval <= maxval | ||
| 215 | - maxval: maximum (and final) value of the progress | ||
| 216 | - finished: True if the bar has finished (reached 100%), False o/w | ||
| 217 | - start_time: the time when start() method of ProgressBar was called | ||
| 218 | - seconds_elapsed: seconds elapsed since start_time | ||
| 219 | - percentage(): percentage of the progress [0..100]. This is a method. | ||
| 220 | |||
| 221 | The attributes above are unlikely to change between different versions, | ||
| 222 | the other ones may change or cease to exist without notice, so try to rely | ||
| 223 | only on the ones documented above if you are extending the progress bar. | ||
| 224 | """ | ||
| 225 | |||
| 226 | __slots__ = ('currval', 'fd', 'finished', 'last_update_time', 'maxval', | ||
| 227 | 'next_update', 'num_intervals', 'seconds_elapsed', | ||
| 228 | 'signal_set', 'start_time', 'term_width', 'update_interval', | ||
| 229 | 'widgets', '_iterable') | ||
| 230 | |||
| 231 | _DEFAULT_MAXVAL = 100 | ||
| 232 | |||
| 233 | def __init__(self, maxval=None, widgets=default_widgets, term_width=None, | ||
| 234 | fd=sys.stderr): | ||
| 235 | self.maxval = maxval | ||
| 236 | self.widgets = widgets | ||
| 237 | self.fd = fd | ||
| 238 | self.signal_set = False | ||
| 239 | if term_width is not None: | ||
| 240 | self.term_width = term_width | ||
| 241 | else: | ||
| 242 | try: | ||
| 243 | self._handle_resize(None, None) | ||
| 244 | signal.signal(signal.SIGWINCH, self._handle_resize) | ||
| 245 | self.signal_set = True | ||
| 246 | except (SystemExit, KeyboardInterrupt): | ||
| 247 | raise | ||
| 248 | except: | ||
| 249 | self.term_width = int(os.environ.get('COLUMNS', 80)) - 1 | ||
| 250 | |||
| 251 | self.currval = 0 | ||
| 252 | self.finished = False | ||
| 253 | self.start_time = None | ||
| 254 | self.last_update_time = None | ||
| 255 | 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 | ||
| 282 | |||
| 283 | def _handle_resize(self, signum, frame): | ||
| 284 | h, w = array('h', ioctl(self.fd, termios.TIOCGWINSZ, '\0' * 8))[:2] | ||
| 285 | self.term_width = w | ||
| 286 | |||
| 287 | def percentage(self): | ||
| 288 | "Returns the percentage of the progress." | ||
| 289 | return self.currval * 100.0 / self.maxval | ||
| 290 | |||
| 291 | def _format_widgets(self): | ||
| 292 | r = [] | ||
| 293 | hfill_inds = [] | ||
| 294 | num_hfill = 0 | ||
| 295 | currwidth = 0 | ||
| 296 | for i, w in enumerate(self.widgets): | ||
| 297 | if isinstance(w, ProgressBarWidgetHFill): | ||
| 298 | r.append(w) | ||
| 299 | hfill_inds.append(i) | ||
| 300 | num_hfill += 1 | ||
| 301 | elif isinstance(w, basestring): | ||
| 302 | r.append(w) | ||
| 303 | currwidth += len(w) | ||
| 304 | else: | ||
| 305 | weval = w.update(self) | ||
| 306 | currwidth += len(weval) | ||
| 307 | r.append(weval) | ||
| 308 | for iw in hfill_inds: | ||
| 309 | widget_width = int((self.term_width - currwidth) // num_hfill) | ||
| 310 | r[iw] = r[iw].update(self, widget_width) | ||
| 311 | return r | ||
| 312 | |||
| 313 | def _format_line(self): | ||
| 314 | return ''.join(self._format_widgets()).ljust(self.term_width) | ||
| 315 | |||
| 316 | def _next_update(self): | ||
| 317 | return int((int(self.num_intervals * | ||
| 318 | (self.currval / self.maxval)) + 1) * | ||
| 319 | self.update_interval) | ||
| 320 | |||
| 321 | def _need_update(self): | ||
| 322 | """Returns true when the progressbar should print an updated line. | ||
| 323 | |||
| 324 | You can override this method if you want finer grained control over | ||
| 325 | updates. | ||
| 326 | |||
| 327 | The current implementation is optimized to be as fast as possible and | ||
| 328 | as economical as possible in the number of updates. However, depending | ||
| 329 | on your usage you may want to do more updates. For instance, if your | ||
| 330 | progressbar stays in the same percentage for a long time, and you want | ||
| 331 | to update other widgets, like ETA, then you could return True after | ||
| 332 | some time has passed with no updates. | ||
| 333 | |||
| 334 | Ideally you could call self._format_line() and see if it's different | ||
| 335 | from the previous _format_line() call, but calling _format_line() takes | ||
| 336 | around 20 times more time than calling this implementation of | ||
| 337 | _need_update(). | ||
| 338 | """ | ||
| 339 | return self.currval >= self.next_update | ||
| 340 | |||
| 341 | def update(self, value): | ||
| 342 | "Updates the progress bar to a new value." | ||
| 343 | assert 0 <= value <= self.maxval, '0 <= %d <= %d' % (value, self.maxval) | ||
| 344 | self.currval = value | ||
| 345 | if not self._need_update(): | ||
| 346 | return | ||
| 347 | if self.start_time is None: | ||
| 348 | raise RuntimeError('You must call start() before calling update()') | ||
| 349 | now = time.time() | ||
| 350 | self.seconds_elapsed = now - self.start_time | ||
| 351 | self.next_update = self._next_update() | ||
| 352 | self.fd.write(self._format_line() + '\r') | ||
| 353 | self.last_update_time = now | ||
| 354 | |||
| 355 | def start(self): | ||
| 356 | """Starts measuring time, and prints the bar at 0%. | ||
| 357 | |||
| 358 | It returns self so you can use it like this: | ||
| 359 | >>> pbar = ProgressBar().start() | ||
| 360 | >>> for i in xrange(100): | ||
| 361 | ... # do something | ||
| 362 | ... pbar.update(i+1) | ||
| 363 | ... | ||
| 364 | >>> pbar.finish() | ||
| 365 | """ | ||
| 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 | |||
| 374 | self.start_time = self.last_update_time = time.time() | ||
| 375 | self.update(0) | ||
| 376 | return self | ||
| 377 | |||
| 378 | def finish(self): | ||
| 379 | """Used to tell the progress is finished.""" | ||
| 380 | self.finished = True | ||
| 381 | self.update(self.maxval) | ||
| 382 | self.fd.write('\n') | ||
| 383 | if self.signal_set: | ||
| 384 | signal.signal(signal.SIGWINCH, signal.SIG_DFL) | ||
diff --git a/bitbake/lib/progressbar/LICENSE.txt b/bitbake/lib/progressbar/LICENSE.txt new file mode 100644 index 0000000000..fc8ccdc1c1 --- /dev/null +++ b/bitbake/lib/progressbar/LICENSE.txt | |||
| @@ -0,0 +1,52 @@ | |||
| 1 | You can redistribute and/or modify this library under the terms of the | ||
| 2 | GNU LGPL license or BSD license (or both). | ||
| 3 | |||
| 4 | --- | ||
| 5 | |||
| 6 | progressbar - Text progress bar library for python. | ||
| 7 | Copyright (C) 2005 Nilton Volpato | ||
| 8 | |||
| 9 | This library is free software; you can redistribute it and/or | ||
| 10 | modify it under the terms of the GNU Lesser General Public | ||
| 11 | License as published by the Free Software Foundation; either | ||
| 12 | version 2.1 of the License, or (at your option) any later version. | ||
| 13 | |||
| 14 | This library is distributed in the hope that it will be useful, | ||
| 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 17 | Lesser General Public License for more details. | ||
| 18 | |||
| 19 | You should have received a copy of the GNU Lesser General Public | ||
| 20 | License along with this library; if not, write to the Free Software | ||
| 21 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 22 | |||
| 23 | --- | ||
| 24 | |||
| 25 | progressbar - Text progress bar library for python | ||
| 26 | Copyright (c) 2008 Nilton Volpato | ||
| 27 | |||
| 28 | All rights reserved. | ||
| 29 | |||
| 30 | Redistribution and use in source and binary forms, with or without | ||
| 31 | modification, are permitted provided that the following conditions are met: | ||
| 32 | |||
| 33 | a. Redistributions of source code must retain the above copyright notice, | ||
| 34 | this list of conditions and the following disclaimer. | ||
| 35 | b. Redistributions in binary form must reproduce the above copyright | ||
| 36 | notice, this list of conditions and the following disclaimer in the | ||
| 37 | documentation and/or other materials provided with the distribution. | ||
| 38 | c. Neither the name of the author nor the names of its contributors | ||
| 39 | may be used to endorse or promote products derived from this software | ||
| 40 | without specific prior written permission. | ||
| 41 | |||
| 42 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||
| 43 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
| 44 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
| 45 | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR | ||
| 46 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
| 47 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||
| 48 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||
| 49 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||
| 50 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||
| 51 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | ||
| 52 | DAMAGE. | ||
diff --git a/bitbake/lib/progressbar/__init__.py b/bitbake/lib/progressbar/__init__.py new file mode 100644 index 0000000000..fbab744ee2 --- /dev/null +++ b/bitbake/lib/progressbar/__init__.py | |||
| @@ -0,0 +1,49 @@ | |||
| 1 | #!/usr/bin/python | ||
| 2 | # -*- coding: utf-8 -*- | ||
| 3 | # | ||
| 4 | # progressbar - Text progress bar 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 | """Text progress bar library for Python. | ||
| 22 | |||
| 23 | A text progress bar is typically used to display the progress of a long | ||
| 24 | running operation, providing a visual cue that processing is underway. | ||
| 25 | |||
| 26 | The ProgressBar class manages the current progress, and the format of the line | ||
| 27 | is given by a number of widgets. A widget is an object that may display | ||
| 28 | differently depending on the state of the progress bar. There are three types | ||
| 29 | of widgets: | ||
| 30 | - a string, which always shows itself | ||
| 31 | |||
| 32 | - a ProgressBarWidget, which may return a different value every time its | ||
| 33 | update method is called | ||
| 34 | |||
| 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. It will also | ||
| 39 | automatically enable features like auto-resizing when the system supports it. | ||
| 40 | """ | ||
| 41 | |||
| 42 | __author__ = 'Nilton Volpato' | ||
| 43 | __author_email__ = 'first-name dot last-name @ gmail.com' | ||
| 44 | __date__ = '2011-05-14' | ||
| 45 | __version__ = '2.3' | ||
| 46 | |||
| 47 | from .compat import * | ||
| 48 | from .widgets import * | ||
| 49 | from .progressbar import * | ||
diff --git a/bitbake/lib/progressbar/compat.py b/bitbake/lib/progressbar/compat.py new file mode 100644 index 0000000000..a39f4a1f4e --- /dev/null +++ b/bitbake/lib/progressbar/compat.py | |||
| @@ -0,0 +1,44 @@ | |||
| 1 | # -*- coding: utf-8 -*- | ||
| 2 | # | ||
| 3 | # progressbar - Text progress bar library for Python. | ||
| 4 | # Copyright (c) 2005 Nilton Volpato | ||
| 5 | # | ||
| 6 | # This library is free software; you can redistribute it and/or | ||
| 7 | # modify it under the terms of the GNU Lesser General Public | ||
| 8 | # License as published by the Free Software Foundation; either | ||
| 9 | # version 2.1 of the License, or (at your option) any later version. | ||
| 10 | # | ||
| 11 | # This library is distributed in the hope that it will be useful, | ||
| 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 14 | # Lesser General Public License for more details. | ||
| 15 | # | ||
| 16 | # You should have received a copy of the GNU Lesser General Public | ||
| 17 | # License along with this library; if not, write to the Free Software | ||
| 18 | # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 19 | |||
| 20 | """Compatibility methods and classes for the progressbar module.""" | ||
| 21 | |||
| 22 | |||
| 23 | # Python 3.x (and backports) use a modified iterator syntax | ||
| 24 | # This will allow 2.x to behave with 3.x iterators | ||
| 25 | try: | ||
| 26 | next | ||
| 27 | except NameError: | ||
| 28 | def next(iter): | ||
| 29 | try: | ||
| 30 | # Try new style iterators | ||
| 31 | return iter.__next__() | ||
| 32 | except AttributeError: | ||
| 33 | # Fallback in case of a "native" iterator | ||
| 34 | return iter.next() | ||
| 35 | |||
| 36 | |||
| 37 | # Python < 2.5 does not have "any" | ||
| 38 | try: | ||
| 39 | any | ||
| 40 | except NameError: | ||
| 41 | def any(iterator): | ||
| 42 | for item in iterator: | ||
| 43 | if item: return True | ||
| 44 | return False | ||
diff --git a/bitbake/lib/progressbar/progressbar.py b/bitbake/lib/progressbar/progressbar.py new file mode 100644 index 0000000000..0b9dcf763e --- /dev/null +++ b/bitbake/lib/progressbar/progressbar.py | |||
| @@ -0,0 +1,307 @@ | |||
| 1 | # -*- coding: utf-8 -*- | ||
| 2 | # | ||
| 3 | # progressbar - Text progress bar library for Python. | ||
| 4 | # Copyright (c) 2005 Nilton Volpato | ||
| 5 | # | ||
| 6 | # This library is free software; you can redistribute it and/or | ||
| 7 | # modify it under the terms of the GNU Lesser General Public | ||
| 8 | # License as published by the Free Software Foundation; either | ||
| 9 | # version 2.1 of the License, or (at your option) any later version. | ||
| 10 | # | ||
| 11 | # This library is distributed in the hope that it will be useful, | ||
| 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 14 | # Lesser General Public License for more details. | ||
| 15 | # | ||
| 16 | # You should have received a copy of the GNU Lesser General Public | ||
| 17 | # License along with this library; if not, write to the Free Software | ||
| 18 | # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 19 | |||
| 20 | """Main ProgressBar class.""" | ||
| 21 | |||
| 22 | from __future__ import division | ||
| 23 | |||
| 24 | import math | ||
| 25 | import os | ||
| 26 | import signal | ||
| 27 | import sys | ||
| 28 | import time | ||
| 29 | |||
| 30 | try: | ||
| 31 | from fcntl import ioctl | ||
| 32 | from array import array | ||
| 33 | import termios | ||
| 34 | except ImportError: | ||
| 35 | pass | ||
| 36 | |||
| 37 | from .compat import * # for: any, next | ||
| 38 | from . import widgets | ||
| 39 | |||
| 40 | |||
| 41 | class UnknownLength: pass | ||
| 42 | |||
| 43 | |||
| 44 | class ProgressBar(object): | ||
| 45 | """The ProgressBar class which updates and prints the bar. | ||
| 46 | |||
| 47 | A common way of using it is like: | ||
| 48 | >>> pbar = ProgressBar().start() | ||
| 49 | >>> for i in range(100): | ||
| 50 | ... # do something | ||
| 51 | ... pbar.update(i+1) | ||
| 52 | ... | ||
| 53 | >>> pbar.finish() | ||
| 54 | |||
| 55 | You can also use a ProgressBar as an iterator: | ||
| 56 | >>> progress = ProgressBar() | ||
| 57 | >>> for i in progress(some_iterable): | ||
| 58 | ... # do something | ||
| 59 | ... | ||
| 60 | |||
| 61 | Since the progress bar is incredibly customizable you can specify | ||
| 62 | different widgets of any type in any order. You can even write your own | ||
| 63 | widgets! However, since there are already a good number of widgets you | ||
| 64 | should probably play around with them before moving on to create your own | ||
| 65 | widgets. | ||
| 66 | |||
| 67 | The term_width parameter represents the current terminal width. If the | ||
| 68 | parameter is set to an integer then the progress bar will use that, | ||
| 69 | otherwise it will attempt to determine the terminal width falling back to | ||
| 70 | 80 columns if the width cannot be determined. | ||
| 71 | |||
| 72 | When implementing a widget's update method you are passed a reference to | ||
| 73 | the current progress bar. As a result, you have access to the | ||
| 74 | ProgressBar's methods and attributes. Although there is nothing preventing | ||
| 75 | you from changing the ProgressBar you should treat it as read only. | ||
| 76 | |||
| 77 | Useful methods and attributes include (Public API): | ||
| 78 | - currval: current progress (0 <= currval <= maxval) | ||
| 79 | - maxval: maximum (and final) value | ||
| 80 | - finished: True if the bar has finished (reached 100%) | ||
| 81 | - start_time: the time when start() method of ProgressBar was called | ||
| 82 | - seconds_elapsed: seconds elapsed since start_time and last call to | ||
| 83 | update | ||
| 84 | - percentage(): progress in percent [0..100] | ||
| 85 | """ | ||
| 86 | |||
| 87 | __slots__ = ('currval', 'fd', 'finished', 'last_update_time', | ||
| 88 | 'left_justify', 'maxval', 'next_update', 'num_intervals', | ||
| 89 | 'poll', 'seconds_elapsed', 'signal_set', 'start_time', | ||
| 90 | 'term_width', 'update_interval', 'widgets', '_time_sensitive', | ||
| 91 | '__iterable') | ||
| 92 | |||
| 93 | _DEFAULT_MAXVAL = 100 | ||
| 94 | _DEFAULT_TERMSIZE = 80 | ||
| 95 | _DEFAULT_WIDGETS = [widgets.Percentage(), ' ', widgets.Bar()] | ||
| 96 | |||
| 97 | def __init__(self, maxval=None, widgets=None, term_width=None, poll=1, | ||
| 98 | left_justify=True, fd=sys.stderr): | ||
| 99 | """Initializes a progress bar with sane defaults.""" | ||
| 100 | |||
| 101 | # Don't share a reference with any other progress bars | ||
| 102 | if widgets is None: | ||
| 103 | widgets = list(self._DEFAULT_WIDGETS) | ||
| 104 | |||
| 105 | self.maxval = maxval | ||
| 106 | self.widgets = widgets | ||
| 107 | self.fd = fd | ||
| 108 | self.left_justify = left_justify | ||
| 109 | |||
| 110 | self.signal_set = False | ||
| 111 | if term_width is not None: | ||
| 112 | self.term_width = term_width | ||
| 113 | else: | ||
| 114 | try: | ||
| 115 | self._handle_resize(None, None) | ||
| 116 | signal.signal(signal.SIGWINCH, self._handle_resize) | ||
| 117 | self.signal_set = True | ||
| 118 | except (SystemExit, KeyboardInterrupt): raise | ||
| 119 | except Exception as e: | ||
| 120 | print("DEBUG 5 %s" % e) | ||
| 121 | self.term_width = self._env_size() | ||
| 122 | |||
| 123 | self.__iterable = None | ||
| 124 | self._update_widgets() | ||
| 125 | self.currval = 0 | ||
| 126 | self.finished = False | ||
| 127 | self.last_update_time = None | ||
| 128 | self.poll = poll | ||
| 129 | self.seconds_elapsed = 0 | ||
| 130 | self.start_time = None | ||
| 131 | self.update_interval = 1 | ||
| 132 | self.next_update = 0 | ||
| 133 | |||
| 134 | |||
| 135 | def __call__(self, iterable): | ||
| 136 | """Use a ProgressBar to iterate through an iterable.""" | ||
| 137 | |||
| 138 | try: | ||
| 139 | self.maxval = len(iterable) | ||
| 140 | except: | ||
| 141 | if self.maxval is None: | ||
| 142 | self.maxval = UnknownLength | ||
| 143 | |||
| 144 | self.__iterable = iter(iterable) | ||
| 145 | return self | ||
| 146 | |||
| 147 | |||
| 148 | def __iter__(self): | ||
| 149 | return self | ||
| 150 | |||
| 151 | |||
| 152 | def __next__(self): | ||
| 153 | try: | ||
| 154 | value = next(self.__iterable) | ||
| 155 | if self.start_time is None: | ||
| 156 | self.start() | ||
| 157 | else: | ||
| 158 | self.update(self.currval + 1) | ||
| 159 | return value | ||
| 160 | except StopIteration: | ||
| 161 | if self.start_time is None: | ||
| 162 | self.start() | ||
| 163 | self.finish() | ||
| 164 | raise | ||
| 165 | |||
| 166 | |||
| 167 | # Create an alias so that Python 2.x won't complain about not being | ||
| 168 | # an iterator. | ||
| 169 | next = __next__ | ||
| 170 | |||
| 171 | |||
| 172 | def _env_size(self): | ||
| 173 | """Tries to find the term_width from the environment.""" | ||
| 174 | |||
| 175 | return int(os.environ.get('COLUMNS', self._DEFAULT_TERMSIZE)) - 1 | ||
| 176 | |||
| 177 | |||
| 178 | def _handle_resize(self, signum=None, frame=None): | ||
| 179 | """Tries to catch resize signals sent from the terminal.""" | ||
| 180 | |||
| 181 | h, w = array('h', ioctl(self.fd, termios.TIOCGWINSZ, '\0' * 8))[:2] | ||
| 182 | self.term_width = w | ||
| 183 | |||
| 184 | |||
| 185 | def percentage(self): | ||
| 186 | """Returns the progress as a percentage.""" | ||
| 187 | if self.currval >= self.maxval: | ||
| 188 | return 100.0 | ||
| 189 | return (self.currval * 100.0 / self.maxval) if self.maxval else 100.00 | ||
| 190 | |||
| 191 | percent = property(percentage) | ||
| 192 | |||
| 193 | |||
| 194 | def _format_widgets(self): | ||
| 195 | result = [] | ||
| 196 | expanding = [] | ||
| 197 | width = self.term_width | ||
| 198 | |||
| 199 | for index, widget in enumerate(self.widgets): | ||
| 200 | if isinstance(widget, widgets.WidgetHFill): | ||
| 201 | result.append(widget) | ||
| 202 | expanding.insert(0, index) | ||
| 203 | else: | ||
| 204 | widget = widgets.format_updatable(widget, self) | ||
| 205 | result.append(widget) | ||
| 206 | width -= len(widget) | ||
| 207 | |||
| 208 | count = len(expanding) | ||
| 209 | while count: | ||
| 210 | portion = max(int(math.ceil(width * 1. / count)), 0) | ||
| 211 | index = expanding.pop() | ||
| 212 | count -= 1 | ||
| 213 | |||
| 214 | widget = result[index].update(self, portion) | ||
| 215 | width -= len(widget) | ||
| 216 | result[index] = widget | ||
| 217 | |||
| 218 | return result | ||
| 219 | |||
| 220 | |||
| 221 | def _format_line(self): | ||
| 222 | """Joins the widgets and justifies the line.""" | ||
| 223 | |||
| 224 | widgets = ''.join(self._format_widgets()) | ||
| 225 | |||
| 226 | if self.left_justify: return widgets.ljust(self.term_width) | ||
| 227 | else: return widgets.rjust(self.term_width) | ||
| 228 | |||
| 229 | |||
| 230 | def _need_update(self): | ||
| 231 | """Returns whether the ProgressBar should redraw the line.""" | ||
| 232 | if self.currval >= self.next_update or self.finished: return True | ||
| 233 | |||
| 234 | delta = time.time() - self.last_update_time | ||
| 235 | return self._time_sensitive and delta > self.poll | ||
| 236 | |||
| 237 | |||
| 238 | def _update_widgets(self): | ||
| 239 | """Checks all widgets for the time sensitive bit.""" | ||
| 240 | |||
| 241 | self._time_sensitive = any(getattr(w, 'TIME_SENSITIVE', False) | ||
| 242 | for w in self.widgets) | ||
| 243 | |||
| 244 | |||
| 245 | def update(self, value=None): | ||
| 246 | """Updates the ProgressBar to a new value.""" | ||
| 247 | |||
| 248 | if value is not None and value is not UnknownLength: | ||
| 249 | if (self.maxval is not UnknownLength | ||
| 250 | and not 0 <= value <= self.maxval): | ||
| 251 | |||
| 252 | raise ValueError('Value out of range') | ||
| 253 | |||
| 254 | self.currval = value | ||
| 255 | |||
| 256 | |||
| 257 | if not self._need_update(): return | ||
| 258 | if self.start_time is None: | ||
| 259 | raise RuntimeError('You must call "start" before calling "update"') | ||
| 260 | |||
| 261 | now = time.time() | ||
| 262 | self.seconds_elapsed = now - self.start_time | ||
| 263 | self.next_update = self.currval + self.update_interval | ||
| 264 | self.fd.write(self._format_line() + '\r') | ||
| 265 | self.fd.flush() | ||
| 266 | self.last_update_time = now | ||
| 267 | |||
| 268 | |||
| 269 | def start(self): | ||
| 270 | """Starts measuring time, and prints the bar at 0%. | ||
| 271 | |||
| 272 | It returns self so you can use it like this: | ||
| 273 | >>> pbar = ProgressBar().start() | ||
| 274 | >>> for i in range(100): | ||
| 275 | ... # do something | ||
| 276 | ... pbar.update(i+1) | ||
| 277 | ... | ||
| 278 | >>> pbar.finish() | ||
| 279 | """ | ||
| 280 | |||
| 281 | if self.maxval is None: | ||
| 282 | self.maxval = self._DEFAULT_MAXVAL | ||
| 283 | |||
| 284 | self.num_intervals = max(100, self.term_width) | ||
| 285 | self.next_update = 0 | ||
| 286 | |||
| 287 | if self.maxval is not UnknownLength: | ||
| 288 | if self.maxval < 0: raise ValueError('Value out of range') | ||
| 289 | self.update_interval = self.maxval / self.num_intervals | ||
| 290 | |||
| 291 | |||
| 292 | self.start_time = self.last_update_time = time.time() | ||
| 293 | self.update(0) | ||
| 294 | |||
| 295 | return self | ||
| 296 | |||
| 297 | |||
| 298 | def finish(self): | ||
| 299 | """Puts the ProgressBar bar in the finished state.""" | ||
| 300 | |||
| 301 | if self.finished: | ||
| 302 | return | ||
| 303 | self.finished = True | ||
| 304 | self.update(self.maxval) | ||
| 305 | self.fd.write('\n') | ||
| 306 | if self.signal_set: | ||
| 307 | signal.signal(signal.SIGWINCH, signal.SIG_DFL) | ||
diff --git a/bitbake/lib/progressbar/widgets.py b/bitbake/lib/progressbar/widgets.py new file mode 100644 index 0000000000..6434ad5591 --- /dev/null +++ b/bitbake/lib/progressbar/widgets.py | |||
| @@ -0,0 +1,355 @@ | |||
| 1 | # -*- coding: utf-8 -*- | ||
| 2 | # | ||
| 3 | # progressbar - Text progress bar library for Python. | ||
| 4 | # Copyright (c) 2005 Nilton Volpato | ||
| 5 | # | ||
| 6 | # This library is free software; you can redistribute it and/or | ||
| 7 | # modify it under the terms of the GNU Lesser General Public | ||
| 8 | # License as published by the Free Software Foundation; either | ||
| 9 | # version 2.1 of the License, or (at your option) any later version. | ||
| 10 | # | ||
| 11 | # This library is distributed in the hope that it will be useful, | ||
| 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 14 | # Lesser General Public License for more details. | ||
| 15 | # | ||
| 16 | # You should have received a copy of the GNU Lesser General Public | ||
| 17 | # License along with this library; if not, write to the Free Software | ||
| 18 | # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 19 | |||
| 20 | """Default ProgressBar widgets.""" | ||
| 21 | |||
| 22 | from __future__ import division | ||
| 23 | |||
| 24 | import datetime | ||
| 25 | import math | ||
| 26 | |||
| 27 | try: | ||
| 28 | from abc import ABCMeta, abstractmethod | ||
| 29 | except ImportError: | ||
| 30 | AbstractWidget = object | ||
| 31 | abstractmethod = lambda fn: fn | ||
| 32 | else: | ||
| 33 | AbstractWidget = ABCMeta('AbstractWidget', (object,), {}) | ||
| 34 | |||
| 35 | |||
| 36 | def format_updatable(updatable, pbar): | ||
| 37 | if hasattr(updatable, 'update'): return updatable.update(pbar) | ||
| 38 | else: return updatable | ||
| 39 | |||
| 40 | |||
| 41 | class Widget(AbstractWidget): | ||
| 42 | """The base class for all widgets. | ||
| 43 | |||
| 44 | The ProgressBar will call the widget's update value when the widget should | ||
| 45 | be updated. The widget's size may change between calls, but the widget may | ||
| 46 | display incorrectly if the size changes drastically and repeatedly. | ||
| 47 | |||
| 48 | The boolean TIME_SENSITIVE informs the ProgressBar that it should be | ||
| 49 | updated more often because it is time sensitive. | ||
| 50 | """ | ||
| 51 | |||
| 52 | TIME_SENSITIVE = False | ||
| 53 | __slots__ = () | ||
| 54 | |||
| 55 | @abstractmethod | ||
| 56 | def update(self, pbar): | ||
| 57 | """Updates the widget. | ||
| 58 | |||
| 59 | pbar - a reference to the calling ProgressBar | ||
| 60 | """ | ||
| 61 | |||
| 62 | |||
| 63 | class WidgetHFill(Widget): | ||
| 64 | """The base class for all variable width widgets. | ||
| 65 | |||
| 66 | This widget is much like the \\hfill command in TeX, it will expand to | ||
| 67 | fill the line. You can use more than one in the same line, and they will | ||
| 68 | all have the same width, and together will fill the line. | ||
| 69 | """ | ||
| 70 | |||
| 71 | @abstractmethod | ||
| 72 | def update(self, pbar, width): | ||
| 73 | """Updates the widget providing the total width the widget must fill. | ||
| 74 | |||
| 75 | pbar - a reference to the calling ProgressBar | ||
| 76 | width - The total width the widget must fill | ||
| 77 | """ | ||
| 78 | |||
| 79 | |||
| 80 | class Timer(Widget): | ||
| 81 | """Widget which displays the elapsed seconds.""" | ||
| 82 | |||
| 83 | __slots__ = ('format_string',) | ||
| 84 | TIME_SENSITIVE = True | ||
| 85 | |||
| 86 | def __init__(self, format='Elapsed Time: %s'): | ||
| 87 | self.format_string = format | ||
| 88 | |||
| 89 | @staticmethod | ||
| 90 | def format_time(seconds): | ||
| 91 | """Formats time as the string "HH:MM:SS".""" | ||
| 92 | |||
| 93 | return str(datetime.timedelta(seconds=int(seconds))) | ||
| 94 | |||
| 95 | |||
| 96 | def update(self, pbar): | ||
| 97 | """Updates the widget to show the elapsed time.""" | ||
| 98 | |||
| 99 | return self.format_string % self.format_time(pbar.seconds_elapsed) | ||
| 100 | |||
| 101 | |||
| 102 | class ETA(Timer): | ||
| 103 | """Widget which attempts to estimate the time of arrival.""" | ||
| 104 | |||
| 105 | TIME_SENSITIVE = True | ||
| 106 | |||
| 107 | def update(self, pbar): | ||
| 108 | """Updates the widget to show the ETA or total time when finished.""" | ||
| 109 | |||
| 110 | if pbar.currval == 0: | ||
| 111 | return 'ETA: --:--:--' | ||
| 112 | elif pbar.finished: | ||
| 113 | return 'Time: %s' % self.format_time(pbar.seconds_elapsed) | ||
| 114 | else: | ||
| 115 | elapsed = pbar.seconds_elapsed | ||
| 116 | eta = elapsed * pbar.maxval / pbar.currval - elapsed | ||
| 117 | return 'ETA: %s' % self.format_time(eta) | ||
| 118 | |||
| 119 | |||
| 120 | class AdaptiveETA(Timer): | ||
| 121 | """Widget which attempts to estimate the time of arrival. | ||
| 122 | |||
| 123 | Uses a weighted average of two estimates: | ||
| 124 | 1) ETA based on the total progress and time elapsed so far | ||
| 125 | 2) ETA based on the progress as per the last 10 update reports | ||
| 126 | |||
| 127 | The weight depends on the current progress so that to begin with the | ||
| 128 | total progress is used and at the end only the most recent progress is | ||
| 129 | used. | ||
| 130 | """ | ||
| 131 | |||
| 132 | TIME_SENSITIVE = True | ||
| 133 | NUM_SAMPLES = 10 | ||
| 134 | |||
| 135 | def _update_samples(self, currval, elapsed): | ||
| 136 | sample = (currval, elapsed) | ||
| 137 | if not hasattr(self, 'samples'): | ||
| 138 | self.samples = [sample] * (self.NUM_SAMPLES + 1) | ||
| 139 | else: | ||
| 140 | self.samples.append(sample) | ||
| 141 | return self.samples.pop(0) | ||
| 142 | |||
| 143 | def _eta(self, maxval, currval, elapsed): | ||
| 144 | return elapsed * maxval / float(currval) - elapsed | ||
| 145 | |||
| 146 | def update(self, pbar): | ||
| 147 | """Updates the widget to show the ETA or total time when finished.""" | ||
| 148 | if pbar.currval == 0: | ||
| 149 | return 'ETA: --:--:--' | ||
| 150 | elif pbar.finished: | ||
| 151 | return 'Time: %s' % self.format_time(pbar.seconds_elapsed) | ||
| 152 | else: | ||
| 153 | elapsed = pbar.seconds_elapsed | ||
| 154 | currval1, elapsed1 = self._update_samples(pbar.currval, elapsed) | ||
| 155 | eta = self._eta(pbar.maxval, pbar.currval, elapsed) | ||
| 156 | if pbar.currval > currval1: | ||
| 157 | etasamp = self._eta(pbar.maxval - currval1, | ||
| 158 | pbar.currval - currval1, | ||
| 159 | elapsed - elapsed1) | ||
| 160 | weight = (pbar.currval / float(pbar.maxval)) ** 0.5 | ||
| 161 | eta = (1 - weight) * eta + weight * etasamp | ||
| 162 | return 'ETA: %s' % self.format_time(eta) | ||
| 163 | |||
| 164 | |||
| 165 | class FileTransferSpeed(Widget): | ||
| 166 | """Widget for showing the transfer speed (useful for file transfers).""" | ||
| 167 | |||
| 168 | FORMAT = '%6.2f %s%s/s' | ||
| 169 | PREFIXES = ' kMGTPEZY' | ||
| 170 | __slots__ = ('unit',) | ||
| 171 | |||
| 172 | def __init__(self, unit='B'): | ||
| 173 | self.unit = unit | ||
| 174 | |||
| 175 | def update(self, pbar): | ||
| 176 | """Updates the widget with the current SI prefixed speed.""" | ||
| 177 | |||
| 178 | if pbar.seconds_elapsed < 2e-6 or pbar.currval < 2e-6: # =~ 0 | ||
| 179 | scaled = power = 0 | ||
| 180 | else: | ||
| 181 | speed = pbar.currval / pbar.seconds_elapsed | ||
| 182 | power = int(math.log(speed, 1000)) | ||
| 183 | scaled = speed / 1000.**power | ||
| 184 | |||
| 185 | return self.FORMAT % (scaled, self.PREFIXES[power], self.unit) | ||
| 186 | |||
| 187 | |||
| 188 | class AnimatedMarker(Widget): | ||
| 189 | """An animated marker for the progress bar which defaults to appear as if | ||
| 190 | it were rotating. | ||
| 191 | """ | ||
| 192 | |||
| 193 | __slots__ = ('markers', 'curmark') | ||
| 194 | |||
| 195 | def __init__(self, markers='|/-\\'): | ||
| 196 | self.markers = markers | ||
| 197 | self.curmark = -1 | ||
| 198 | |||
| 199 | def update(self, pbar): | ||
| 200 | """Updates the widget to show the next marker or the first marker when | ||
| 201 | finished""" | ||
| 202 | |||
| 203 | if pbar.finished: return self.markers[0] | ||
| 204 | |||
| 205 | self.curmark = (self.curmark + 1) % len(self.markers) | ||
| 206 | return self.markers[self.curmark] | ||
| 207 | |||
| 208 | # Alias for backwards compatibility | ||
| 209 | RotatingMarker = AnimatedMarker | ||
| 210 | |||
| 211 | |||
| 212 | class Counter(Widget): | ||
| 213 | """Displays the current count.""" | ||
| 214 | |||
| 215 | __slots__ = ('format_string',) | ||
| 216 | |||
| 217 | def __init__(self, format='%d'): | ||
| 218 | self.format_string = format | ||
| 219 | |||
| 220 | def update(self, pbar): | ||
| 221 | return self.format_string % pbar.currval | ||
| 222 | |||
| 223 | |||
| 224 | class Percentage(Widget): | ||
| 225 | """Displays the current percentage as a number with a percent sign.""" | ||
| 226 | |||
| 227 | def update(self, pbar): | ||
| 228 | return '%3d%%' % pbar.percentage() | ||
| 229 | |||
| 230 | |||
| 231 | class FormatLabel(Timer): | ||
| 232 | """Displays a formatted label.""" | ||
| 233 | |||
| 234 | mapping = { | ||
| 235 | 'elapsed': ('seconds_elapsed', Timer.format_time), | ||
| 236 | 'finished': ('finished', None), | ||
| 237 | 'last_update': ('last_update_time', None), | ||
| 238 | 'max': ('maxval', None), | ||
| 239 | 'seconds': ('seconds_elapsed', None), | ||
| 240 | 'start': ('start_time', None), | ||
| 241 | 'value': ('currval', None) | ||
| 242 | } | ||
| 243 | |||
| 244 | __slots__ = ('format_string',) | ||
| 245 | def __init__(self, format): | ||
| 246 | self.format_string = format | ||
| 247 | |||
| 248 | def update(self, pbar): | ||
| 249 | context = {} | ||
| 250 | for name, (key, transform) in self.mapping.items(): | ||
| 251 | try: | ||
| 252 | value = getattr(pbar, key) | ||
| 253 | |||
| 254 | if transform is None: | ||
| 255 | context[name] = value | ||
| 256 | else: | ||
| 257 | context[name] = transform(value) | ||
| 258 | except: pass | ||
| 259 | |||
| 260 | return self.format_string % context | ||
| 261 | |||
| 262 | |||
| 263 | class SimpleProgress(Widget): | ||
| 264 | """Returns progress as a count of the total (e.g.: "5 of 47").""" | ||
| 265 | |||
| 266 | __slots__ = ('sep',) | ||
| 267 | |||
| 268 | def __init__(self, sep=' of '): | ||
| 269 | self.sep = sep | ||
| 270 | |||
| 271 | def update(self, pbar): | ||
| 272 | return '%d%s%d' % (pbar.currval, self.sep, pbar.maxval) | ||
| 273 | |||
| 274 | |||
| 275 | class Bar(WidgetHFill): | ||
| 276 | """A progress bar which stretches to fill the line.""" | ||
| 277 | |||
| 278 | __slots__ = ('marker', 'left', 'right', 'fill', 'fill_left') | ||
| 279 | |||
| 280 | def __init__(self, marker='#', left='|', right='|', fill=' ', | ||
| 281 | fill_left=True): | ||
| 282 | """Creates a customizable progress bar. | ||
| 283 | |||
| 284 | marker - string or updatable object to use as a marker | ||
| 285 | left - string or updatable object to use as a left border | ||
| 286 | right - string or updatable object to use as a right border | ||
| 287 | fill - character to use for the empty part of the progress bar | ||
| 288 | fill_left - whether to fill from the left or the right | ||
| 289 | """ | ||
| 290 | self.marker = marker | ||
| 291 | self.left = left | ||
| 292 | self.right = right | ||
| 293 | self.fill = fill | ||
| 294 | self.fill_left = fill_left | ||
| 295 | |||
| 296 | |||
| 297 | def update(self, pbar, width): | ||
| 298 | """Updates the progress bar and its subcomponents.""" | ||
| 299 | |||
| 300 | left, marked, right = (format_updatable(i, pbar) for i in | ||
| 301 | (self.left, self.marker, self.right)) | ||
| 302 | |||
| 303 | width -= len(left) + len(right) | ||
| 304 | # Marked must *always* have length of 1 | ||
| 305 | if pbar.maxval: | ||
| 306 | marked *= int(pbar.currval / pbar.maxval * width) | ||
| 307 | else: | ||
| 308 | marked = '' | ||
| 309 | |||
| 310 | if self.fill_left: | ||
| 311 | return '%s%s%s' % (left, marked.ljust(width, self.fill), right) | ||
| 312 | else: | ||
| 313 | return '%s%s%s' % (left, marked.rjust(width, self.fill), right) | ||
| 314 | |||
| 315 | |||
| 316 | class ReverseBar(Bar): | ||
| 317 | """A bar which has a marker which bounces from side to side.""" | ||
| 318 | |||
| 319 | def __init__(self, marker='#', left='|', right='|', fill=' ', | ||
| 320 | fill_left=False): | ||
| 321 | """Creates a customizable progress bar. | ||
| 322 | |||
| 323 | marker - string or updatable object to use as a marker | ||
| 324 | left - string or updatable object to use as a left border | ||
| 325 | right - string or updatable object to use as a right border | ||
| 326 | fill - character to use for the empty part of the progress bar | ||
| 327 | fill_left - whether to fill from the left or the right | ||
| 328 | """ | ||
| 329 | self.marker = marker | ||
| 330 | self.left = left | ||
| 331 | self.right = right | ||
| 332 | self.fill = fill | ||
| 333 | self.fill_left = fill_left | ||
| 334 | |||
| 335 | |||
| 336 | class BouncingBar(Bar): | ||
| 337 | def update(self, pbar, width): | ||
| 338 | """Updates the progress bar and its subcomponents.""" | ||
| 339 | |||
| 340 | left, marker, right = (format_updatable(i, pbar) for i in | ||
| 341 | (self.left, self.marker, self.right)) | ||
| 342 | |||
| 343 | width -= len(left) + len(right) | ||
| 344 | |||
| 345 | if pbar.finished: return '%s%s%s' % (left, width * marker, right) | ||
| 346 | |||
| 347 | position = int(pbar.currval % (width * 2 - 1)) | ||
| 348 | if position > width: position = width * 2 - position | ||
| 349 | lpad = self.fill * (position - 1) | ||
| 350 | rpad = self.fill * (width - len(marker) - len(lpad)) | ||
| 351 | |||
| 352 | # Swap if we want to bounce the other way | ||
| 353 | if not self.fill_left: rpad, lpad = lpad, rpad | ||
| 354 | |||
| 355 | return '%s%s%s%s%s' % (left, lpad, marker, rpad, right) | ||
