summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bitbake/lib/bb/ui/knotty.py2
-rw-r--r--bitbake/lib/progressbar.py384
-rw-r--r--bitbake/lib/progressbar/LICENSE.txt52
-rw-r--r--bitbake/lib/progressbar/__init__.py49
-rw-r--r--bitbake/lib/progressbar/compat.py44
-rw-r--r--bitbake/lib/progressbar/progressbar.py307
-rw-r--r--bitbake/lib/progressbar/widgets.py355
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
24This library provides a text mode progressbar. This is typically used
25to display the progress of a long running operation, providing a
26visual clue that processing is underway.
27
28The ProgressBar class manages the progress, and the format of the line
29is given by a number of widgets. A widget is an object that may
30display diferently depending on the state of the progress. There are
31three types of widget:
32- a string, which always shows itself;
33- a ProgressBarWidget, which may return a diferent value every time
34it's update method is called; and
35- a ProgressBarWidgetHFill, which is like ProgressBarWidget, except it
36expands to fill the remaining width of the line.
37
38The progressbar module is very easy to use, yet very powerful. And
39automatically supports features like auto-resizing when available.
40"""
41
42from __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
49import sys, time, os
50from array import array
51try:
52 from fcntl import ioctl
53 import termios
54except ImportError:
55 pass
56import signal
57try:
58 basestring
59except NameError:
60 basestring = (str,)
61
62class 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
79class 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
100class 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
114class 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
132class 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
143class Percentage(ProgressBarWidget):
144 "Just the percentage done."
145 def update(self, pbar):
146 return '%3d%%' % pbar.percentage()
147
148class 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
155class 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
174class 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
184default_widgets = [Percentage(), ' ', Bar()]
185class 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 @@
1You can redistribute and/or modify this library under the terms of the
2GNU LGPL license or BSD license (or both).
3
4---
5
6progressbar - Text progress bar library for python.
7Copyright (C) 2005 Nilton Volpato
8
9This library is free software; you can redistribute it and/or
10modify it under the terms of the GNU Lesser General Public
11License as published by the Free Software Foundation; either
12version 2.1 of the License, or (at your option) any later version.
13
14This library is distributed in the hope that it will be useful,
15but WITHOUT ANY WARRANTY; without even the implied warranty of
16MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17Lesser General Public License for more details.
18
19You should have received a copy of the GNU Lesser General Public
20License along with this library; if not, write to the Free Software
21Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22
23---
24
25progressbar - Text progress bar library for python
26Copyright (c) 2008 Nilton Volpato
27
28All rights reserved.
29
30Redistribution and use in source and binary forms, with or without
31modification, 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
42THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
43AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
44IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
45ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
46ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
47DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
48SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
49CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
50LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
51OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
52DAMAGE.
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
23A text progress bar is typically used to display the progress of a long
24running operation, providing a visual cue that processing is underway.
25
26The ProgressBar class manages the current progress, and the format of the line
27is given by a number of widgets. A widget is an object that may display
28differently depending on the state of the progress bar. There are three types
29of 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
38The progressbar module is very easy to use, yet very powerful. It will also
39automatically 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
47from .compat import *
48from .widgets import *
49from .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
25try:
26 next
27except 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"
38try:
39 any
40except 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
22from __future__ import division
23
24import math
25import os
26import signal
27import sys
28import time
29
30try:
31 from fcntl import ioctl
32 from array import array
33 import termios
34except ImportError:
35 pass
36
37from .compat import * # for: any, next
38from . import widgets
39
40
41class UnknownLength: pass
42
43
44class 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
22from __future__ import division
23
24import datetime
25import math
26
27try:
28 from abc import ABCMeta, abstractmethod
29except ImportError:
30 AbstractWidget = object
31 abstractmethod = lambda fn: fn
32else:
33 AbstractWidget = ABCMeta('AbstractWidget', (object,), {})
34
35
36def format_updatable(updatable, pbar):
37 if hasattr(updatable, 'update'): return updatable.update(pbar)
38 else: return updatable
39
40
41class 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
63class 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
80class 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
102class 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
120class 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
165class 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
188class 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
209RotatingMarker = AnimatedMarker
210
211
212class 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
224class 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
231class 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
263class 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
275class 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
316class 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
336class 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)