diff options
Diffstat (limited to 'libs/tqdm/std.py')
-rw-r--r-- | libs/tqdm/std.py | 581 |
1 files changed, 302 insertions, 279 deletions
diff --git a/libs/tqdm/std.py b/libs/tqdm/std.py index 0cdc8e6b7..e81c83680 100644 --- a/libs/tqdm/std.py +++ b/libs/tqdm/std.py @@ -1,30 +1,30 @@ """ Customisable progressbar decorator for iterators. -Includes a default (x)range iterator printing to stderr. +Includes a default `range` iterator printing to `stderr`. Usage: - >>> from tqdm import trange[, tqdm] - >>> for i in trange(10): #same as: for i in tqdm(xrange(10)) - ... ... +>>> from tqdm import trange, tqdm +>>> for i in trange(10): +... ... """ from __future__ import absolute_import, division -# compatibility functions and utilities -from .utils import _supports_unicode, _screen_shape_wrapper, _range, _unich, \ - _term_move_up, _unicode, WeakSet, _basestring, _OrderedDict, \ - Comparable, _is_ascii, FormatReplace, disp_len, disp_trim, \ - SimpleTextIOWrapper, CallbackIOWrapper -from ._monitor import TMonitor -# native libraries -from contextlib import contextmanager + import sys +from collections import OrderedDict, defaultdict +from contextlib import contextmanager +from datetime import datetime, timedelta from numbers import Number from time import time -# For parallelism safety -import threading as th from warnings import warn +from weakref import WeakSet + +from ._monitor import TMonitor +from .utils import ( + CallbackIOWrapper, Comparable, DisableOnWriteError, FormatReplace, SimpleTextIOWrapper, + _basestring, _is_ascii, _range, _screen_shape_wrapper, _supports_unicode, _term_move_up, + _unich, _unicode, disp_len, disp_trim) -__author__ = {"github.com/": ["noamraph", "obiwanus", "kmike", "hadim", - "casperdcl", "lrq3000"]} +__author__ = "https://github.com/tqdm/tqdm#contributions" __all__ = ['tqdm', 'trange', 'TqdmTypeError', 'TqdmKeyError', 'TqdmWarning', 'TqdmExperimentalWarning', 'TqdmDeprecationWarning', @@ -46,8 +46,7 @@ class TqdmWarning(Warning): """ def __init__(self, msg, fp_write=None, *a, **k): if fp_write is not None: - fp_write("\n" + self.__class__.__name__ + ": " + - str(msg).rstrip() + '\n') + fp_write("\n" + self.__class__.__name__ + ": " + str(msg).rstrip() + '\n') else: super(TqdmWarning, self).__init__(msg, *a, **k) @@ -67,6 +66,15 @@ class TqdmMonitorWarning(TqdmWarning, RuntimeWarning): pass +def TRLock(*args, **kwargs): + """threading RLock""" + try: + from threading import RLock + return RLock(*args, **kwargs) + except (ImportError, OSError): # pragma: no cover + pass + + class TqdmDefaultWriteLock(object): """ Provide a default write lock for thread and multiprocessing safety. @@ -76,13 +84,22 @@ class TqdmDefaultWriteLock(object): On Windows, you need to supply the lock from the parent to the children as an argument to joblib or the parallelism lib you use. """ + # global thread lock so no setup required for multithreading. + # NB: Do not create multiprocessing lock as it sets the multiprocessing + # context, disallowing `spawn()`/`forkserver()` + th_lock = TRLock() + def __init__(self): # Create global parallelism locks to avoid racing issues with parallel # bars works only if fork available (Linux/MacOSX, but not Windows) - self.create_mp_lock() - self.create_th_lock() cls = type(self) + root_lock = cls.th_lock + if root_lock is not None: + root_lock.acquire() + cls.create_mp_lock() self.locks = [lk for lk in [cls.mp_lock, cls.th_lock] if lk is not None] + if root_lock is not None: + root_lock.release() def acquire(self, *a, **k): for lock in self.locks: @@ -103,26 +120,14 @@ class TqdmDefaultWriteLock(object): if not hasattr(cls, 'mp_lock'): try: from multiprocessing import RLock - cls.mp_lock = RLock() # multiprocessing lock - except ImportError: # pragma: no cover - cls.mp_lock = None - except OSError: # pragma: no cover + cls.mp_lock = RLock() + except (ImportError, OSError): # pragma: no cover cls.mp_lock = None @classmethod def create_th_lock(cls): - if not hasattr(cls, 'th_lock'): - try: - cls.th_lock = th.RLock() # thread lock - except OSError: # pragma: no cover - cls.th_lock = None - - -# Create a thread lock before instantiation so that no setup needs to be done -# before running in a multithreaded environment. -# Do not create the multiprocessing lock because it sets the multiprocessing -# context and does not allow the user to use 'spawn' or 'forkserver' methods. -TqdmDefaultWriteLock.create_th_lock() + assert hasattr(cls, 'th_lock') + warn("create_th_lock not needed anymore", TqdmDeprecationWarning, stacklevel=2) class Bar(object): @@ -141,21 +146,50 @@ class Bar(object): ASCII = " 123456789#" UTF = u" " + u''.join(map(_unich, range(0x258F, 0x2587, -1))) BLANK = " " - - def __init__(self, frac, default_len=10, charset=UTF): - if not (0 <= frac <= 1): + COLOUR_RESET = '\x1b[0m' + COLOUR_RGB = '\x1b[38;2;%d;%d;%dm' + COLOURS = {'BLACK': '\x1b[30m', 'RED': '\x1b[31m', 'GREEN': '\x1b[32m', + 'YELLOW': '\x1b[33m', 'BLUE': '\x1b[34m', 'MAGENTA': '\x1b[35m', + 'CYAN': '\x1b[36m', 'WHITE': '\x1b[37m'} + + def __init__(self, frac, default_len=10, charset=UTF, colour=None): + if not 0 <= frac <= 1: warn("clamping frac to range [0, 1]", TqdmWarning, stacklevel=2) frac = max(0, min(1, frac)) assert default_len > 0 self.frac = frac self.default_len = default_len self.charset = charset + self.colour = colour + + @property + def colour(self): + return self._colour + + @colour.setter + def colour(self, value): + if not value: + self._colour = None + return + try: + if value.upper() in self.COLOURS: + self._colour = self.COLOURS[value.upper()] + elif value[0] == '#' and len(value) == 7: + self._colour = self.COLOUR_RGB % tuple( + int(i, 16) for i in (value[1:3], value[3:5], value[5:7])) + else: + raise KeyError + except (KeyError, AttributeError): + warn("Unknown colour (%s); valid choices: [hex (#00ff00), %s]" % ( + value, ", ".join(self.COLOURS)), + TqdmWarning, stacklevel=2) + self._colour = None def __format__(self, format_spec): if format_spec: _type = format_spec[-1].lower() try: - charset = dict(a=self.ASCII, u=self.UTF, b=self.BLANK)[_type] + charset = {'a': self.ASCII, 'u': self.UTF, 'b': self.BLANK}[_type] except KeyError: charset = self.charset else: @@ -171,17 +205,43 @@ class Bar(object): N_BARS = self.default_len nsyms = len(charset) - 1 - bar_length, frac_bar_length = divmod( - int(self.frac * N_BARS * nsyms), nsyms) + bar_length, frac_bar_length = divmod(int(self.frac * N_BARS * nsyms), nsyms) + + res = charset[-1] * bar_length + if bar_length < N_BARS: # whitespace padding + res = res + charset[frac_bar_length] + charset[0] * (N_BARS - bar_length - 1) + return self.colour + res + self.COLOUR_RESET if self.colour else res + - bar = charset[-1] * bar_length - frac_bar = charset[frac_bar_length] +class EMA(object): + """ + Exponential moving average: smoothing to give progressively lower + weights to older values. + + Parameters + ---------- + smoothing : float, optional + Smoothing factor in range [0, 1], [default: 0.3]. + Increase to give more weight to recent values. + Ranges from 0 (yields old value) to 1 (yields new value). + """ + def __init__(self, smoothing=0.3): + self.alpha = smoothing + self.last = 0 + self.calls = 0 - # whitespace padding - if bar_length < N_BARS: - return bar + frac_bar + \ - charset[0] * (N_BARS - bar_length - 1) - return bar + def __call__(self, x=None): + """ + Parameters + ---------- + x : float + New value to include in EMA. + """ + beta = 1 - self.alpha + if x is not None: + self.last = self.alpha * x + beta * self.last + self.calls += 1 + return self.last / (1 - beta ** self.calls) if self.calls else self.last class tqdm(Comparable): @@ -193,6 +253,7 @@ class tqdm(Comparable): monitor_interval = 10 # set to 0 to disable the thread monitor = None + _instances = WeakSet() @staticmethod def format_sizeof(num, suffix='', divisor=1000): @@ -266,25 +327,6 @@ class tqdm(Comparable): return f if len(f) < len(n) else n @staticmethod - def ema(x, mu=None, alpha=0.3): - """ - Exponential moving average: smoothing to give progressively lower - weights to older values. - - Parameters - ---------- - x : float - New value to include in EMA. - mu : float, optional - Previous EMA value. - alpha : float, optional - Smoothing factor in range [0, 1], [default: 0.3]. - Increase to give more weight to recent values. - Ranges from 0 (yields mu) to 1 (yields x). - """ - return x if mu is None else (alpha * x) + (1 - alpha) * mu - - @staticmethod def status_printer(file): """ Manage the printing and in-place updating of a line of characters. @@ -293,6 +335,9 @@ class tqdm(Comparable): """ fp = file fp_flush = getattr(fp, 'flush', lambda: None) # pragma: no cover + if fp in (sys.stderr, sys.stdout): + sys.stderr.flush() + sys.stdout.flush() def fp_write(s): fp.write(_unicode(s)) @@ -301,16 +346,16 @@ class tqdm(Comparable): last_len = [0] def print_status(s): - len_s = len(s) + len_s = disp_len(s) fp_write('\r' + s + (' ' * max(last_len[0] - len_s, 0))) last_len[0] = len_s return print_status @staticmethod - def format_meter(n, total, elapsed, ncols=None, prefix='', ascii=False, - unit='it', unit_scale=False, rate=None, bar_format=None, - postfix=None, unit_divisor=1000, **extra_kwargs): + def format_meter(n, total, elapsed, ncols=None, prefix='', ascii=False, unit='it', + unit_scale=False, rate=None, bar_format=None, postfix=None, + unit_divisor=1000, initial=0, colour=None, **extra_kwargs): """ Return a string-based progress bar given some parameters @@ -355,7 +400,7 @@ class tqdm(Comparable): percentage, elapsed, elapsed_s, ncols, nrows, desc, unit, rate, rate_fmt, rate_noinv, rate_noinv_fmt, rate_inv, rate_inv_fmt, postfix, unit_divisor, - remaining, remaining_s. + remaining, remaining_s, eta. Note that a trailing ": " is automatically removed after {desc} if the latter is empty. postfix : *, optional @@ -366,6 +411,10 @@ class tqdm(Comparable): However other types are supported (#382). unit_divisor : float, optional [default: 1000], ignored unless `unit_scale` is True. + initial : int or float, optional + The initial counter value [default: 0]. + colour : str, optional + Bar colour (e.g. 'green', '#00ff00'). Returns ------- @@ -382,7 +431,7 @@ class tqdm(Comparable): total *= unit_scale n *= unit_scale if rate: - rate *= unit_scale # by default rate = 1 / self.avg_time + rate *= unit_scale # by default rate = self.avg_dn / self.avg_dt unit_scale = False elapsed_str = tqdm.format_interval(elapsed) @@ -390,21 +439,19 @@ class tqdm(Comparable): # if unspecified, attempt to use rate = average speed # (we allow manual override since predicting time is an arcane art) if rate is None and elapsed: - rate = n / elapsed + rate = (n - initial) / elapsed inv_rate = 1 / rate if rate else None format_sizeof = tqdm.format_sizeof rate_noinv_fmt = ((format_sizeof(rate) if unit_scale else - '{0:5.2f}'.format(rate)) - if rate else '?') + unit + '/s' - rate_inv_fmt = ((format_sizeof(inv_rate) if unit_scale else - '{0:5.2f}'.format(inv_rate)) - if inv_rate else '?') + 's/' + unit + '{0:5.2f}'.format(rate)) if rate else '?') + unit + '/s' + rate_inv_fmt = ( + (format_sizeof(inv_rate) if unit_scale else '{0:5.2f}'.format(inv_rate)) + if inv_rate else '?') + 's/' + unit rate_fmt = rate_inv_fmt if inv_rate and inv_rate > 1 else rate_noinv_fmt if unit_scale: n_fmt = format_sizeof(n, divisor=unit_divisor) - total_fmt = format_sizeof(total, divisor=unit_divisor) \ - if total is not None else '?' + total_fmt = format_sizeof(total, divisor=unit_divisor) if total is not None else '?' else: n_fmt = str(n) total_fmt = str(total) if total is not None else '?' @@ -416,6 +463,11 @@ class tqdm(Comparable): remaining = (total - n) / rate if rate and total else 0 remaining_str = tqdm.format_interval(remaining) if rate else '?' + try: + eta_dt = (datetime.now() + timedelta(seconds=remaining) + if rate and total else datetime.utcfromtimestamp(0)) + except OverflowError: + eta_dt = datetime.max # format the stats displayed to the left and right sides of the bar if prefix: @@ -440,9 +492,10 @@ class tqdm(Comparable): rate_noinv_fmt=rate_noinv_fmt, rate_inv=inv_rate, rate_inv_fmt=rate_inv_fmt, postfix=postfix, unit_divisor=unit_divisor, + colour=colour, # plus more useful definitions remaining=remaining_str, remaining_s=remaining, - l_bar=l_bar, r_bar=r_bar, + l_bar=l_bar, r_bar=r_bar, eta=eta_dt, **extra_kwargs) # total is known: we can predict some stats @@ -477,11 +530,10 @@ class tqdm(Comparable): return nobar # Formatting progress bar space available for bar's display - full_bar = Bar( - frac, - max(1, ncols - disp_len(nobar)) - if ncols else 10, - charset=Bar.ASCII if ascii is True else ascii or Bar.UTF) + full_bar = Bar(frac, + max(1, ncols - disp_len(nobar)) if ncols else 10, + charset=Bar.ASCII if ascii is True else ascii or Bar.UTF, + colour=colour) if not _is_ascii(full_bar.charset) and _is_ascii(bar_format): bar_format = _unicode(bar_format) res = bar_format.format(bar=full_bar, **format_dict) @@ -495,31 +547,23 @@ class tqdm(Comparable): nobar = bar_format.format(bar=full_bar, **format_dict) if not full_bar.format_called: return nobar - full_bar = Bar( - 0, - max(1, ncols - disp_len(nobar)) - if ncols else 10, - charset=Bar.BLANK) + full_bar = Bar(0, + max(1, ncols - disp_len(nobar)) if ncols else 10, + charset=Bar.BLANK, colour=colour) res = bar_format.format(bar=full_bar, **format_dict) return disp_trim(res, ncols) if ncols else res else: # no total: no progressbar, ETA, just progress stats - return ((prefix + ": ") if prefix else '') + \ - '{0}{1} [{2}, {3}{4}]'.format( - n_fmt, unit, elapsed_str, rate_fmt, postfix) + return '{0}{1}{2} [{3}, {4}{5}]'.format( + (prefix + ": ") if prefix else '', n_fmt, unit, elapsed_str, rate_fmt, postfix) - def __new__(cls, *args, **kwargs): - # Create a new instance + def __new__(cls, *_, **__): instance = object.__new__(cls) - # Construct the lock if it does not exist - with cls.get_lock(): - # Add to the list of instances - if not hasattr(cls, '_instances'): - cls._instances = WeakSet() + with cls.get_lock(): # also constructs lock if non-existent cls._instances.add(instance) - # Create the monitoring thread - if cls.monitor_interval and (cls.monitor is None or not - cls.monitor.report()): + # create monitoring thread + if cls.monitor_interval and (cls.monitor is None + or not cls.monitor.report()): try: cls.monitor = TMonitor(cls, cls.monitor_interval) except Exception as e: # pragma: nocover @@ -527,14 +571,13 @@ class tqdm(Comparable): " (monitor_interval = 0) due to:\n" + str(e), TqdmMonitorWarning, stacklevel=2) cls.monitor_interval = 0 - # Return the instance return instance @classmethod def _get_free_pos(cls, instance=None): """Skips specified instance.""" - positions = set(abs(inst.pos) for inst in cls._instances - if inst is not instance and hasattr(inst, "pos")) + positions = {abs(inst.pos) for inst in cls._instances + if inst is not instance and hasattr(inst, "pos")} return min(set(range(len(positions) + 1)).difference(positions)) @classmethod @@ -566,15 +609,6 @@ class tqdm(Comparable): inst = min(instances, key=lambda i: i.pos) inst.clear(nolock=True) inst.pos = abs(instance.pos) - # Kill monitor if no instances are left - if not cls._instances and cls.monitor: - try: - cls.monitor.exit() - del cls.monitor - except AttributeError: # pragma: nocover - pass - else: - cls.monitor = None @classmethod def write(cls, s, file=None, end="\n", nolock=False): @@ -628,9 +662,9 @@ class tqdm(Comparable): return cls._lock @classmethod - def pandas(tclass, *targs, **tkwargs): + def pandas(cls, **tqdm_kwargs): """ - Registers the given `tqdm` class with + Registers the current `tqdm` class with pandas.core. ( frame.DataFrame | series.Series @@ -639,11 +673,11 @@ class tqdm(Comparable): ).progress_apply A new instance will be create every time `progress_apply` is called, - and each instance will automatically close() upon completion. + and each instance will automatically `close()` upon completion. Parameters ---------- - targs, tkwargs : arguments for the tqdm instance + tqdm_kwargs : arguments for the tqdm instance Examples -------- @@ -659,35 +693,43 @@ class tqdm(Comparable): References ---------- - https://stackoverflow.com/questions/18603270/ - progress-indicator-during-pandas-operations-python + <https://stackoverflow.com/questions/18603270/\ + progress-indicator-during-pandas-operations-python> """ + from warnings import catch_warnings, simplefilter + from pandas.core.frame import DataFrame from pandas.core.series import Series try: - from pandas import Panel - except ImportError: # TODO: pandas>0.25.2 + with catch_warnings(): + simplefilter("ignore", category=FutureWarning) + from pandas import Panel + except ImportError: # pandas>=1.2.0 Panel = None + Rolling, Expanding = None, None try: # pandas>=1.0.0 from pandas.core.window.rolling import _Rolling_and_Expanding except ImportError: try: # pandas>=0.18.0 from pandas.core.window import _Rolling_and_Expanding - except ImportError: # pragma: no cover - _Rolling_and_Expanding = None + except ImportError: # pandas>=1.2.0 + try: # pandas>=1.2.0 + from pandas.core.window.expanding import Expanding + from pandas.core.window.rolling import Rolling + _Rolling_and_Expanding = Rolling, Expanding + except ImportError: # pragma: no cover + _Rolling_and_Expanding = None try: # pandas>=0.25.0 - from pandas.core.groupby.generic import DataFrameGroupBy, \ - SeriesGroupBy # , NDFrameGroupBy - except ImportError: + from pandas.core.groupby.generic import SeriesGroupBy # , NDFrameGroupBy + from pandas.core.groupby.generic import DataFrameGroupBy + except ImportError: # pragma: no cover try: # pandas>=0.23.0 - from pandas.core.groupby.groupby import DataFrameGroupBy, \ - SeriesGroupBy + from pandas.core.groupby.groupby import DataFrameGroupBy, SeriesGroupBy except ImportError: - from pandas.core.groupby import DataFrameGroupBy, \ - SeriesGroupBy + from pandas.core.groupby import DataFrameGroupBy, SeriesGroupBy try: # pandas>=0.23.0 from pandas.core.groupby.groupby import GroupBy - except ImportError: + except ImportError: # pragma: no cover from pandas.core.groupby import GroupBy try: # pandas>=0.23.0 @@ -698,7 +740,8 @@ class tqdm(Comparable): except ImportError: # pandas>=0.25.0 PanelGroupBy = None - deprecated_t = [tkwargs.pop('deprecated_t', None)] + tqdm_kwargs = tqdm_kwargs.copy() + deprecated_t = [tqdm_kwargs.pop('deprecated_t', None)] def inner_generator(df_function='apply'): def inner(df, func, *args, **kwargs): @@ -714,14 +757,14 @@ class tqdm(Comparable): """ # Precompute total iterations - total = tkwargs.pop("total", getattr(df, 'ngroups', None)) + total = tqdm_kwargs.pop("total", getattr(df, 'ngroups', None)) if total is None: # not grouped if df_function == 'applymap': total = df.size elif isinstance(df, Series): total = len(df) - elif _Rolling_and_Expanding is None or \ - not isinstance(df, _Rolling_and_Expanding): + elif (_Rolling_and_Expanding is None or + not isinstance(df, _Rolling_and_Expanding)): # DataFrame or Panel axis = kwargs.get('axis', 0) if axis == 'index': @@ -736,7 +779,7 @@ class tqdm(Comparable): t = deprecated_t[0] deprecated_t[0] = None else: - t = tclass(*targs, total=total, **tkwargs) + t = cls(total=total, **tqdm_kwargs) if len(args) > 0: # *args intentionally not supported (see #244, #299) @@ -747,8 +790,12 @@ class tqdm(Comparable): " Use keyword arguments instead.", fp_write=getattr(t.fp, 'write', sys.stderr.write)) + try: # pandas>=1.3.0 + from pandas.core.common import is_builtin_func + except ImportError: + is_builtin_func = df._is_builtin_func try: - func = df._is_builtin_func(func) + func = is_builtin_func(func) except TypeError: pass @@ -790,17 +837,19 @@ class tqdm(Comparable): GroupBy.progress_aggregate = inner_generator('aggregate') GroupBy.progress_transform = inner_generator('transform') - if _Rolling_and_Expanding is not None: # pragma: no cover + if Rolling is not None and Expanding is not None: + Rolling.progress_apply = inner_generator() + Expanding.progress_apply = inner_generator() + elif _Rolling_and_Expanding is not None: _Rolling_and_Expanding.progress_apply = inner_generator() - def __init__(self, iterable=None, desc=None, total=None, leave=True, - file=None, ncols=None, mininterval=0.1, maxinterval=10.0, - miniters=None, ascii=None, disable=False, unit='it', - unit_scale=False, dynamic_ncols=False, smoothing=0.3, - bar_format=None, initial=0, position=None, postfix=None, - unit_divisor=1000, write_bytes=None, lock_args=None, - nrows=None, - gui=False, **kwargs): + def __init__(self, iterable=None, desc=None, total=None, leave=True, file=None, + ncols=None, mininterval=0.1, maxinterval=10.0, miniters=None, + ascii=None, disable=False, unit='it', unit_scale=False, + dynamic_ncols=False, smoothing=0.3, bar_format=None, initial=0, + position=None, postfix=None, unit_divisor=1000, write_bytes=None, + lock_args=None, nrows=None, colour=None, delay=0, gui=False, + **kwargs): """ Parameters ---------- @@ -878,7 +927,7 @@ class tqdm(Comparable): percentage, elapsed, elapsed_s, ncols, nrows, desc, unit, rate, rate_fmt, rate_noinv, rate_noinv_fmt, rate_inv, rate_inv_fmt, postfix, unit_divisor, - remaining, remaining_s. + remaining, remaining_s, eta. Note that a trailing ": " is automatically removed after {desc} if the latter is empty. initial : int or float, optional @@ -905,6 +954,10 @@ class tqdm(Comparable): The screen height. If specified, hides nested bars outside this bound. If unspecified, attempts to use environment height. The fallback is 20. + colour : str, optional + Bar colour (e.g. 'green', '#00ff00'). + delay : float, optional + Don't display until [default: 0] seconds have elapsed. gui : bool, optional WARNING: internal parameter - do not use. Use tqdm.gui.tqdm(...) instead. If set, will attempt to use @@ -926,6 +979,8 @@ class tqdm(Comparable): file = SimpleTextIOWrapper( file, encoding=getattr(file, 'encoding', None) or 'utf-8') + file = DisableOnWriteError(file, tqdm_instance=self) + if disable is None and hasattr(file, "isatty") and not file.isatty(): disable = True @@ -963,9 +1018,9 @@ class tqdm(Comparable): TqdmKeyError("Unknown argument(s): " + str(kwargs))) # Preprocess the arguments - if ((ncols is None or nrows is None) and - (file in (sys.stderr, sys.stdout))) or \ - dynamic_ncols: # pragma: no cover + if ( + (ncols is None or nrows is None) and (file in (sys.stderr, sys.stdout)) + ) or dynamic_ncols: # pragma: no cover if dynamic_ncols: dynamic_ncols = _screen_shape_wrapper() if dynamic_ncols: @@ -994,7 +1049,7 @@ class tqdm(Comparable): if ascii is None: ascii = not _supports_unicode(file) - if bar_format and not ((ascii is True) or _is_ascii(ascii)): + if bar_format and ascii is not True and not _is_ascii(ascii): # Convert bar format into unicode since terminal uses unicode bar_format = _unicode(bar_format) @@ -1018,14 +1073,19 @@ class tqdm(Comparable): self.unit = unit self.unit_scale = unit_scale self.unit_divisor = unit_divisor + self.initial = initial self.lock_args = lock_args + self.delay = delay self.gui = gui self.dynamic_ncols = dynamic_ncols self.smoothing = smoothing - self.avg_time = None - self._time = time + self._ema_dn = EMA(smoothing) + self._ema_dt = EMA(smoothing) + self._ema_miniters = EMA(smoothing) self.bar_format = bar_format self.postfix = None + self.colour = colour + self._time = time if postfix: try: self.set_postfix(refresh=False, **postfix) @@ -1039,15 +1099,14 @@ class tqdm(Comparable): # if nested, at initial sp() call we replace '\r' by '\n' to # not overwrite the outer progress bar with self._lock: - if position is None: - self.pos = self._get_free_pos(self) - else: # mark fixed positions as negative - self.pos = -position + # mark fixed positions as negative + self.pos = self._get_free_pos(self) if position is None else -position if not gui: # Initialize the screen printer self.sp = self.status_printer(self.fp) - self.refresh(lock_args=self.lock_args) + if delay <= 0: + self.refresh(lock_args=self.lock_args) # Init the time counter self.last_print_t = self._time() @@ -1065,10 +1124,12 @@ class tqdm(Comparable): return self.__bool__() def __len__(self): - return self.total if self.iterable is None else \ - (self.iterable.shape[0] if hasattr(self.iterable, "shape") - else len(self.iterable) if hasattr(self.iterable, "__len__") - else getattr(self, "total", None)) + return ( + self.total if self.iterable is None + else self.iterable.shape[0] if hasattr(self.iterable, "shape") + else len(self.iterable) if hasattr(self.iterable, "__len__") + else self.iterable.__length_hint__() if hasattr(self.iterable, "__length_hint__") + else getattr(self, "total", None)) def __enter__(self): return self @@ -1085,7 +1146,7 @@ class tqdm(Comparable): def __del__(self): self.close() - def __repr__(self): + def __str__(self): return self.format_meter(**self.format_dict) @property @@ -1109,76 +1170,28 @@ class tqdm(Comparable): return mininterval = self.mininterval - maxinterval = self.maxinterval - miniters = self.miniters - dynamic_miniters = self.dynamic_miniters last_print_t = self.last_print_t last_print_n = self.last_print_n + min_start_t = self.start_t + self.delay n = self.n - smoothing = self.smoothing - avg_time = self.avg_time time = self._time - if not hasattr(self, 'sp'): - raise TqdmDeprecationWarning( - "Please use `tqdm.gui.tqdm(...)` instead of" - " `tqdm(..., gui=True)`\n", - fp_write=getattr(self.fp, 'write', sys.stderr.write)) - try: for obj in iterable: yield obj # Update and possibly print the progressbar. # Note: does not call self.update(1) for speed optimisation. n += 1 - # check counter first to avoid calls to time() + if n - last_print_n >= self.miniters: - miniters = self.miniters # watch monitoring thread changes - delta_t = time() - last_print_t - if delta_t >= mininterval: - cur_t = time() - delta_it = n - last_print_n - # EMA (not just overall average) - if smoothing and delta_t and delta_it: - rate = delta_t / delta_it - avg_time = self.ema(rate, avg_time, smoothing) - self.avg_time = avg_time - - self.n = n - self.refresh(lock_args=self.lock_args) - - # If no `miniters` was specified, adjust automatically - # to the max iteration rate seen so far between 2 prints - if dynamic_miniters: - if maxinterval and delta_t >= maxinterval: - # Adjust miniters to time interval by rule of 3 - if mininterval: - # Set miniters to correspond to mininterval - miniters = delta_it * mininterval / delta_t - else: - # Set miniters to correspond to maxinterval - miniters = delta_it * maxinterval / delta_t - elif smoothing: - # EMA-weight miniters to converge - # towards the timeframe of mininterval - rate = delta_it - if mininterval and delta_t: - rate *= mininterval / delta_t - miniters = self.ema(rate, miniters, smoothing) - else: - # Maximum nb of iterations between 2 prints - miniters = max(miniters, delta_it) - - # Store old values for next call - self.n = self.last_print_n = last_print_n = n - self.last_print_t = last_print_t = cur_t - self.miniters = miniters + cur_t = time() + dt = cur_t - last_print_t + if dt >= mininterval and cur_t >= min_start_t: + self.update(n - last_print_n) + last_print_n = self.last_print_n + last_print_t = self.last_print_t finally: - # Closing the progress bar. - # Update some internal variables for close(). - self.last_print_n = last_print_n self.n = n - self.miniters = miniters self.close() def update(self, n=1): @@ -1201,8 +1214,12 @@ class tqdm(Comparable): Increment to add to the internal counter of iterations [default: 1]. If using float, consider specifying `{n:.3f}` or similar in `bar_format`, or specifying `unit_scale`. + + Returns + ------- + out : bool or None + True if a `display()` was triggered. """ - # N.B.: see __iter__() for more comments. if self.disable: return @@ -1212,50 +1229,37 @@ class tqdm(Comparable): # check counter first to reduce calls to time() if self.n - self.last_print_n >= self.miniters: - delta_t = self._time() - self.last_print_t - if delta_t >= self.mininterval: + cur_t = self._time() + dt = cur_t - self.last_print_t + if dt >= self.mininterval and cur_t >= self.start_t + self.delay: cur_t = self._time() - delta_it = self.n - self.last_print_n # >= n - # elapsed = cur_t - self.start_t - # EMA (not just overall average) - if self.smoothing and delta_t and delta_it: - rate = delta_t / delta_it - self.avg_time = self.ema( - rate, self.avg_time, self.smoothing) - - if not hasattr(self, "sp"): - raise TqdmDeprecationWarning( - "Please use `tqdm.gui.tqdm(...)`" - " instead of `tqdm(..., gui=True)`\n", - fp_write=getattr(self.fp, 'write', sys.stderr.write)) - + dn = self.n - self.last_print_n # >= n + if self.smoothing and dt and dn: + # EMA (not just overall average) + self._ema_dn(dn) + self._ema_dt(dt) self.refresh(lock_args=self.lock_args) - - # If no `miniters` was specified, adjust automatically to the - # maximum iteration rate seen so far between two prints. - # e.g.: After running `tqdm.update(5)`, subsequent - # calls to `tqdm.update()` will only cause an update after - # at least 5 more iterations. if self.dynamic_miniters: - if self.maxinterval and delta_t >= self.maxinterval: - if self.mininterval: - self.miniters = delta_it * self.mininterval \ - / delta_t - else: - self.miniters = delta_it * self.maxinterval \ - / delta_t + # If no `miniters` was specified, adjust automatically to the + # maximum iteration rate seen so far between two prints. + # e.g.: After running `tqdm.update(5)`, subsequent + # calls to `tqdm.update()` will only cause an update after + # at least 5 more iterations. + if self.maxinterval and dt >= self.maxinterval: + self.miniters = dn * (self.mininterval or self.maxinterval) / dt elif self.smoothing: - self.miniters = self.smoothing * delta_it * \ - (self.mininterval / delta_t - if self.mininterval and delta_t - else 1) + \ - (1 - self.smoothing) * self.miniters + # EMA miniters update + self.miniters = self._ema_miniters( + dn * (self.mininterval / dt if self.mininterval and dt + else 1)) else: - self.miniters = max(self.miniters, delta_it) + # max iters between two prints + self.miniters = max(self.miniters, dn) # Store old values for next call self.last_print_n = self.n self.last_print_t = cur_t + return True def close(self): """Cleanup and (if leave=False) close the progressbar.""" @@ -1269,8 +1273,12 @@ class tqdm(Comparable): pos = abs(self.pos) self._decr_instances(self) + if self.last_print_t < self.start_t + self.delay: + # haven't ever displayed; nothing to clear + return + # GUI mode - if not hasattr(self, "sp"): + if getattr(self, 'sp', None) is None: return # annoyingly, _supports_unicode isn't good enough @@ -1289,7 +1297,7 @@ class tqdm(Comparable): with self._lock: if leave: # stats for overall rate (no weighted average) - self.avg_time = None + self._ema_dt = lambda: None self.display(pos=0) fp_write('\n') else: @@ -1342,6 +1350,8 @@ class tqdm(Comparable): def unpause(self): """Restart tqdm timer from last print time.""" + if self.disable: + return cur_t = self._time() self.start_t += cur_t - self.last_print_t self.last_print_t = cur_t @@ -1356,10 +1366,16 @@ class tqdm(Comparable): ---------- total : int or float, optional. Total to use for the new bar. """ - self.last_print_n = self.n = 0 - self.last_print_t = self.start_t = self._time() + self.n = 0 if total is not None: self.total = total + if self.disable: + return + self.last_print_n = 0 + self.last_print_t = self.start_t = self._time() + self._ema_dn = EMA(self.smoothing) + self._ema_dt = EMA(self.smoothing) + self._ema_miniters = EMA(self.smoothing) self.refresh() def set_description(self, desc=None, refresh=True): @@ -1395,7 +1411,7 @@ class tqdm(Comparable): kwargs : dict, optional """ # Sort in alphabetical order to be more deterministic - postfix = _OrderedDict([] if ordered_dict is None else ordered_dict) + postfix = OrderedDict([] if ordered_dict is None else ordered_dict) for key in sorted(kwargs.keys()): postfix[key] = kwargs[key] # Preprocess stats according to datatype @@ -1429,19 +1445,20 @@ class tqdm(Comparable): @property def format_dict(self): """Public API for read-only member access.""" + if self.disable and not hasattr(self, 'unit'): + return defaultdict(lambda: None, { + 'n': self.n, 'total': self.total, 'elapsed': 0, 'unit': 'it'}) if self.dynamic_ncols: self.ncols, self.nrows = self.dynamic_ncols(self.fp) - ncols, nrows = self.ncols, self.nrows - return dict( - n=self.n, total=self.total, - elapsed=self._time() - self.start_t - if hasattr(self, 'start_t') else 0, - ncols=ncols, nrows=nrows, - prefix=self.desc, ascii=self.ascii, unit=self.unit, - unit_scale=self.unit_scale, - rate=1 / self.avg_time if self.avg_time else None, - bar_format=self.bar_format, postfix=self.postfix, - unit_divisor=self.unit_divisor) + return { + 'n': self.n, 'total': self.total, + 'elapsed': self._time() - self.start_t if hasattr(self, 'start_t') else 0, + 'ncols': self.ncols, 'nrows': self.nrows, 'prefix': self.desc, + 'ascii': self.ascii, 'unit': self.unit, 'unit_scale': self.unit_scale, + 'rate': self._ema_dn() / self._ema_dt() if self._ema_dt() else None, + 'bar_format': self.bar_format, 'postfix': self.postfix, + 'unit_divisor': self.unit_divisor, 'initial': self.initial, + 'colour': self.colour} def display(self, msg=None, pos=None): """ @@ -1466,16 +1483,22 @@ class tqdm(Comparable): if msg or msg is None: # override at `nrows - 1` msg = " ... (more hidden) ..." + if not hasattr(self, "sp"): + raise TqdmDeprecationWarning( + "Please use `tqdm.gui.tqdm(...)`" + " instead of `tqdm(..., gui=True)`\n", + fp_write=getattr(self.fp, 'write', sys.stderr.write)) + if pos: self.moveto(pos) - self.sp(self.__repr__() if msg is None else msg) + self.sp(self.__str__() if msg is None else msg) if pos: self.moveto(-pos) return True @classmethod @contextmanager - def wrapattr(tclass, stream, method, total=None, bytes=True, **tkwargs): + def wrapattr(cls, stream, method, total=None, bytes=True, **tqdm_kwargs): """ stream : file-like object. method : str, "read" or "write". The result of `read()` and @@ -1487,7 +1510,7 @@ class tqdm(Comparable): ... if not chunk: ... break """ - with tclass(total=total, **tkwargs) as t: + with cls(total=total, **tqdm_kwargs) as t: if bytes: t.unit = "B" t.unit_scale = True |