jpayne@68
|
1 """
|
jpayne@68
|
2 Customisable progressbar decorator for iterators.
|
jpayne@68
|
3 Includes a default `range` iterator printing to `stderr`.
|
jpayne@68
|
4
|
jpayne@68
|
5 Usage:
|
jpayne@68
|
6 >>> from tqdm import trange, tqdm
|
jpayne@68
|
7 >>> for i in trange(10):
|
jpayne@68
|
8 ... ...
|
jpayne@68
|
9 """
|
jpayne@68
|
10 import sys
|
jpayne@68
|
11 from collections import OrderedDict, defaultdict
|
jpayne@68
|
12 from contextlib import contextmanager
|
jpayne@68
|
13 from datetime import datetime, timedelta, timezone
|
jpayne@68
|
14 from numbers import Number
|
jpayne@68
|
15 from time import time
|
jpayne@68
|
16 from warnings import warn
|
jpayne@68
|
17 from weakref import WeakSet
|
jpayne@68
|
18
|
jpayne@68
|
19 from ._monitor import TMonitor
|
jpayne@68
|
20 from .utils import (
|
jpayne@68
|
21 CallbackIOWrapper, Comparable, DisableOnWriteError, FormatReplace, SimpleTextIOWrapper,
|
jpayne@68
|
22 _is_ascii, _screen_shape_wrapper, _supports_unicode, _term_move_up, disp_len, disp_trim,
|
jpayne@68
|
23 envwrap)
|
jpayne@68
|
24
|
jpayne@68
|
25 __author__ = "https://github.com/tqdm/tqdm#contributions"
|
jpayne@68
|
26 __all__ = ['tqdm', 'trange',
|
jpayne@68
|
27 'TqdmTypeError', 'TqdmKeyError', 'TqdmWarning',
|
jpayne@68
|
28 'TqdmExperimentalWarning', 'TqdmDeprecationWarning',
|
jpayne@68
|
29 'TqdmMonitorWarning']
|
jpayne@68
|
30
|
jpayne@68
|
31
|
jpayne@68
|
32 class TqdmTypeError(TypeError):
|
jpayne@68
|
33 pass
|
jpayne@68
|
34
|
jpayne@68
|
35
|
jpayne@68
|
36 class TqdmKeyError(KeyError):
|
jpayne@68
|
37 pass
|
jpayne@68
|
38
|
jpayne@68
|
39
|
jpayne@68
|
40 class TqdmWarning(Warning):
|
jpayne@68
|
41 """base class for all tqdm warnings.
|
jpayne@68
|
42
|
jpayne@68
|
43 Used for non-external-code-breaking errors, such as garbled printing.
|
jpayne@68
|
44 """
|
jpayne@68
|
45 def __init__(self, msg, fp_write=None, *a, **k):
|
jpayne@68
|
46 if fp_write is not None:
|
jpayne@68
|
47 fp_write("\n" + self.__class__.__name__ + ": " + str(msg).rstrip() + '\n')
|
jpayne@68
|
48 else:
|
jpayne@68
|
49 super().__init__(msg, *a, **k)
|
jpayne@68
|
50
|
jpayne@68
|
51
|
jpayne@68
|
52 class TqdmExperimentalWarning(TqdmWarning, FutureWarning):
|
jpayne@68
|
53 """beta feature, unstable API and behaviour"""
|
jpayne@68
|
54 pass
|
jpayne@68
|
55
|
jpayne@68
|
56
|
jpayne@68
|
57 class TqdmDeprecationWarning(TqdmWarning, DeprecationWarning):
|
jpayne@68
|
58 # not suppressed if raised
|
jpayne@68
|
59 pass
|
jpayne@68
|
60
|
jpayne@68
|
61
|
jpayne@68
|
62 class TqdmMonitorWarning(TqdmWarning, RuntimeWarning):
|
jpayne@68
|
63 """tqdm monitor errors which do not affect external functionality"""
|
jpayne@68
|
64 pass
|
jpayne@68
|
65
|
jpayne@68
|
66
|
jpayne@68
|
67 def TRLock(*args, **kwargs):
|
jpayne@68
|
68 """threading RLock"""
|
jpayne@68
|
69 try:
|
jpayne@68
|
70 from threading import RLock
|
jpayne@68
|
71 return RLock(*args, **kwargs)
|
jpayne@68
|
72 except (ImportError, OSError): # pragma: no cover
|
jpayne@68
|
73 pass
|
jpayne@68
|
74
|
jpayne@68
|
75
|
jpayne@68
|
76 class TqdmDefaultWriteLock(object):
|
jpayne@68
|
77 """
|
jpayne@68
|
78 Provide a default write lock for thread and multiprocessing safety.
|
jpayne@68
|
79 Works only on platforms supporting `fork` (so Windows is excluded).
|
jpayne@68
|
80 You must initialise a `tqdm` or `TqdmDefaultWriteLock` instance
|
jpayne@68
|
81 before forking in order for the write lock to work.
|
jpayne@68
|
82 On Windows, you need to supply the lock from the parent to the children as
|
jpayne@68
|
83 an argument to joblib or the parallelism lib you use.
|
jpayne@68
|
84 """
|
jpayne@68
|
85 # global thread lock so no setup required for multithreading.
|
jpayne@68
|
86 # NB: Do not create multiprocessing lock as it sets the multiprocessing
|
jpayne@68
|
87 # context, disallowing `spawn()`/`forkserver()`
|
jpayne@68
|
88 th_lock = TRLock()
|
jpayne@68
|
89
|
jpayne@68
|
90 def __init__(self):
|
jpayne@68
|
91 # Create global parallelism locks to avoid racing issues with parallel
|
jpayne@68
|
92 # bars works only if fork available (Linux/MacOSX, but not Windows)
|
jpayne@68
|
93 cls = type(self)
|
jpayne@68
|
94 root_lock = cls.th_lock
|
jpayne@68
|
95 if root_lock is not None:
|
jpayne@68
|
96 root_lock.acquire()
|
jpayne@68
|
97 cls.create_mp_lock()
|
jpayne@68
|
98 self.locks = [lk for lk in [cls.mp_lock, cls.th_lock] if lk is not None]
|
jpayne@68
|
99 if root_lock is not None:
|
jpayne@68
|
100 root_lock.release()
|
jpayne@68
|
101
|
jpayne@68
|
102 def acquire(self, *a, **k):
|
jpayne@68
|
103 for lock in self.locks:
|
jpayne@68
|
104 lock.acquire(*a, **k)
|
jpayne@68
|
105
|
jpayne@68
|
106 def release(self):
|
jpayne@68
|
107 for lock in self.locks[::-1]: # Release in inverse order of acquisition
|
jpayne@68
|
108 lock.release()
|
jpayne@68
|
109
|
jpayne@68
|
110 def __enter__(self):
|
jpayne@68
|
111 self.acquire()
|
jpayne@68
|
112
|
jpayne@68
|
113 def __exit__(self, *exc):
|
jpayne@68
|
114 self.release()
|
jpayne@68
|
115
|
jpayne@68
|
116 @classmethod
|
jpayne@68
|
117 def create_mp_lock(cls):
|
jpayne@68
|
118 if not hasattr(cls, 'mp_lock'):
|
jpayne@68
|
119 try:
|
jpayne@68
|
120 from multiprocessing import RLock
|
jpayne@68
|
121 cls.mp_lock = RLock()
|
jpayne@68
|
122 except (ImportError, OSError): # pragma: no cover
|
jpayne@68
|
123 cls.mp_lock = None
|
jpayne@68
|
124
|
jpayne@68
|
125 @classmethod
|
jpayne@68
|
126 def create_th_lock(cls):
|
jpayne@68
|
127 assert hasattr(cls, 'th_lock')
|
jpayne@68
|
128 warn("create_th_lock not needed anymore", TqdmDeprecationWarning, stacklevel=2)
|
jpayne@68
|
129
|
jpayne@68
|
130
|
jpayne@68
|
131 class Bar(object):
|
jpayne@68
|
132 """
|
jpayne@68
|
133 `str.format`-able bar with format specifiers: `[width][type]`
|
jpayne@68
|
134
|
jpayne@68
|
135 - `width`
|
jpayne@68
|
136 + unspecified (default): use `self.default_len`
|
jpayne@68
|
137 + `int >= 0`: overrides `self.default_len`
|
jpayne@68
|
138 + `int < 0`: subtract from `self.default_len`
|
jpayne@68
|
139 - `type`
|
jpayne@68
|
140 + `a`: ascii (`charset=self.ASCII` override)
|
jpayne@68
|
141 + `u`: unicode (`charset=self.UTF` override)
|
jpayne@68
|
142 + `b`: blank (`charset=" "` override)
|
jpayne@68
|
143 """
|
jpayne@68
|
144 ASCII = " 123456789#"
|
jpayne@68
|
145 UTF = u" " + u''.join(map(chr, range(0x258F, 0x2587, -1)))
|
jpayne@68
|
146 BLANK = " "
|
jpayne@68
|
147 COLOUR_RESET = '\x1b[0m'
|
jpayne@68
|
148 COLOUR_RGB = '\x1b[38;2;%d;%d;%dm'
|
jpayne@68
|
149 COLOURS = {'BLACK': '\x1b[30m', 'RED': '\x1b[31m', 'GREEN': '\x1b[32m',
|
jpayne@68
|
150 'YELLOW': '\x1b[33m', 'BLUE': '\x1b[34m', 'MAGENTA': '\x1b[35m',
|
jpayne@68
|
151 'CYAN': '\x1b[36m', 'WHITE': '\x1b[37m'}
|
jpayne@68
|
152
|
jpayne@68
|
153 def __init__(self, frac, default_len=10, charset=UTF, colour=None):
|
jpayne@68
|
154 if not 0 <= frac <= 1:
|
jpayne@68
|
155 warn("clamping frac to range [0, 1]", TqdmWarning, stacklevel=2)
|
jpayne@68
|
156 frac = max(0, min(1, frac))
|
jpayne@68
|
157 assert default_len > 0
|
jpayne@68
|
158 self.frac = frac
|
jpayne@68
|
159 self.default_len = default_len
|
jpayne@68
|
160 self.charset = charset
|
jpayne@68
|
161 self.colour = colour
|
jpayne@68
|
162
|
jpayne@68
|
163 @property
|
jpayne@68
|
164 def colour(self):
|
jpayne@68
|
165 return self._colour
|
jpayne@68
|
166
|
jpayne@68
|
167 @colour.setter
|
jpayne@68
|
168 def colour(self, value):
|
jpayne@68
|
169 if not value:
|
jpayne@68
|
170 self._colour = None
|
jpayne@68
|
171 return
|
jpayne@68
|
172 try:
|
jpayne@68
|
173 if value.upper() in self.COLOURS:
|
jpayne@68
|
174 self._colour = self.COLOURS[value.upper()]
|
jpayne@68
|
175 elif value[0] == '#' and len(value) == 7:
|
jpayne@68
|
176 self._colour = self.COLOUR_RGB % tuple(
|
jpayne@68
|
177 int(i, 16) for i in (value[1:3], value[3:5], value[5:7]))
|
jpayne@68
|
178 else:
|
jpayne@68
|
179 raise KeyError
|
jpayne@68
|
180 except (KeyError, AttributeError):
|
jpayne@68
|
181 warn("Unknown colour (%s); valid choices: [hex (#00ff00), %s]" % (
|
jpayne@68
|
182 value, ", ".join(self.COLOURS)),
|
jpayne@68
|
183 TqdmWarning, stacklevel=2)
|
jpayne@68
|
184 self._colour = None
|
jpayne@68
|
185
|
jpayne@68
|
186 def __format__(self, format_spec):
|
jpayne@68
|
187 if format_spec:
|
jpayne@68
|
188 _type = format_spec[-1].lower()
|
jpayne@68
|
189 try:
|
jpayne@68
|
190 charset = {'a': self.ASCII, 'u': self.UTF, 'b': self.BLANK}[_type]
|
jpayne@68
|
191 except KeyError:
|
jpayne@68
|
192 charset = self.charset
|
jpayne@68
|
193 else:
|
jpayne@68
|
194 format_spec = format_spec[:-1]
|
jpayne@68
|
195 if format_spec:
|
jpayne@68
|
196 N_BARS = int(format_spec)
|
jpayne@68
|
197 if N_BARS < 0:
|
jpayne@68
|
198 N_BARS += self.default_len
|
jpayne@68
|
199 else:
|
jpayne@68
|
200 N_BARS = self.default_len
|
jpayne@68
|
201 else:
|
jpayne@68
|
202 charset = self.charset
|
jpayne@68
|
203 N_BARS = self.default_len
|
jpayne@68
|
204
|
jpayne@68
|
205 nsyms = len(charset) - 1
|
jpayne@68
|
206 bar_length, frac_bar_length = divmod(int(self.frac * N_BARS * nsyms), nsyms)
|
jpayne@68
|
207
|
jpayne@68
|
208 res = charset[-1] * bar_length
|
jpayne@68
|
209 if bar_length < N_BARS: # whitespace padding
|
jpayne@68
|
210 res = res + charset[frac_bar_length] + charset[0] * (N_BARS - bar_length - 1)
|
jpayne@68
|
211 return self.colour + res + self.COLOUR_RESET if self.colour else res
|
jpayne@68
|
212
|
jpayne@68
|
213
|
jpayne@68
|
214 class EMA(object):
|
jpayne@68
|
215 """
|
jpayne@68
|
216 Exponential moving average: smoothing to give progressively lower
|
jpayne@68
|
217 weights to older values.
|
jpayne@68
|
218
|
jpayne@68
|
219 Parameters
|
jpayne@68
|
220 ----------
|
jpayne@68
|
221 smoothing : float, optional
|
jpayne@68
|
222 Smoothing factor in range [0, 1], [default: 0.3].
|
jpayne@68
|
223 Increase to give more weight to recent values.
|
jpayne@68
|
224 Ranges from 0 (yields old value) to 1 (yields new value).
|
jpayne@68
|
225 """
|
jpayne@68
|
226 def __init__(self, smoothing=0.3):
|
jpayne@68
|
227 self.alpha = smoothing
|
jpayne@68
|
228 self.last = 0
|
jpayne@68
|
229 self.calls = 0
|
jpayne@68
|
230
|
jpayne@68
|
231 def __call__(self, x=None):
|
jpayne@68
|
232 """
|
jpayne@68
|
233 Parameters
|
jpayne@68
|
234 ----------
|
jpayne@68
|
235 x : float
|
jpayne@68
|
236 New value to include in EMA.
|
jpayne@68
|
237 """
|
jpayne@68
|
238 beta = 1 - self.alpha
|
jpayne@68
|
239 if x is not None:
|
jpayne@68
|
240 self.last = self.alpha * x + beta * self.last
|
jpayne@68
|
241 self.calls += 1
|
jpayne@68
|
242 return self.last / (1 - beta ** self.calls) if self.calls else self.last
|
jpayne@68
|
243
|
jpayne@68
|
244
|
jpayne@68
|
245 class tqdm(Comparable):
|
jpayne@68
|
246 """
|
jpayne@68
|
247 Decorate an iterable object, returning an iterator which acts exactly
|
jpayne@68
|
248 like the original iterable, but prints a dynamically updating
|
jpayne@68
|
249 progressbar every time a value is requested.
|
jpayne@68
|
250
|
jpayne@68
|
251 Parameters
|
jpayne@68
|
252 ----------
|
jpayne@68
|
253 iterable : iterable, optional
|
jpayne@68
|
254 Iterable to decorate with a progressbar.
|
jpayne@68
|
255 Leave blank to manually manage the updates.
|
jpayne@68
|
256 desc : str, optional
|
jpayne@68
|
257 Prefix for the progressbar.
|
jpayne@68
|
258 total : int or float, optional
|
jpayne@68
|
259 The number of expected iterations. If unspecified,
|
jpayne@68
|
260 len(iterable) is used if possible. If float("inf") or as a last
|
jpayne@68
|
261 resort, only basic progress statistics are displayed
|
jpayne@68
|
262 (no ETA, no progressbar).
|
jpayne@68
|
263 If `gui` is True and this parameter needs subsequent updating,
|
jpayne@68
|
264 specify an initial arbitrary large positive number,
|
jpayne@68
|
265 e.g. 9e9.
|
jpayne@68
|
266 leave : bool, optional
|
jpayne@68
|
267 If [default: True], keeps all traces of the progressbar
|
jpayne@68
|
268 upon termination of iteration.
|
jpayne@68
|
269 If `None`, will leave only if `position` is `0`.
|
jpayne@68
|
270 file : `io.TextIOWrapper` or `io.StringIO`, optional
|
jpayne@68
|
271 Specifies where to output the progress messages
|
jpayne@68
|
272 (default: sys.stderr). Uses `file.write(str)` and `file.flush()`
|
jpayne@68
|
273 methods. For encoding, see `write_bytes`.
|
jpayne@68
|
274 ncols : int, optional
|
jpayne@68
|
275 The width of the entire output message. If specified,
|
jpayne@68
|
276 dynamically resizes the progressbar to stay within this bound.
|
jpayne@68
|
277 If unspecified, attempts to use environment width. The
|
jpayne@68
|
278 fallback is a meter width of 10 and no limit for the counter and
|
jpayne@68
|
279 statistics. If 0, will not print any meter (only stats).
|
jpayne@68
|
280 mininterval : float, optional
|
jpayne@68
|
281 Minimum progress display update interval [default: 0.1] seconds.
|
jpayne@68
|
282 maxinterval : float, optional
|
jpayne@68
|
283 Maximum progress display update interval [default: 10] seconds.
|
jpayne@68
|
284 Automatically adjusts `miniters` to correspond to `mininterval`
|
jpayne@68
|
285 after long display update lag. Only works if `dynamic_miniters`
|
jpayne@68
|
286 or monitor thread is enabled.
|
jpayne@68
|
287 miniters : int or float, optional
|
jpayne@68
|
288 Minimum progress display update interval, in iterations.
|
jpayne@68
|
289 If 0 and `dynamic_miniters`, will automatically adjust to equal
|
jpayne@68
|
290 `mininterval` (more CPU efficient, good for tight loops).
|
jpayne@68
|
291 If > 0, will skip display of specified number of iterations.
|
jpayne@68
|
292 Tweak this and `mininterval` to get very efficient loops.
|
jpayne@68
|
293 If your progress is erratic with both fast and slow iterations
|
jpayne@68
|
294 (network, skipping items, etc) you should set miniters=1.
|
jpayne@68
|
295 ascii : bool or str, optional
|
jpayne@68
|
296 If unspecified or False, use unicode (smooth blocks) to fill
|
jpayne@68
|
297 the meter. The fallback is to use ASCII characters " 123456789#".
|
jpayne@68
|
298 disable : bool, optional
|
jpayne@68
|
299 Whether to disable the entire progressbar wrapper
|
jpayne@68
|
300 [default: False]. If set to None, disable on non-TTY.
|
jpayne@68
|
301 unit : str, optional
|
jpayne@68
|
302 String that will be used to define the unit of each iteration
|
jpayne@68
|
303 [default: it].
|
jpayne@68
|
304 unit_scale : bool or int or float, optional
|
jpayne@68
|
305 If 1 or True, the number of iterations will be reduced/scaled
|
jpayne@68
|
306 automatically and a metric prefix following the
|
jpayne@68
|
307 International System of Units standard will be added
|
jpayne@68
|
308 (kilo, mega, etc.) [default: False]. If any other non-zero
|
jpayne@68
|
309 number, will scale `total` and `n`.
|
jpayne@68
|
310 dynamic_ncols : bool, optional
|
jpayne@68
|
311 If set, constantly alters `ncols` and `nrows` to the
|
jpayne@68
|
312 environment (allowing for window resizes) [default: False].
|
jpayne@68
|
313 smoothing : float, optional
|
jpayne@68
|
314 Exponential moving average smoothing factor for speed estimates
|
jpayne@68
|
315 (ignored in GUI mode). Ranges from 0 (average speed) to 1
|
jpayne@68
|
316 (current/instantaneous speed) [default: 0.3].
|
jpayne@68
|
317 bar_format : str, optional
|
jpayne@68
|
318 Specify a custom bar string formatting. May impact performance.
|
jpayne@68
|
319 [default: '{l_bar}{bar}{r_bar}'], where
|
jpayne@68
|
320 l_bar='{desc}: {percentage:3.0f}%|' and
|
jpayne@68
|
321 r_bar='| {n_fmt}/{total_fmt} [{elapsed}<{remaining}, '
|
jpayne@68
|
322 '{rate_fmt}{postfix}]'
|
jpayne@68
|
323 Possible vars: l_bar, bar, r_bar, n, n_fmt, total, total_fmt,
|
jpayne@68
|
324 percentage, elapsed, elapsed_s, ncols, nrows, desc, unit,
|
jpayne@68
|
325 rate, rate_fmt, rate_noinv, rate_noinv_fmt,
|
jpayne@68
|
326 rate_inv, rate_inv_fmt, postfix, unit_divisor,
|
jpayne@68
|
327 remaining, remaining_s, eta.
|
jpayne@68
|
328 Note that a trailing ": " is automatically removed after {desc}
|
jpayne@68
|
329 if the latter is empty.
|
jpayne@68
|
330 initial : int or float, optional
|
jpayne@68
|
331 The initial counter value. Useful when restarting a progress
|
jpayne@68
|
332 bar [default: 0]. If using float, consider specifying `{n:.3f}`
|
jpayne@68
|
333 or similar in `bar_format`, or specifying `unit_scale`.
|
jpayne@68
|
334 position : int, optional
|
jpayne@68
|
335 Specify the line offset to print this bar (starting from 0)
|
jpayne@68
|
336 Automatic if unspecified.
|
jpayne@68
|
337 Useful to manage multiple bars at once (eg, from threads).
|
jpayne@68
|
338 postfix : dict or *, optional
|
jpayne@68
|
339 Specify additional stats to display at the end of the bar.
|
jpayne@68
|
340 Calls `set_postfix(**postfix)` if possible (dict).
|
jpayne@68
|
341 unit_divisor : float, optional
|
jpayne@68
|
342 [default: 1000], ignored unless `unit_scale` is True.
|
jpayne@68
|
343 write_bytes : bool, optional
|
jpayne@68
|
344 Whether to write bytes. If (default: False) will write unicode.
|
jpayne@68
|
345 lock_args : tuple, optional
|
jpayne@68
|
346 Passed to `refresh` for intermediate output
|
jpayne@68
|
347 (initialisation, iterating, and updating).
|
jpayne@68
|
348 nrows : int, optional
|
jpayne@68
|
349 The screen height. If specified, hides nested bars outside this
|
jpayne@68
|
350 bound. If unspecified, attempts to use environment height.
|
jpayne@68
|
351 The fallback is 20.
|
jpayne@68
|
352 colour : str, optional
|
jpayne@68
|
353 Bar colour (e.g. 'green', '#00ff00').
|
jpayne@68
|
354 delay : float, optional
|
jpayne@68
|
355 Don't display until [default: 0] seconds have elapsed.
|
jpayne@68
|
356 gui : bool, optional
|
jpayne@68
|
357 WARNING: internal parameter - do not use.
|
jpayne@68
|
358 Use tqdm.gui.tqdm(...) instead. If set, will attempt to use
|
jpayne@68
|
359 matplotlib animations for a graphical output [default: False].
|
jpayne@68
|
360
|
jpayne@68
|
361 Returns
|
jpayne@68
|
362 -------
|
jpayne@68
|
363 out : decorated iterator.
|
jpayne@68
|
364 """
|
jpayne@68
|
365
|
jpayne@68
|
366 monitor_interval = 10 # set to 0 to disable the thread
|
jpayne@68
|
367 monitor = None
|
jpayne@68
|
368 _instances = WeakSet()
|
jpayne@68
|
369
|
jpayne@68
|
370 @staticmethod
|
jpayne@68
|
371 def format_sizeof(num, suffix='', divisor=1000):
|
jpayne@68
|
372 """
|
jpayne@68
|
373 Formats a number (greater than unity) with SI Order of Magnitude
|
jpayne@68
|
374 prefixes.
|
jpayne@68
|
375
|
jpayne@68
|
376 Parameters
|
jpayne@68
|
377 ----------
|
jpayne@68
|
378 num : float
|
jpayne@68
|
379 Number ( >= 1) to format.
|
jpayne@68
|
380 suffix : str, optional
|
jpayne@68
|
381 Post-postfix [default: ''].
|
jpayne@68
|
382 divisor : float, optional
|
jpayne@68
|
383 Divisor between prefixes [default: 1000].
|
jpayne@68
|
384
|
jpayne@68
|
385 Returns
|
jpayne@68
|
386 -------
|
jpayne@68
|
387 out : str
|
jpayne@68
|
388 Number with Order of Magnitude SI unit postfix.
|
jpayne@68
|
389 """
|
jpayne@68
|
390 for unit in ['', 'k', 'M', 'G', 'T', 'P', 'E', 'Z']:
|
jpayne@68
|
391 if abs(num) < 999.5:
|
jpayne@68
|
392 if abs(num) < 99.95:
|
jpayne@68
|
393 if abs(num) < 9.995:
|
jpayne@68
|
394 return f'{num:1.2f}{unit}{suffix}'
|
jpayne@68
|
395 return f'{num:2.1f}{unit}{suffix}'
|
jpayne@68
|
396 return f'{num:3.0f}{unit}{suffix}'
|
jpayne@68
|
397 num /= divisor
|
jpayne@68
|
398 return f'{num:3.1f}Y{suffix}'
|
jpayne@68
|
399
|
jpayne@68
|
400 @staticmethod
|
jpayne@68
|
401 def format_interval(t):
|
jpayne@68
|
402 """
|
jpayne@68
|
403 Formats a number of seconds as a clock time, [H:]MM:SS
|
jpayne@68
|
404
|
jpayne@68
|
405 Parameters
|
jpayne@68
|
406 ----------
|
jpayne@68
|
407 t : int
|
jpayne@68
|
408 Number of seconds.
|
jpayne@68
|
409
|
jpayne@68
|
410 Returns
|
jpayne@68
|
411 -------
|
jpayne@68
|
412 out : str
|
jpayne@68
|
413 [H:]MM:SS
|
jpayne@68
|
414 """
|
jpayne@68
|
415 mins, s = divmod(int(t), 60)
|
jpayne@68
|
416 h, m = divmod(mins, 60)
|
jpayne@68
|
417 return f'{h:d}:{m:02d}:{s:02d}' if h else f'{m:02d}:{s:02d}'
|
jpayne@68
|
418
|
jpayne@68
|
419 @staticmethod
|
jpayne@68
|
420 def format_num(n):
|
jpayne@68
|
421 """
|
jpayne@68
|
422 Intelligent scientific notation (.3g).
|
jpayne@68
|
423
|
jpayne@68
|
424 Parameters
|
jpayne@68
|
425 ----------
|
jpayne@68
|
426 n : int or float or Numeric
|
jpayne@68
|
427 A Number.
|
jpayne@68
|
428
|
jpayne@68
|
429 Returns
|
jpayne@68
|
430 -------
|
jpayne@68
|
431 out : str
|
jpayne@68
|
432 Formatted number.
|
jpayne@68
|
433 """
|
jpayne@68
|
434 f = f'{n:.3g}'.replace('e+0', 'e+').replace('e-0', 'e-')
|
jpayne@68
|
435 n = str(n)
|
jpayne@68
|
436 return f if len(f) < len(n) else n
|
jpayne@68
|
437
|
jpayne@68
|
438 @staticmethod
|
jpayne@68
|
439 def status_printer(file):
|
jpayne@68
|
440 """
|
jpayne@68
|
441 Manage the printing and in-place updating of a line of characters.
|
jpayne@68
|
442 Note that if the string is longer than a line, then in-place
|
jpayne@68
|
443 updating may not work (it will print a new line at each refresh).
|
jpayne@68
|
444 """
|
jpayne@68
|
445 fp = file
|
jpayne@68
|
446 fp_flush = getattr(fp, 'flush', lambda: None) # pragma: no cover
|
jpayne@68
|
447 if fp in (sys.stderr, sys.stdout):
|
jpayne@68
|
448 getattr(sys.stderr, 'flush', lambda: None)()
|
jpayne@68
|
449 getattr(sys.stdout, 'flush', lambda: None)()
|
jpayne@68
|
450
|
jpayne@68
|
451 def fp_write(s):
|
jpayne@68
|
452 fp.write(str(s))
|
jpayne@68
|
453 fp_flush()
|
jpayne@68
|
454
|
jpayne@68
|
455 last_len = [0]
|
jpayne@68
|
456
|
jpayne@68
|
457 def print_status(s):
|
jpayne@68
|
458 len_s = disp_len(s)
|
jpayne@68
|
459 fp_write('\r' + s + (' ' * max(last_len[0] - len_s, 0)))
|
jpayne@68
|
460 last_len[0] = len_s
|
jpayne@68
|
461
|
jpayne@68
|
462 return print_status
|
jpayne@68
|
463
|
jpayne@68
|
464 @staticmethod
|
jpayne@68
|
465 def format_meter(n, total, elapsed, ncols=None, prefix='', ascii=False, unit='it',
|
jpayne@68
|
466 unit_scale=False, rate=None, bar_format=None, postfix=None,
|
jpayne@68
|
467 unit_divisor=1000, initial=0, colour=None, **extra_kwargs):
|
jpayne@68
|
468 """
|
jpayne@68
|
469 Return a string-based progress bar given some parameters
|
jpayne@68
|
470
|
jpayne@68
|
471 Parameters
|
jpayne@68
|
472 ----------
|
jpayne@68
|
473 n : int or float
|
jpayne@68
|
474 Number of finished iterations.
|
jpayne@68
|
475 total : int or float
|
jpayne@68
|
476 The expected total number of iterations. If meaningless (None),
|
jpayne@68
|
477 only basic progress statistics are displayed (no ETA).
|
jpayne@68
|
478 elapsed : float
|
jpayne@68
|
479 Number of seconds passed since start.
|
jpayne@68
|
480 ncols : int, optional
|
jpayne@68
|
481 The width of the entire output message. If specified,
|
jpayne@68
|
482 dynamically resizes `{bar}` to stay within this bound
|
jpayne@68
|
483 [default: None]. If `0`, will not print any bar (only stats).
|
jpayne@68
|
484 The fallback is `{bar:10}`.
|
jpayne@68
|
485 prefix : str, optional
|
jpayne@68
|
486 Prefix message (included in total width) [default: ''].
|
jpayne@68
|
487 Use as {desc} in bar_format string.
|
jpayne@68
|
488 ascii : bool, optional or str, optional
|
jpayne@68
|
489 If not set, use unicode (smooth blocks) to fill the meter
|
jpayne@68
|
490 [default: False]. The fallback is to use ASCII characters
|
jpayne@68
|
491 " 123456789#".
|
jpayne@68
|
492 unit : str, optional
|
jpayne@68
|
493 The iteration unit [default: 'it'].
|
jpayne@68
|
494 unit_scale : bool or int or float, optional
|
jpayne@68
|
495 If 1 or True, the number of iterations will be printed with an
|
jpayne@68
|
496 appropriate SI metric prefix (k = 10^3, M = 10^6, etc.)
|
jpayne@68
|
497 [default: False]. If any other non-zero number, will scale
|
jpayne@68
|
498 `total` and `n`.
|
jpayne@68
|
499 rate : float, optional
|
jpayne@68
|
500 Manual override for iteration rate.
|
jpayne@68
|
501 If [default: None], uses n/elapsed.
|
jpayne@68
|
502 bar_format : str, optional
|
jpayne@68
|
503 Specify a custom bar string formatting. May impact performance.
|
jpayne@68
|
504 [default: '{l_bar}{bar}{r_bar}'], where
|
jpayne@68
|
505 l_bar='{desc}: {percentage:3.0f}%|' and
|
jpayne@68
|
506 r_bar='| {n_fmt}/{total_fmt} [{elapsed}<{remaining}, '
|
jpayne@68
|
507 '{rate_fmt}{postfix}]'
|
jpayne@68
|
508 Possible vars: l_bar, bar, r_bar, n, n_fmt, total, total_fmt,
|
jpayne@68
|
509 percentage, elapsed, elapsed_s, ncols, nrows, desc, unit,
|
jpayne@68
|
510 rate, rate_fmt, rate_noinv, rate_noinv_fmt,
|
jpayne@68
|
511 rate_inv, rate_inv_fmt, postfix, unit_divisor,
|
jpayne@68
|
512 remaining, remaining_s, eta.
|
jpayne@68
|
513 Note that a trailing ": " is automatically removed after {desc}
|
jpayne@68
|
514 if the latter is empty.
|
jpayne@68
|
515 postfix : *, optional
|
jpayne@68
|
516 Similar to `prefix`, but placed at the end
|
jpayne@68
|
517 (e.g. for additional stats).
|
jpayne@68
|
518 Note: postfix is usually a string (not a dict) for this method,
|
jpayne@68
|
519 and will if possible be set to postfix = ', ' + postfix.
|
jpayne@68
|
520 However other types are supported (#382).
|
jpayne@68
|
521 unit_divisor : float, optional
|
jpayne@68
|
522 [default: 1000], ignored unless `unit_scale` is True.
|
jpayne@68
|
523 initial : int or float, optional
|
jpayne@68
|
524 The initial counter value [default: 0].
|
jpayne@68
|
525 colour : str, optional
|
jpayne@68
|
526 Bar colour (e.g. 'green', '#00ff00').
|
jpayne@68
|
527
|
jpayne@68
|
528 Returns
|
jpayne@68
|
529 -------
|
jpayne@68
|
530 out : Formatted meter and stats, ready to display.
|
jpayne@68
|
531 """
|
jpayne@68
|
532
|
jpayne@68
|
533 # sanity check: total
|
jpayne@68
|
534 if total and n >= (total + 0.5): # allow float imprecision (#849)
|
jpayne@68
|
535 total = None
|
jpayne@68
|
536
|
jpayne@68
|
537 # apply custom scale if necessary
|
jpayne@68
|
538 if unit_scale and unit_scale not in (True, 1):
|
jpayne@68
|
539 if total:
|
jpayne@68
|
540 total *= unit_scale
|
jpayne@68
|
541 n *= unit_scale
|
jpayne@68
|
542 if rate:
|
jpayne@68
|
543 rate *= unit_scale # by default rate = self.avg_dn / self.avg_dt
|
jpayne@68
|
544 unit_scale = False
|
jpayne@68
|
545
|
jpayne@68
|
546 elapsed_str = tqdm.format_interval(elapsed)
|
jpayne@68
|
547
|
jpayne@68
|
548 # if unspecified, attempt to use rate = average speed
|
jpayne@68
|
549 # (we allow manual override since predicting time is an arcane art)
|
jpayne@68
|
550 if rate is None and elapsed:
|
jpayne@68
|
551 rate = (n - initial) / elapsed
|
jpayne@68
|
552 inv_rate = 1 / rate if rate else None
|
jpayne@68
|
553 format_sizeof = tqdm.format_sizeof
|
jpayne@68
|
554 rate_noinv_fmt = ((format_sizeof(rate) if unit_scale else f'{rate:5.2f}')
|
jpayne@68
|
555 if rate else '?') + unit + '/s'
|
jpayne@68
|
556 rate_inv_fmt = (
|
jpayne@68
|
557 (format_sizeof(inv_rate) if unit_scale else f'{inv_rate:5.2f}')
|
jpayne@68
|
558 if inv_rate else '?') + 's/' + unit
|
jpayne@68
|
559 rate_fmt = rate_inv_fmt if inv_rate and inv_rate > 1 else rate_noinv_fmt
|
jpayne@68
|
560
|
jpayne@68
|
561 if unit_scale:
|
jpayne@68
|
562 n_fmt = format_sizeof(n, divisor=unit_divisor)
|
jpayne@68
|
563 total_fmt = format_sizeof(total, divisor=unit_divisor) if total is not None else '?'
|
jpayne@68
|
564 else:
|
jpayne@68
|
565 n_fmt = str(n)
|
jpayne@68
|
566 total_fmt = str(total) if total is not None else '?'
|
jpayne@68
|
567
|
jpayne@68
|
568 try:
|
jpayne@68
|
569 postfix = ', ' + postfix if postfix else ''
|
jpayne@68
|
570 except TypeError:
|
jpayne@68
|
571 pass
|
jpayne@68
|
572
|
jpayne@68
|
573 remaining = (total - n) / rate if rate and total else 0
|
jpayne@68
|
574 remaining_str = tqdm.format_interval(remaining) if rate else '?'
|
jpayne@68
|
575 try:
|
jpayne@68
|
576 eta_dt = (datetime.now() + timedelta(seconds=remaining)
|
jpayne@68
|
577 if rate and total else datetime.fromtimestamp(0, timezone.utc))
|
jpayne@68
|
578 except OverflowError:
|
jpayne@68
|
579 eta_dt = datetime.max
|
jpayne@68
|
580
|
jpayne@68
|
581 # format the stats displayed to the left and right sides of the bar
|
jpayne@68
|
582 if prefix:
|
jpayne@68
|
583 # old prefix setup work around
|
jpayne@68
|
584 bool_prefix_colon_already = (prefix[-2:] == ": ")
|
jpayne@68
|
585 l_bar = prefix if bool_prefix_colon_already else prefix + ": "
|
jpayne@68
|
586 else:
|
jpayne@68
|
587 l_bar = ''
|
jpayne@68
|
588
|
jpayne@68
|
589 r_bar = f'| {n_fmt}/{total_fmt} [{elapsed_str}<{remaining_str}, {rate_fmt}{postfix}]'
|
jpayne@68
|
590
|
jpayne@68
|
591 # Custom bar formatting
|
jpayne@68
|
592 # Populate a dict with all available progress indicators
|
jpayne@68
|
593 format_dict = {
|
jpayne@68
|
594 # slight extension of self.format_dict
|
jpayne@68
|
595 'n': n, 'n_fmt': n_fmt, 'total': total, 'total_fmt': total_fmt,
|
jpayne@68
|
596 'elapsed': elapsed_str, 'elapsed_s': elapsed,
|
jpayne@68
|
597 'ncols': ncols, 'desc': prefix or '', 'unit': unit,
|
jpayne@68
|
598 'rate': inv_rate if inv_rate and inv_rate > 1 else rate,
|
jpayne@68
|
599 'rate_fmt': rate_fmt, 'rate_noinv': rate,
|
jpayne@68
|
600 'rate_noinv_fmt': rate_noinv_fmt, 'rate_inv': inv_rate,
|
jpayne@68
|
601 'rate_inv_fmt': rate_inv_fmt,
|
jpayne@68
|
602 'postfix': postfix, 'unit_divisor': unit_divisor,
|
jpayne@68
|
603 'colour': colour,
|
jpayne@68
|
604 # plus more useful definitions
|
jpayne@68
|
605 'remaining': remaining_str, 'remaining_s': remaining,
|
jpayne@68
|
606 'l_bar': l_bar, 'r_bar': r_bar, 'eta': eta_dt,
|
jpayne@68
|
607 **extra_kwargs}
|
jpayne@68
|
608
|
jpayne@68
|
609 # total is known: we can predict some stats
|
jpayne@68
|
610 if total:
|
jpayne@68
|
611 # fractional and percentage progress
|
jpayne@68
|
612 frac = n / total
|
jpayne@68
|
613 percentage = frac * 100
|
jpayne@68
|
614
|
jpayne@68
|
615 l_bar += f'{percentage:3.0f}%|'
|
jpayne@68
|
616
|
jpayne@68
|
617 if ncols == 0:
|
jpayne@68
|
618 return l_bar[:-1] + r_bar[1:]
|
jpayne@68
|
619
|
jpayne@68
|
620 format_dict.update(l_bar=l_bar)
|
jpayne@68
|
621 if bar_format:
|
jpayne@68
|
622 format_dict.update(percentage=percentage)
|
jpayne@68
|
623
|
jpayne@68
|
624 # auto-remove colon for empty `{desc}`
|
jpayne@68
|
625 if not prefix:
|
jpayne@68
|
626 bar_format = bar_format.replace("{desc}: ", '')
|
jpayne@68
|
627 else:
|
jpayne@68
|
628 bar_format = "{l_bar}{bar}{r_bar}"
|
jpayne@68
|
629
|
jpayne@68
|
630 full_bar = FormatReplace()
|
jpayne@68
|
631 nobar = bar_format.format(bar=full_bar, **format_dict)
|
jpayne@68
|
632 if not full_bar.format_called:
|
jpayne@68
|
633 return nobar # no `{bar}`; nothing else to do
|
jpayne@68
|
634
|
jpayne@68
|
635 # Formatting progress bar space available for bar's display
|
jpayne@68
|
636 full_bar = Bar(frac,
|
jpayne@68
|
637 max(1, ncols - disp_len(nobar)) if ncols else 10,
|
jpayne@68
|
638 charset=Bar.ASCII if ascii is True else ascii or Bar.UTF,
|
jpayne@68
|
639 colour=colour)
|
jpayne@68
|
640 if not _is_ascii(full_bar.charset) and _is_ascii(bar_format):
|
jpayne@68
|
641 bar_format = str(bar_format)
|
jpayne@68
|
642 res = bar_format.format(bar=full_bar, **format_dict)
|
jpayne@68
|
643 return disp_trim(res, ncols) if ncols else res
|
jpayne@68
|
644
|
jpayne@68
|
645 elif bar_format:
|
jpayne@68
|
646 # user-specified bar_format but no total
|
jpayne@68
|
647 l_bar += '|'
|
jpayne@68
|
648 format_dict.update(l_bar=l_bar, percentage=0)
|
jpayne@68
|
649 full_bar = FormatReplace()
|
jpayne@68
|
650 nobar = bar_format.format(bar=full_bar, **format_dict)
|
jpayne@68
|
651 if not full_bar.format_called:
|
jpayne@68
|
652 return nobar
|
jpayne@68
|
653 full_bar = Bar(0,
|
jpayne@68
|
654 max(1, ncols - disp_len(nobar)) if ncols else 10,
|
jpayne@68
|
655 charset=Bar.BLANK, colour=colour)
|
jpayne@68
|
656 res = bar_format.format(bar=full_bar, **format_dict)
|
jpayne@68
|
657 return disp_trim(res, ncols) if ncols else res
|
jpayne@68
|
658 else:
|
jpayne@68
|
659 # no total: no progressbar, ETA, just progress stats
|
jpayne@68
|
660 return (f'{(prefix + ": ") if prefix else ""}'
|
jpayne@68
|
661 f'{n_fmt}{unit} [{elapsed_str}, {rate_fmt}{postfix}]')
|
jpayne@68
|
662
|
jpayne@68
|
663 def __new__(cls, *_, **__):
|
jpayne@68
|
664 instance = object.__new__(cls)
|
jpayne@68
|
665 with cls.get_lock(): # also constructs lock if non-existent
|
jpayne@68
|
666 cls._instances.add(instance)
|
jpayne@68
|
667 # create monitoring thread
|
jpayne@68
|
668 if cls.monitor_interval and (cls.monitor is None
|
jpayne@68
|
669 or not cls.monitor.report()):
|
jpayne@68
|
670 try:
|
jpayne@68
|
671 cls.monitor = TMonitor(cls, cls.monitor_interval)
|
jpayne@68
|
672 except Exception as e: # pragma: nocover
|
jpayne@68
|
673 warn("tqdm:disabling monitor support"
|
jpayne@68
|
674 " (monitor_interval = 0) due to:\n" + str(e),
|
jpayne@68
|
675 TqdmMonitorWarning, stacklevel=2)
|
jpayne@68
|
676 cls.monitor_interval = 0
|
jpayne@68
|
677 return instance
|
jpayne@68
|
678
|
jpayne@68
|
679 @classmethod
|
jpayne@68
|
680 def _get_free_pos(cls, instance=None):
|
jpayne@68
|
681 """Skips specified instance."""
|
jpayne@68
|
682 positions = {abs(inst.pos) for inst in cls._instances
|
jpayne@68
|
683 if inst is not instance and hasattr(inst, "pos")}
|
jpayne@68
|
684 return min(set(range(len(positions) + 1)).difference(positions))
|
jpayne@68
|
685
|
jpayne@68
|
686 @classmethod
|
jpayne@68
|
687 def _decr_instances(cls, instance):
|
jpayne@68
|
688 """
|
jpayne@68
|
689 Remove from list and reposition another unfixed bar
|
jpayne@68
|
690 to fill the new gap.
|
jpayne@68
|
691
|
jpayne@68
|
692 This means that by default (where all nested bars are unfixed),
|
jpayne@68
|
693 order is not maintained but screen flicker/blank space is minimised.
|
jpayne@68
|
694 (tqdm<=4.44.1 moved ALL subsequent unfixed bars up.)
|
jpayne@68
|
695 """
|
jpayne@68
|
696 with cls._lock:
|
jpayne@68
|
697 try:
|
jpayne@68
|
698 cls._instances.remove(instance)
|
jpayne@68
|
699 except KeyError:
|
jpayne@68
|
700 # if not instance.gui: # pragma: no cover
|
jpayne@68
|
701 # raise
|
jpayne@68
|
702 pass # py2: maybe magically removed already
|
jpayne@68
|
703 # else:
|
jpayne@68
|
704 if not instance.gui:
|
jpayne@68
|
705 last = (instance.nrows or 20) - 1
|
jpayne@68
|
706 # find unfixed (`pos >= 0`) overflow (`pos >= nrows - 1`)
|
jpayne@68
|
707 instances = list(filter(
|
jpayne@68
|
708 lambda i: hasattr(i, "pos") and last <= i.pos,
|
jpayne@68
|
709 cls._instances))
|
jpayne@68
|
710 # set first found to current `pos`
|
jpayne@68
|
711 if instances:
|
jpayne@68
|
712 inst = min(instances, key=lambda i: i.pos)
|
jpayne@68
|
713 inst.clear(nolock=True)
|
jpayne@68
|
714 inst.pos = abs(instance.pos)
|
jpayne@68
|
715
|
jpayne@68
|
716 @classmethod
|
jpayne@68
|
717 def write(cls, s, file=None, end="\n", nolock=False):
|
jpayne@68
|
718 """Print a message via tqdm (without overlap with bars)."""
|
jpayne@68
|
719 fp = file if file is not None else sys.stdout
|
jpayne@68
|
720 with cls.external_write_mode(file=file, nolock=nolock):
|
jpayne@68
|
721 # Write the message
|
jpayne@68
|
722 fp.write(s)
|
jpayne@68
|
723 fp.write(end)
|
jpayne@68
|
724
|
jpayne@68
|
725 @classmethod
|
jpayne@68
|
726 @contextmanager
|
jpayne@68
|
727 def external_write_mode(cls, file=None, nolock=False):
|
jpayne@68
|
728 """
|
jpayne@68
|
729 Disable tqdm within context and refresh tqdm when exits.
|
jpayne@68
|
730 Useful when writing to standard output stream
|
jpayne@68
|
731 """
|
jpayne@68
|
732 fp = file if file is not None else sys.stdout
|
jpayne@68
|
733
|
jpayne@68
|
734 try:
|
jpayne@68
|
735 if not nolock:
|
jpayne@68
|
736 cls.get_lock().acquire()
|
jpayne@68
|
737 # Clear all bars
|
jpayne@68
|
738 inst_cleared = []
|
jpayne@68
|
739 for inst in getattr(cls, '_instances', []):
|
jpayne@68
|
740 # Clear instance if in the target output file
|
jpayne@68
|
741 # or if write output + tqdm output are both either
|
jpayne@68
|
742 # sys.stdout or sys.stderr (because both are mixed in terminal)
|
jpayne@68
|
743 if hasattr(inst, "start_t") and (inst.fp == fp or all(
|
jpayne@68
|
744 f in (sys.stdout, sys.stderr) for f in (fp, inst.fp))):
|
jpayne@68
|
745 inst.clear(nolock=True)
|
jpayne@68
|
746 inst_cleared.append(inst)
|
jpayne@68
|
747 yield
|
jpayne@68
|
748 # Force refresh display of bars we cleared
|
jpayne@68
|
749 for inst in inst_cleared:
|
jpayne@68
|
750 inst.refresh(nolock=True)
|
jpayne@68
|
751 finally:
|
jpayne@68
|
752 if not nolock:
|
jpayne@68
|
753 cls._lock.release()
|
jpayne@68
|
754
|
jpayne@68
|
755 @classmethod
|
jpayne@68
|
756 def set_lock(cls, lock):
|
jpayne@68
|
757 """Set the global lock."""
|
jpayne@68
|
758 cls._lock = lock
|
jpayne@68
|
759
|
jpayne@68
|
760 @classmethod
|
jpayne@68
|
761 def get_lock(cls):
|
jpayne@68
|
762 """Get the global lock. Construct it if it does not exist."""
|
jpayne@68
|
763 if not hasattr(cls, '_lock'):
|
jpayne@68
|
764 cls._lock = TqdmDefaultWriteLock()
|
jpayne@68
|
765 return cls._lock
|
jpayne@68
|
766
|
jpayne@68
|
767 @classmethod
|
jpayne@68
|
768 def pandas(cls, **tqdm_kwargs):
|
jpayne@68
|
769 """
|
jpayne@68
|
770 Registers the current `tqdm` class with
|
jpayne@68
|
771 pandas.core.
|
jpayne@68
|
772 ( frame.DataFrame
|
jpayne@68
|
773 | series.Series
|
jpayne@68
|
774 | groupby.(generic.)DataFrameGroupBy
|
jpayne@68
|
775 | groupby.(generic.)SeriesGroupBy
|
jpayne@68
|
776 ).progress_apply
|
jpayne@68
|
777
|
jpayne@68
|
778 A new instance will be created every time `progress_apply` is called,
|
jpayne@68
|
779 and each instance will automatically `close()` upon completion.
|
jpayne@68
|
780
|
jpayne@68
|
781 Parameters
|
jpayne@68
|
782 ----------
|
jpayne@68
|
783 tqdm_kwargs : arguments for the tqdm instance
|
jpayne@68
|
784
|
jpayne@68
|
785 Examples
|
jpayne@68
|
786 --------
|
jpayne@68
|
787 >>> import pandas as pd
|
jpayne@68
|
788 >>> import numpy as np
|
jpayne@68
|
789 >>> from tqdm import tqdm
|
jpayne@68
|
790 >>> from tqdm.gui import tqdm as tqdm_gui
|
jpayne@68
|
791 >>>
|
jpayne@68
|
792 >>> df = pd.DataFrame(np.random.randint(0, 100, (100000, 6)))
|
jpayne@68
|
793 >>> tqdm.pandas(ncols=50) # can use tqdm_gui, optional kwargs, etc
|
jpayne@68
|
794 >>> # Now you can use `progress_apply` instead of `apply`
|
jpayne@68
|
795 >>> df.groupby(0).progress_apply(lambda x: x**2)
|
jpayne@68
|
796
|
jpayne@68
|
797 References
|
jpayne@68
|
798 ----------
|
jpayne@68
|
799 <https://stackoverflow.com/questions/18603270/\
|
jpayne@68
|
800 progress-indicator-during-pandas-operations-python>
|
jpayne@68
|
801 """
|
jpayne@68
|
802 from warnings import catch_warnings, simplefilter
|
jpayne@68
|
803
|
jpayne@68
|
804 from pandas.core.frame import DataFrame
|
jpayne@68
|
805 from pandas.core.series import Series
|
jpayne@68
|
806 try:
|
jpayne@68
|
807 with catch_warnings():
|
jpayne@68
|
808 simplefilter("ignore", category=FutureWarning)
|
jpayne@68
|
809 from pandas import Panel
|
jpayne@68
|
810 except ImportError: # pandas>=1.2.0
|
jpayne@68
|
811 Panel = None
|
jpayne@68
|
812 Rolling, Expanding = None, None
|
jpayne@68
|
813 try: # pandas>=1.0.0
|
jpayne@68
|
814 from pandas.core.window.rolling import _Rolling_and_Expanding
|
jpayne@68
|
815 except ImportError:
|
jpayne@68
|
816 try: # pandas>=0.18.0
|
jpayne@68
|
817 from pandas.core.window import _Rolling_and_Expanding
|
jpayne@68
|
818 except ImportError: # pandas>=1.2.0
|
jpayne@68
|
819 try: # pandas>=1.2.0
|
jpayne@68
|
820 from pandas.core.window.expanding import Expanding
|
jpayne@68
|
821 from pandas.core.window.rolling import Rolling
|
jpayne@68
|
822 _Rolling_and_Expanding = Rolling, Expanding
|
jpayne@68
|
823 except ImportError: # pragma: no cover
|
jpayne@68
|
824 _Rolling_and_Expanding = None
|
jpayne@68
|
825 try: # pandas>=0.25.0
|
jpayne@68
|
826 from pandas.core.groupby.generic import SeriesGroupBy # , NDFrameGroupBy
|
jpayne@68
|
827 from pandas.core.groupby.generic import DataFrameGroupBy
|
jpayne@68
|
828 except ImportError: # pragma: no cover
|
jpayne@68
|
829 try: # pandas>=0.23.0
|
jpayne@68
|
830 from pandas.core.groupby.groupby import DataFrameGroupBy, SeriesGroupBy
|
jpayne@68
|
831 except ImportError:
|
jpayne@68
|
832 from pandas.core.groupby import DataFrameGroupBy, SeriesGroupBy
|
jpayne@68
|
833 try: # pandas>=0.23.0
|
jpayne@68
|
834 from pandas.core.groupby.groupby import GroupBy
|
jpayne@68
|
835 except ImportError: # pragma: no cover
|
jpayne@68
|
836 from pandas.core.groupby import GroupBy
|
jpayne@68
|
837
|
jpayne@68
|
838 try: # pandas>=0.23.0
|
jpayne@68
|
839 from pandas.core.groupby.groupby import PanelGroupBy
|
jpayne@68
|
840 except ImportError:
|
jpayne@68
|
841 try:
|
jpayne@68
|
842 from pandas.core.groupby import PanelGroupBy
|
jpayne@68
|
843 except ImportError: # pandas>=0.25.0
|
jpayne@68
|
844 PanelGroupBy = None
|
jpayne@68
|
845
|
jpayne@68
|
846 tqdm_kwargs = tqdm_kwargs.copy()
|
jpayne@68
|
847 deprecated_t = [tqdm_kwargs.pop('deprecated_t', None)]
|
jpayne@68
|
848
|
jpayne@68
|
849 def inner_generator(df_function='apply'):
|
jpayne@68
|
850 def inner(df, func, *args, **kwargs):
|
jpayne@68
|
851 """
|
jpayne@68
|
852 Parameters
|
jpayne@68
|
853 ----------
|
jpayne@68
|
854 df : (DataFrame|Series)[GroupBy]
|
jpayne@68
|
855 Data (may be grouped).
|
jpayne@68
|
856 func : function
|
jpayne@68
|
857 To be applied on the (grouped) data.
|
jpayne@68
|
858 **kwargs : optional
|
jpayne@68
|
859 Transmitted to `df.apply()`.
|
jpayne@68
|
860 """
|
jpayne@68
|
861
|
jpayne@68
|
862 # Precompute total iterations
|
jpayne@68
|
863 total = tqdm_kwargs.pop("total", getattr(df, 'ngroups', None))
|
jpayne@68
|
864 if total is None: # not grouped
|
jpayne@68
|
865 if df_function == 'applymap':
|
jpayne@68
|
866 total = df.size
|
jpayne@68
|
867 elif isinstance(df, Series):
|
jpayne@68
|
868 total = len(df)
|
jpayne@68
|
869 elif (_Rolling_and_Expanding is None or
|
jpayne@68
|
870 not isinstance(df, _Rolling_and_Expanding)):
|
jpayne@68
|
871 # DataFrame or Panel
|
jpayne@68
|
872 axis = kwargs.get('axis', 0)
|
jpayne@68
|
873 if axis == 'index':
|
jpayne@68
|
874 axis = 0
|
jpayne@68
|
875 elif axis == 'columns':
|
jpayne@68
|
876 axis = 1
|
jpayne@68
|
877 # when axis=0, total is shape[axis1]
|
jpayne@68
|
878 total = df.size // df.shape[axis]
|
jpayne@68
|
879
|
jpayne@68
|
880 # Init bar
|
jpayne@68
|
881 if deprecated_t[0] is not None:
|
jpayne@68
|
882 t = deprecated_t[0]
|
jpayne@68
|
883 deprecated_t[0] = None
|
jpayne@68
|
884 else:
|
jpayne@68
|
885 t = cls(total=total, **tqdm_kwargs)
|
jpayne@68
|
886
|
jpayne@68
|
887 if len(args) > 0:
|
jpayne@68
|
888 # *args intentionally not supported (see #244, #299)
|
jpayne@68
|
889 TqdmDeprecationWarning(
|
jpayne@68
|
890 "Except func, normal arguments are intentionally" +
|
jpayne@68
|
891 " not supported by" +
|
jpayne@68
|
892 " `(DataFrame|Series|GroupBy).progress_apply`." +
|
jpayne@68
|
893 " Use keyword arguments instead.",
|
jpayne@68
|
894 fp_write=getattr(t.fp, 'write', sys.stderr.write))
|
jpayne@68
|
895
|
jpayne@68
|
896 try: # pandas>=1.3.0
|
jpayne@68
|
897 from pandas.core.common import is_builtin_func
|
jpayne@68
|
898 except ImportError:
|
jpayne@68
|
899 is_builtin_func = df._is_builtin_func
|
jpayne@68
|
900 try:
|
jpayne@68
|
901 func = is_builtin_func(func)
|
jpayne@68
|
902 except TypeError:
|
jpayne@68
|
903 pass
|
jpayne@68
|
904
|
jpayne@68
|
905 # Define bar updating wrapper
|
jpayne@68
|
906 def wrapper(*args, **kwargs):
|
jpayne@68
|
907 # update tbar correctly
|
jpayne@68
|
908 # it seems `pandas apply` calls `func` twice
|
jpayne@68
|
909 # on the first column/row to decide whether it can
|
jpayne@68
|
910 # take a fast or slow code path; so stop when t.total==t.n
|
jpayne@68
|
911 t.update(n=1 if not t.total or t.n < t.total else 0)
|
jpayne@68
|
912 return func(*args, **kwargs)
|
jpayne@68
|
913
|
jpayne@68
|
914 # Apply the provided function (in **kwargs)
|
jpayne@68
|
915 # on the df using our wrapper (which provides bar updating)
|
jpayne@68
|
916 try:
|
jpayne@68
|
917 return getattr(df, df_function)(wrapper, **kwargs)
|
jpayne@68
|
918 finally:
|
jpayne@68
|
919 t.close()
|
jpayne@68
|
920
|
jpayne@68
|
921 return inner
|
jpayne@68
|
922
|
jpayne@68
|
923 # Monkeypatch pandas to provide easy methods
|
jpayne@68
|
924 # Enable custom tqdm progress in pandas!
|
jpayne@68
|
925 Series.progress_apply = inner_generator()
|
jpayne@68
|
926 SeriesGroupBy.progress_apply = inner_generator()
|
jpayne@68
|
927 Series.progress_map = inner_generator('map')
|
jpayne@68
|
928 SeriesGroupBy.progress_map = inner_generator('map')
|
jpayne@68
|
929
|
jpayne@68
|
930 DataFrame.progress_apply = inner_generator()
|
jpayne@68
|
931 DataFrameGroupBy.progress_apply = inner_generator()
|
jpayne@68
|
932 DataFrame.progress_applymap = inner_generator('applymap')
|
jpayne@68
|
933 DataFrame.progress_map = inner_generator('map')
|
jpayne@68
|
934 DataFrameGroupBy.progress_map = inner_generator('map')
|
jpayne@68
|
935
|
jpayne@68
|
936 if Panel is not None:
|
jpayne@68
|
937 Panel.progress_apply = inner_generator()
|
jpayne@68
|
938 if PanelGroupBy is not None:
|
jpayne@68
|
939 PanelGroupBy.progress_apply = inner_generator()
|
jpayne@68
|
940
|
jpayne@68
|
941 GroupBy.progress_apply = inner_generator()
|
jpayne@68
|
942 GroupBy.progress_aggregate = inner_generator('aggregate')
|
jpayne@68
|
943 GroupBy.progress_transform = inner_generator('transform')
|
jpayne@68
|
944
|
jpayne@68
|
945 if Rolling is not None and Expanding is not None:
|
jpayne@68
|
946 Rolling.progress_apply = inner_generator()
|
jpayne@68
|
947 Expanding.progress_apply = inner_generator()
|
jpayne@68
|
948 elif _Rolling_and_Expanding is not None:
|
jpayne@68
|
949 _Rolling_and_Expanding.progress_apply = inner_generator()
|
jpayne@68
|
950
|
jpayne@68
|
951 # override defaults via env vars
|
jpayne@68
|
952 @envwrap("TQDM_", is_method=True, types={'total': float, 'ncols': int, 'miniters': float,
|
jpayne@68
|
953 'position': int, 'nrows': int})
|
jpayne@68
|
954 def __init__(self, iterable=None, desc=None, total=None, leave=True, file=None,
|
jpayne@68
|
955 ncols=None, mininterval=0.1, maxinterval=10.0, miniters=None,
|
jpayne@68
|
956 ascii=None, disable=False, unit='it', unit_scale=False,
|
jpayne@68
|
957 dynamic_ncols=False, smoothing=0.3, bar_format=None, initial=0,
|
jpayne@68
|
958 position=None, postfix=None, unit_divisor=1000, write_bytes=False,
|
jpayne@68
|
959 lock_args=None, nrows=None, colour=None, delay=0.0, gui=False,
|
jpayne@68
|
960 **kwargs):
|
jpayne@68
|
961 """see tqdm.tqdm for arguments"""
|
jpayne@68
|
962 if file is None:
|
jpayne@68
|
963 file = sys.stderr
|
jpayne@68
|
964
|
jpayne@68
|
965 if write_bytes:
|
jpayne@68
|
966 # Despite coercing unicode into bytes, py2 sys.std* streams
|
jpayne@68
|
967 # should have bytes written to them.
|
jpayne@68
|
968 file = SimpleTextIOWrapper(
|
jpayne@68
|
969 file, encoding=getattr(file, 'encoding', None) or 'utf-8')
|
jpayne@68
|
970
|
jpayne@68
|
971 file = DisableOnWriteError(file, tqdm_instance=self)
|
jpayne@68
|
972
|
jpayne@68
|
973 if disable is None and hasattr(file, "isatty") and not file.isatty():
|
jpayne@68
|
974 disable = True
|
jpayne@68
|
975
|
jpayne@68
|
976 if total is None and iterable is not None:
|
jpayne@68
|
977 try:
|
jpayne@68
|
978 total = len(iterable)
|
jpayne@68
|
979 except (TypeError, AttributeError):
|
jpayne@68
|
980 total = None
|
jpayne@68
|
981 if total == float("inf"):
|
jpayne@68
|
982 # Infinite iterations, behave same as unknown
|
jpayne@68
|
983 total = None
|
jpayne@68
|
984
|
jpayne@68
|
985 if disable:
|
jpayne@68
|
986 self.iterable = iterable
|
jpayne@68
|
987 self.disable = disable
|
jpayne@68
|
988 with self._lock:
|
jpayne@68
|
989 self.pos = self._get_free_pos(self)
|
jpayne@68
|
990 self._instances.remove(self)
|
jpayne@68
|
991 self.n = initial
|
jpayne@68
|
992 self.total = total
|
jpayne@68
|
993 self.leave = leave
|
jpayne@68
|
994 return
|
jpayne@68
|
995
|
jpayne@68
|
996 if kwargs:
|
jpayne@68
|
997 self.disable = True
|
jpayne@68
|
998 with self._lock:
|
jpayne@68
|
999 self.pos = self._get_free_pos(self)
|
jpayne@68
|
1000 self._instances.remove(self)
|
jpayne@68
|
1001 raise (
|
jpayne@68
|
1002 TqdmDeprecationWarning(
|
jpayne@68
|
1003 "`nested` is deprecated and automated.\n"
|
jpayne@68
|
1004 "Use `position` instead for manual control.\n",
|
jpayne@68
|
1005 fp_write=getattr(file, 'write', sys.stderr.write))
|
jpayne@68
|
1006 if "nested" in kwargs else
|
jpayne@68
|
1007 TqdmKeyError("Unknown argument(s): " + str(kwargs)))
|
jpayne@68
|
1008
|
jpayne@68
|
1009 # Preprocess the arguments
|
jpayne@68
|
1010 if (
|
jpayne@68
|
1011 (ncols is None or nrows is None) and (file in (sys.stderr, sys.stdout))
|
jpayne@68
|
1012 ) or dynamic_ncols: # pragma: no cover
|
jpayne@68
|
1013 if dynamic_ncols:
|
jpayne@68
|
1014 dynamic_ncols = _screen_shape_wrapper()
|
jpayne@68
|
1015 if dynamic_ncols:
|
jpayne@68
|
1016 ncols, nrows = dynamic_ncols(file)
|
jpayne@68
|
1017 else:
|
jpayne@68
|
1018 _dynamic_ncols = _screen_shape_wrapper()
|
jpayne@68
|
1019 if _dynamic_ncols:
|
jpayne@68
|
1020 _ncols, _nrows = _dynamic_ncols(file)
|
jpayne@68
|
1021 if ncols is None:
|
jpayne@68
|
1022 ncols = _ncols
|
jpayne@68
|
1023 if nrows is None:
|
jpayne@68
|
1024 nrows = _nrows
|
jpayne@68
|
1025
|
jpayne@68
|
1026 if miniters is None:
|
jpayne@68
|
1027 miniters = 0
|
jpayne@68
|
1028 dynamic_miniters = True
|
jpayne@68
|
1029 else:
|
jpayne@68
|
1030 dynamic_miniters = False
|
jpayne@68
|
1031
|
jpayne@68
|
1032 if mininterval is None:
|
jpayne@68
|
1033 mininterval = 0
|
jpayne@68
|
1034
|
jpayne@68
|
1035 if maxinterval is None:
|
jpayne@68
|
1036 maxinterval = 0
|
jpayne@68
|
1037
|
jpayne@68
|
1038 if ascii is None:
|
jpayne@68
|
1039 ascii = not _supports_unicode(file)
|
jpayne@68
|
1040
|
jpayne@68
|
1041 if bar_format and ascii is not True and not _is_ascii(ascii):
|
jpayne@68
|
1042 # Convert bar format into unicode since terminal uses unicode
|
jpayne@68
|
1043 bar_format = str(bar_format)
|
jpayne@68
|
1044
|
jpayne@68
|
1045 if smoothing is None:
|
jpayne@68
|
1046 smoothing = 0
|
jpayne@68
|
1047
|
jpayne@68
|
1048 # Store the arguments
|
jpayne@68
|
1049 self.iterable = iterable
|
jpayne@68
|
1050 self.desc = desc or ''
|
jpayne@68
|
1051 self.total = total
|
jpayne@68
|
1052 self.leave = leave
|
jpayne@68
|
1053 self.fp = file
|
jpayne@68
|
1054 self.ncols = ncols
|
jpayne@68
|
1055 self.nrows = nrows
|
jpayne@68
|
1056 self.mininterval = mininterval
|
jpayne@68
|
1057 self.maxinterval = maxinterval
|
jpayne@68
|
1058 self.miniters = miniters
|
jpayne@68
|
1059 self.dynamic_miniters = dynamic_miniters
|
jpayne@68
|
1060 self.ascii = ascii
|
jpayne@68
|
1061 self.disable = disable
|
jpayne@68
|
1062 self.unit = unit
|
jpayne@68
|
1063 self.unit_scale = unit_scale
|
jpayne@68
|
1064 self.unit_divisor = unit_divisor
|
jpayne@68
|
1065 self.initial = initial
|
jpayne@68
|
1066 self.lock_args = lock_args
|
jpayne@68
|
1067 self.delay = delay
|
jpayne@68
|
1068 self.gui = gui
|
jpayne@68
|
1069 self.dynamic_ncols = dynamic_ncols
|
jpayne@68
|
1070 self.smoothing = smoothing
|
jpayne@68
|
1071 self._ema_dn = EMA(smoothing)
|
jpayne@68
|
1072 self._ema_dt = EMA(smoothing)
|
jpayne@68
|
1073 self._ema_miniters = EMA(smoothing)
|
jpayne@68
|
1074 self.bar_format = bar_format
|
jpayne@68
|
1075 self.postfix = None
|
jpayne@68
|
1076 self.colour = colour
|
jpayne@68
|
1077 self._time = time
|
jpayne@68
|
1078 if postfix:
|
jpayne@68
|
1079 try:
|
jpayne@68
|
1080 self.set_postfix(refresh=False, **postfix)
|
jpayne@68
|
1081 except TypeError:
|
jpayne@68
|
1082 self.postfix = postfix
|
jpayne@68
|
1083
|
jpayne@68
|
1084 # Init the iterations counters
|
jpayne@68
|
1085 self.last_print_n = initial
|
jpayne@68
|
1086 self.n = initial
|
jpayne@68
|
1087
|
jpayne@68
|
1088 # if nested, at initial sp() call we replace '\r' by '\n' to
|
jpayne@68
|
1089 # not overwrite the outer progress bar
|
jpayne@68
|
1090 with self._lock:
|
jpayne@68
|
1091 # mark fixed positions as negative
|
jpayne@68
|
1092 self.pos = self._get_free_pos(self) if position is None else -position
|
jpayne@68
|
1093
|
jpayne@68
|
1094 if not gui:
|
jpayne@68
|
1095 # Initialize the screen printer
|
jpayne@68
|
1096 self.sp = self.status_printer(self.fp)
|
jpayne@68
|
1097 if delay <= 0:
|
jpayne@68
|
1098 self.refresh(lock_args=self.lock_args)
|
jpayne@68
|
1099
|
jpayne@68
|
1100 # Init the time counter
|
jpayne@68
|
1101 self.last_print_t = self._time()
|
jpayne@68
|
1102 # NB: Avoid race conditions by setting start_t at the very end of init
|
jpayne@68
|
1103 self.start_t = self.last_print_t
|
jpayne@68
|
1104
|
jpayne@68
|
1105 def __bool__(self):
|
jpayne@68
|
1106 if self.total is not None:
|
jpayne@68
|
1107 return self.total > 0
|
jpayne@68
|
1108 if self.iterable is None:
|
jpayne@68
|
1109 raise TypeError('bool() undefined when iterable == total == None')
|
jpayne@68
|
1110 return bool(self.iterable)
|
jpayne@68
|
1111
|
jpayne@68
|
1112 def __len__(self):
|
jpayne@68
|
1113 return (
|
jpayne@68
|
1114 self.total if self.iterable is None
|
jpayne@68
|
1115 else self.iterable.shape[0] if hasattr(self.iterable, "shape")
|
jpayne@68
|
1116 else len(self.iterable) if hasattr(self.iterable, "__len__")
|
jpayne@68
|
1117 else self.iterable.__length_hint__() if hasattr(self.iterable, "__length_hint__")
|
jpayne@68
|
1118 else getattr(self, "total", None))
|
jpayne@68
|
1119
|
jpayne@68
|
1120 def __reversed__(self):
|
jpayne@68
|
1121 try:
|
jpayne@68
|
1122 orig = self.iterable
|
jpayne@68
|
1123 except AttributeError:
|
jpayne@68
|
1124 raise TypeError("'tqdm' object is not reversible")
|
jpayne@68
|
1125 else:
|
jpayne@68
|
1126 self.iterable = reversed(self.iterable)
|
jpayne@68
|
1127 return self.__iter__()
|
jpayne@68
|
1128 finally:
|
jpayne@68
|
1129 self.iterable = orig
|
jpayne@68
|
1130
|
jpayne@68
|
1131 def __contains__(self, item):
|
jpayne@68
|
1132 contains = getattr(self.iterable, '__contains__', None)
|
jpayne@68
|
1133 return contains(item) if contains is not None else item in self.__iter__()
|
jpayne@68
|
1134
|
jpayne@68
|
1135 def __enter__(self):
|
jpayne@68
|
1136 return self
|
jpayne@68
|
1137
|
jpayne@68
|
1138 def __exit__(self, exc_type, exc_value, traceback):
|
jpayne@68
|
1139 try:
|
jpayne@68
|
1140 self.close()
|
jpayne@68
|
1141 except AttributeError:
|
jpayne@68
|
1142 # maybe eager thread cleanup upon external error
|
jpayne@68
|
1143 if (exc_type, exc_value, traceback) == (None, None, None):
|
jpayne@68
|
1144 raise
|
jpayne@68
|
1145 warn("AttributeError ignored", TqdmWarning, stacklevel=2)
|
jpayne@68
|
1146
|
jpayne@68
|
1147 def __del__(self):
|
jpayne@68
|
1148 self.close()
|
jpayne@68
|
1149
|
jpayne@68
|
1150 def __str__(self):
|
jpayne@68
|
1151 return self.format_meter(**self.format_dict)
|
jpayne@68
|
1152
|
jpayne@68
|
1153 @property
|
jpayne@68
|
1154 def _comparable(self):
|
jpayne@68
|
1155 return abs(getattr(self, "pos", 1 << 31))
|
jpayne@68
|
1156
|
jpayne@68
|
1157 def __hash__(self):
|
jpayne@68
|
1158 return id(self)
|
jpayne@68
|
1159
|
jpayne@68
|
1160 def __iter__(self):
|
jpayne@68
|
1161 """Backward-compatibility to use: for x in tqdm(iterable)"""
|
jpayne@68
|
1162
|
jpayne@68
|
1163 # Inlining instance variables as locals (speed optimisation)
|
jpayne@68
|
1164 iterable = self.iterable
|
jpayne@68
|
1165
|
jpayne@68
|
1166 # If the bar is disabled, then just walk the iterable
|
jpayne@68
|
1167 # (note: keep this check outside the loop for performance)
|
jpayne@68
|
1168 if self.disable:
|
jpayne@68
|
1169 for obj in iterable:
|
jpayne@68
|
1170 yield obj
|
jpayne@68
|
1171 return
|
jpayne@68
|
1172
|
jpayne@68
|
1173 mininterval = self.mininterval
|
jpayne@68
|
1174 last_print_t = self.last_print_t
|
jpayne@68
|
1175 last_print_n = self.last_print_n
|
jpayne@68
|
1176 min_start_t = self.start_t + self.delay
|
jpayne@68
|
1177 n = self.n
|
jpayne@68
|
1178 time = self._time
|
jpayne@68
|
1179
|
jpayne@68
|
1180 try:
|
jpayne@68
|
1181 for obj in iterable:
|
jpayne@68
|
1182 yield obj
|
jpayne@68
|
1183 # Update and possibly print the progressbar.
|
jpayne@68
|
1184 # Note: does not call self.update(1) for speed optimisation.
|
jpayne@68
|
1185 n += 1
|
jpayne@68
|
1186
|
jpayne@68
|
1187 if n - last_print_n >= self.miniters:
|
jpayne@68
|
1188 cur_t = time()
|
jpayne@68
|
1189 dt = cur_t - last_print_t
|
jpayne@68
|
1190 if dt >= mininterval and cur_t >= min_start_t:
|
jpayne@68
|
1191 self.update(n - last_print_n)
|
jpayne@68
|
1192 last_print_n = self.last_print_n
|
jpayne@68
|
1193 last_print_t = self.last_print_t
|
jpayne@68
|
1194 finally:
|
jpayne@68
|
1195 self.n = n
|
jpayne@68
|
1196 self.close()
|
jpayne@68
|
1197
|
jpayne@68
|
1198 def update(self, n=1):
|
jpayne@68
|
1199 """
|
jpayne@68
|
1200 Manually update the progress bar, useful for streams
|
jpayne@68
|
1201 such as reading files.
|
jpayne@68
|
1202 E.g.:
|
jpayne@68
|
1203 >>> t = tqdm(total=filesize) # Initialise
|
jpayne@68
|
1204 >>> for current_buffer in stream:
|
jpayne@68
|
1205 ... ...
|
jpayne@68
|
1206 ... t.update(len(current_buffer))
|
jpayne@68
|
1207 >>> t.close()
|
jpayne@68
|
1208 The last line is highly recommended, but possibly not necessary if
|
jpayne@68
|
1209 `t.update()` will be called in such a way that `filesize` will be
|
jpayne@68
|
1210 exactly reached and printed.
|
jpayne@68
|
1211
|
jpayne@68
|
1212 Parameters
|
jpayne@68
|
1213 ----------
|
jpayne@68
|
1214 n : int or float, optional
|
jpayne@68
|
1215 Increment to add to the internal counter of iterations
|
jpayne@68
|
1216 [default: 1]. If using float, consider specifying `{n:.3f}`
|
jpayne@68
|
1217 or similar in `bar_format`, or specifying `unit_scale`.
|
jpayne@68
|
1218
|
jpayne@68
|
1219 Returns
|
jpayne@68
|
1220 -------
|
jpayne@68
|
1221 out : bool or None
|
jpayne@68
|
1222 True if a `display()` was triggered.
|
jpayne@68
|
1223 """
|
jpayne@68
|
1224 if self.disable:
|
jpayne@68
|
1225 return
|
jpayne@68
|
1226
|
jpayne@68
|
1227 if n < 0:
|
jpayne@68
|
1228 self.last_print_n += n # for auto-refresh logic to work
|
jpayne@68
|
1229 self.n += n
|
jpayne@68
|
1230
|
jpayne@68
|
1231 # check counter first to reduce calls to time()
|
jpayne@68
|
1232 if self.n - self.last_print_n >= self.miniters:
|
jpayne@68
|
1233 cur_t = self._time()
|
jpayne@68
|
1234 dt = cur_t - self.last_print_t
|
jpayne@68
|
1235 if dt >= self.mininterval and cur_t >= self.start_t + self.delay:
|
jpayne@68
|
1236 cur_t = self._time()
|
jpayne@68
|
1237 dn = self.n - self.last_print_n # >= n
|
jpayne@68
|
1238 if self.smoothing and dt and dn:
|
jpayne@68
|
1239 # EMA (not just overall average)
|
jpayne@68
|
1240 self._ema_dn(dn)
|
jpayne@68
|
1241 self._ema_dt(dt)
|
jpayne@68
|
1242 self.refresh(lock_args=self.lock_args)
|
jpayne@68
|
1243 if self.dynamic_miniters:
|
jpayne@68
|
1244 # If no `miniters` was specified, adjust automatically to the
|
jpayne@68
|
1245 # maximum iteration rate seen so far between two prints.
|
jpayne@68
|
1246 # e.g.: After running `tqdm.update(5)`, subsequent
|
jpayne@68
|
1247 # calls to `tqdm.update()` will only cause an update after
|
jpayne@68
|
1248 # at least 5 more iterations.
|
jpayne@68
|
1249 if self.maxinterval and dt >= self.maxinterval:
|
jpayne@68
|
1250 self.miniters = dn * (self.mininterval or self.maxinterval) / dt
|
jpayne@68
|
1251 elif self.smoothing:
|
jpayne@68
|
1252 # EMA miniters update
|
jpayne@68
|
1253 self.miniters = self._ema_miniters(
|
jpayne@68
|
1254 dn * (self.mininterval / dt if self.mininterval and dt
|
jpayne@68
|
1255 else 1))
|
jpayne@68
|
1256 else:
|
jpayne@68
|
1257 # max iters between two prints
|
jpayne@68
|
1258 self.miniters = max(self.miniters, dn)
|
jpayne@68
|
1259
|
jpayne@68
|
1260 # Store old values for next call
|
jpayne@68
|
1261 self.last_print_n = self.n
|
jpayne@68
|
1262 self.last_print_t = cur_t
|
jpayne@68
|
1263 return True
|
jpayne@68
|
1264
|
jpayne@68
|
1265 def close(self):
|
jpayne@68
|
1266 """Cleanup and (if leave=False) close the progressbar."""
|
jpayne@68
|
1267 if self.disable:
|
jpayne@68
|
1268 return
|
jpayne@68
|
1269
|
jpayne@68
|
1270 # Prevent multiple closures
|
jpayne@68
|
1271 self.disable = True
|
jpayne@68
|
1272
|
jpayne@68
|
1273 # decrement instance pos and remove from internal set
|
jpayne@68
|
1274 pos = abs(self.pos)
|
jpayne@68
|
1275 self._decr_instances(self)
|
jpayne@68
|
1276
|
jpayne@68
|
1277 if self.last_print_t < self.start_t + self.delay:
|
jpayne@68
|
1278 # haven't ever displayed; nothing to clear
|
jpayne@68
|
1279 return
|
jpayne@68
|
1280
|
jpayne@68
|
1281 # GUI mode
|
jpayne@68
|
1282 if getattr(self, 'sp', None) is None:
|
jpayne@68
|
1283 return
|
jpayne@68
|
1284
|
jpayne@68
|
1285 # annoyingly, _supports_unicode isn't good enough
|
jpayne@68
|
1286 def fp_write(s):
|
jpayne@68
|
1287 self.fp.write(str(s))
|
jpayne@68
|
1288
|
jpayne@68
|
1289 try:
|
jpayne@68
|
1290 fp_write('')
|
jpayne@68
|
1291 except ValueError as e:
|
jpayne@68
|
1292 if 'closed' in str(e):
|
jpayne@68
|
1293 return
|
jpayne@68
|
1294 raise # pragma: no cover
|
jpayne@68
|
1295
|
jpayne@68
|
1296 leave = pos == 0 if self.leave is None else self.leave
|
jpayne@68
|
1297
|
jpayne@68
|
1298 with self._lock:
|
jpayne@68
|
1299 if leave:
|
jpayne@68
|
1300 # stats for overall rate (no weighted average)
|
jpayne@68
|
1301 self._ema_dt = lambda: None
|
jpayne@68
|
1302 self.display(pos=0)
|
jpayne@68
|
1303 fp_write('\n')
|
jpayne@68
|
1304 else:
|
jpayne@68
|
1305 # clear previous display
|
jpayne@68
|
1306 if self.display(msg='', pos=pos) and not pos:
|
jpayne@68
|
1307 fp_write('\r')
|
jpayne@68
|
1308
|
jpayne@68
|
1309 def clear(self, nolock=False):
|
jpayne@68
|
1310 """Clear current bar display."""
|
jpayne@68
|
1311 if self.disable:
|
jpayne@68
|
1312 return
|
jpayne@68
|
1313
|
jpayne@68
|
1314 if not nolock:
|
jpayne@68
|
1315 self._lock.acquire()
|
jpayne@68
|
1316 pos = abs(self.pos)
|
jpayne@68
|
1317 if pos < (self.nrows or 20):
|
jpayne@68
|
1318 self.moveto(pos)
|
jpayne@68
|
1319 self.sp('')
|
jpayne@68
|
1320 self.fp.write('\r') # place cursor back at the beginning of line
|
jpayne@68
|
1321 self.moveto(-pos)
|
jpayne@68
|
1322 if not nolock:
|
jpayne@68
|
1323 self._lock.release()
|
jpayne@68
|
1324
|
jpayne@68
|
1325 def refresh(self, nolock=False, lock_args=None):
|
jpayne@68
|
1326 """
|
jpayne@68
|
1327 Force refresh the display of this bar.
|
jpayne@68
|
1328
|
jpayne@68
|
1329 Parameters
|
jpayne@68
|
1330 ----------
|
jpayne@68
|
1331 nolock : bool, optional
|
jpayne@68
|
1332 If `True`, does not lock.
|
jpayne@68
|
1333 If [default: `False`]: calls `acquire()` on internal lock.
|
jpayne@68
|
1334 lock_args : tuple, optional
|
jpayne@68
|
1335 Passed to internal lock's `acquire()`.
|
jpayne@68
|
1336 If specified, will only `display()` if `acquire()` returns `True`.
|
jpayne@68
|
1337 """
|
jpayne@68
|
1338 if self.disable:
|
jpayne@68
|
1339 return
|
jpayne@68
|
1340
|
jpayne@68
|
1341 if not nolock:
|
jpayne@68
|
1342 if lock_args:
|
jpayne@68
|
1343 if not self._lock.acquire(*lock_args):
|
jpayne@68
|
1344 return False
|
jpayne@68
|
1345 else:
|
jpayne@68
|
1346 self._lock.acquire()
|
jpayne@68
|
1347 self.display()
|
jpayne@68
|
1348 if not nolock:
|
jpayne@68
|
1349 self._lock.release()
|
jpayne@68
|
1350 return True
|
jpayne@68
|
1351
|
jpayne@68
|
1352 def unpause(self):
|
jpayne@68
|
1353 """Restart tqdm timer from last print time."""
|
jpayne@68
|
1354 if self.disable:
|
jpayne@68
|
1355 return
|
jpayne@68
|
1356 cur_t = self._time()
|
jpayne@68
|
1357 self.start_t += cur_t - self.last_print_t
|
jpayne@68
|
1358 self.last_print_t = cur_t
|
jpayne@68
|
1359
|
jpayne@68
|
1360 def reset(self, total=None):
|
jpayne@68
|
1361 """
|
jpayne@68
|
1362 Resets to 0 iterations for repeated use.
|
jpayne@68
|
1363
|
jpayne@68
|
1364 Consider combining with `leave=True`.
|
jpayne@68
|
1365
|
jpayne@68
|
1366 Parameters
|
jpayne@68
|
1367 ----------
|
jpayne@68
|
1368 total : int or float, optional. Total to use for the new bar.
|
jpayne@68
|
1369 """
|
jpayne@68
|
1370 self.n = 0
|
jpayne@68
|
1371 if total is not None:
|
jpayne@68
|
1372 self.total = total
|
jpayne@68
|
1373 if self.disable:
|
jpayne@68
|
1374 return
|
jpayne@68
|
1375 self.last_print_n = 0
|
jpayne@68
|
1376 self.last_print_t = self.start_t = self._time()
|
jpayne@68
|
1377 self._ema_dn = EMA(self.smoothing)
|
jpayne@68
|
1378 self._ema_dt = EMA(self.smoothing)
|
jpayne@68
|
1379 self._ema_miniters = EMA(self.smoothing)
|
jpayne@68
|
1380 self.refresh()
|
jpayne@68
|
1381
|
jpayne@68
|
1382 def set_description(self, desc=None, refresh=True):
|
jpayne@68
|
1383 """
|
jpayne@68
|
1384 Set/modify description of the progress bar.
|
jpayne@68
|
1385
|
jpayne@68
|
1386 Parameters
|
jpayne@68
|
1387 ----------
|
jpayne@68
|
1388 desc : str, optional
|
jpayne@68
|
1389 refresh : bool, optional
|
jpayne@68
|
1390 Forces refresh [default: True].
|
jpayne@68
|
1391 """
|
jpayne@68
|
1392 self.desc = desc + ': ' if desc else ''
|
jpayne@68
|
1393 if refresh:
|
jpayne@68
|
1394 self.refresh()
|
jpayne@68
|
1395
|
jpayne@68
|
1396 def set_description_str(self, desc=None, refresh=True):
|
jpayne@68
|
1397 """Set/modify description without ': ' appended."""
|
jpayne@68
|
1398 self.desc = desc or ''
|
jpayne@68
|
1399 if refresh:
|
jpayne@68
|
1400 self.refresh()
|
jpayne@68
|
1401
|
jpayne@68
|
1402 def set_postfix(self, ordered_dict=None, refresh=True, **kwargs):
|
jpayne@68
|
1403 """
|
jpayne@68
|
1404 Set/modify postfix (additional stats)
|
jpayne@68
|
1405 with automatic formatting based on datatype.
|
jpayne@68
|
1406
|
jpayne@68
|
1407 Parameters
|
jpayne@68
|
1408 ----------
|
jpayne@68
|
1409 ordered_dict : dict or OrderedDict, optional
|
jpayne@68
|
1410 refresh : bool, optional
|
jpayne@68
|
1411 Forces refresh [default: True].
|
jpayne@68
|
1412 kwargs : dict, optional
|
jpayne@68
|
1413 """
|
jpayne@68
|
1414 # Sort in alphabetical order to be more deterministic
|
jpayne@68
|
1415 postfix = OrderedDict([] if ordered_dict is None else ordered_dict)
|
jpayne@68
|
1416 for key in sorted(kwargs.keys()):
|
jpayne@68
|
1417 postfix[key] = kwargs[key]
|
jpayne@68
|
1418 # Preprocess stats according to datatype
|
jpayne@68
|
1419 for key in postfix.keys():
|
jpayne@68
|
1420 # Number: limit the length of the string
|
jpayne@68
|
1421 if isinstance(postfix[key], Number):
|
jpayne@68
|
1422 postfix[key] = self.format_num(postfix[key])
|
jpayne@68
|
1423 # Else for any other type, try to get the string conversion
|
jpayne@68
|
1424 elif not isinstance(postfix[key], str):
|
jpayne@68
|
1425 postfix[key] = str(postfix[key])
|
jpayne@68
|
1426 # Else if it's a string, don't need to preprocess anything
|
jpayne@68
|
1427 # Stitch together to get the final postfix
|
jpayne@68
|
1428 self.postfix = ', '.join(key + '=' + postfix[key].strip()
|
jpayne@68
|
1429 for key in postfix.keys())
|
jpayne@68
|
1430 if refresh:
|
jpayne@68
|
1431 self.refresh()
|
jpayne@68
|
1432
|
jpayne@68
|
1433 def set_postfix_str(self, s='', refresh=True):
|
jpayne@68
|
1434 """
|
jpayne@68
|
1435 Postfix without dictionary expansion, similar to prefix handling.
|
jpayne@68
|
1436 """
|
jpayne@68
|
1437 self.postfix = str(s)
|
jpayne@68
|
1438 if refresh:
|
jpayne@68
|
1439 self.refresh()
|
jpayne@68
|
1440
|
jpayne@68
|
1441 def moveto(self, n):
|
jpayne@68
|
1442 # TODO: private method
|
jpayne@68
|
1443 self.fp.write('\n' * n + _term_move_up() * -n)
|
jpayne@68
|
1444 getattr(self.fp, 'flush', lambda: None)()
|
jpayne@68
|
1445
|
jpayne@68
|
1446 @property
|
jpayne@68
|
1447 def format_dict(self):
|
jpayne@68
|
1448 """Public API for read-only member access."""
|
jpayne@68
|
1449 if self.disable and not hasattr(self, 'unit'):
|
jpayne@68
|
1450 return defaultdict(lambda: None, {
|
jpayne@68
|
1451 'n': self.n, 'total': self.total, 'elapsed': 0, 'unit': 'it'})
|
jpayne@68
|
1452 if self.dynamic_ncols:
|
jpayne@68
|
1453 self.ncols, self.nrows = self.dynamic_ncols(self.fp)
|
jpayne@68
|
1454 return {
|
jpayne@68
|
1455 'n': self.n, 'total': self.total,
|
jpayne@68
|
1456 'elapsed': self._time() - self.start_t if hasattr(self, 'start_t') else 0,
|
jpayne@68
|
1457 'ncols': self.ncols, 'nrows': self.nrows, 'prefix': self.desc,
|
jpayne@68
|
1458 'ascii': self.ascii, 'unit': self.unit, 'unit_scale': self.unit_scale,
|
jpayne@68
|
1459 'rate': self._ema_dn() / self._ema_dt() if self._ema_dt() else None,
|
jpayne@68
|
1460 'bar_format': self.bar_format, 'postfix': self.postfix,
|
jpayne@68
|
1461 'unit_divisor': self.unit_divisor, 'initial': self.initial,
|
jpayne@68
|
1462 'colour': self.colour}
|
jpayne@68
|
1463
|
jpayne@68
|
1464 def display(self, msg=None, pos=None):
|
jpayne@68
|
1465 """
|
jpayne@68
|
1466 Use `self.sp` to display `msg` in the specified `pos`.
|
jpayne@68
|
1467
|
jpayne@68
|
1468 Consider overloading this function when inheriting to use e.g.:
|
jpayne@68
|
1469 `self.some_frontend(**self.format_dict)` instead of `self.sp`.
|
jpayne@68
|
1470
|
jpayne@68
|
1471 Parameters
|
jpayne@68
|
1472 ----------
|
jpayne@68
|
1473 msg : str, optional. What to display (default: `repr(self)`).
|
jpayne@68
|
1474 pos : int, optional. Position to `moveto`
|
jpayne@68
|
1475 (default: `abs(self.pos)`).
|
jpayne@68
|
1476 """
|
jpayne@68
|
1477 if pos is None:
|
jpayne@68
|
1478 pos = abs(self.pos)
|
jpayne@68
|
1479
|
jpayne@68
|
1480 nrows = self.nrows or 20
|
jpayne@68
|
1481 if pos >= nrows - 1:
|
jpayne@68
|
1482 if pos >= nrows:
|
jpayne@68
|
1483 return False
|
jpayne@68
|
1484 if msg or msg is None: # override at `nrows - 1`
|
jpayne@68
|
1485 msg = " ... (more hidden) ..."
|
jpayne@68
|
1486
|
jpayne@68
|
1487 if not hasattr(self, "sp"):
|
jpayne@68
|
1488 raise TqdmDeprecationWarning(
|
jpayne@68
|
1489 "Please use `tqdm.gui.tqdm(...)`"
|
jpayne@68
|
1490 " instead of `tqdm(..., gui=True)`\n",
|
jpayne@68
|
1491 fp_write=getattr(self.fp, 'write', sys.stderr.write))
|
jpayne@68
|
1492
|
jpayne@68
|
1493 if pos:
|
jpayne@68
|
1494 self.moveto(pos)
|
jpayne@68
|
1495 self.sp(self.__str__() if msg is None else msg)
|
jpayne@68
|
1496 if pos:
|
jpayne@68
|
1497 self.moveto(-pos)
|
jpayne@68
|
1498 return True
|
jpayne@68
|
1499
|
jpayne@68
|
1500 @classmethod
|
jpayne@68
|
1501 @contextmanager
|
jpayne@68
|
1502 def wrapattr(cls, stream, method, total=None, bytes=True, **tqdm_kwargs):
|
jpayne@68
|
1503 """
|
jpayne@68
|
1504 stream : file-like object.
|
jpayne@68
|
1505 method : str, "read" or "write". The result of `read()` and
|
jpayne@68
|
1506 the first argument of `write()` should have a `len()`.
|
jpayne@68
|
1507
|
jpayne@68
|
1508 >>> with tqdm.wrapattr(file_obj, "read", total=file_obj.size) as fobj:
|
jpayne@68
|
1509 ... while True:
|
jpayne@68
|
1510 ... chunk = fobj.read(chunk_size)
|
jpayne@68
|
1511 ... if not chunk:
|
jpayne@68
|
1512 ... break
|
jpayne@68
|
1513 """
|
jpayne@68
|
1514 with cls(total=total, **tqdm_kwargs) as t:
|
jpayne@68
|
1515 if bytes:
|
jpayne@68
|
1516 t.unit = "B"
|
jpayne@68
|
1517 t.unit_scale = True
|
jpayne@68
|
1518 t.unit_divisor = 1024
|
jpayne@68
|
1519 yield CallbackIOWrapper(t.update, stream, method)
|
jpayne@68
|
1520
|
jpayne@68
|
1521
|
jpayne@68
|
1522 def trange(*args, **kwargs):
|
jpayne@68
|
1523 """Shortcut for tqdm(range(*args), **kwargs)."""
|
jpayne@68
|
1524 return tqdm(range(*args), **kwargs)
|