summaryrefslogtreecommitdiffstats
path: root/bitbake/lib/progressbar/progressbar.py
diff options
context:
space:
mode:
Diffstat (limited to 'bitbake/lib/progressbar/progressbar.py')
-rw-r--r--bitbake/lib/progressbar/progressbar.py307
1 files changed, 307 insertions, 0 deletions
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)