| """ |
| IPython/Jupyter Notebook progressbar decorator for iterators. |
| Includes a default `range` iterator printing to `stderr`. |
| |
| Usage: |
| >>> from tqdm.notebook import trange, tqdm |
| >>> for i in trange(10): |
| ... ... |
| """ |
| # future division is important to divide integers and get as |
| # a result precise floating numbers (instead of truncated int) |
| from __future__ import absolute_import, division |
| |
| # import compatibility functions and utilities |
| import re |
| import sys |
| from weakref import proxy |
| |
| # to inherit from the tqdm class |
| from .std import tqdm as std_tqdm |
| from .utils import _range |
| |
| if True: # pragma: no cover |
| # import IPython/Jupyter base widget and display utilities |
| IPY = 0 |
| try: # IPython 4.x |
| import ipywidgets |
| IPY = 4 |
| except ImportError: # IPython 3.x / 2.x |
| IPY = 32 |
| import warnings |
| with warnings.catch_warnings(): |
| warnings.filterwarnings( |
| 'ignore', message=".*The `IPython.html` package has been deprecated.*") |
| try: |
| import IPython.html.widgets as ipywidgets # NOQA: F401 |
| except ImportError: |
| pass |
| |
| try: # IPython 4.x / 3.x |
| if IPY == 32: |
| from IPython.html.widgets import HTML |
| from IPython.html.widgets import FloatProgress as IProgress |
| from IPython.html.widgets import HBox |
| IPY = 3 |
| else: |
| from ipywidgets import HTML |
| from ipywidgets import FloatProgress as IProgress |
| from ipywidgets import HBox |
| except ImportError: |
| try: # IPython 2.x |
| from IPython.html.widgets import HTML |
| from IPython.html.widgets import ContainerWidget as HBox |
| from IPython.html.widgets import FloatProgressWidget as IProgress |
| IPY = 2 |
| except ImportError: |
| IPY = 0 |
| IProgress = None |
| HBox = object |
| |
| try: |
| from IPython.display import display # , clear_output |
| except ImportError: |
| pass |
| |
| # HTML encoding |
| try: # Py3 |
| from html import escape |
| except ImportError: # Py2 |
| from cgi import escape |
| |
| __author__ = {"github.com/": ["lrq3000", "casperdcl", "alexanderkuk"]} |
| __all__ = ['tqdm_notebook', 'tnrange', 'tqdm', 'trange'] |
| WARN_NOIPYW = ("IProgress not found. Please update jupyter and ipywidgets." |
| " See https://ipywidgets.readthedocs.io/en/stable" |
| "/user_install.html") |
| |
| |
| class TqdmHBox(HBox): |
| """`ipywidgets.HBox` with a pretty representation""" |
| def _json_(self, pretty=None): |
| pbar = getattr(self, 'pbar', None) |
| if pbar is None: |
| return {} |
| d = pbar.format_dict |
| if pretty is not None: |
| d["ascii"] = not pretty |
| return d |
| |
| def __repr__(self, pretty=False): |
| pbar = getattr(self, 'pbar', None) |
| if pbar is None: |
| return super(TqdmHBox, self).__repr__() |
| return pbar.format_meter(**self._json_(pretty)) |
| |
| def _repr_pretty_(self, pp, *_, **__): |
| pp.text(self.__repr__(True)) |
| |
| |
| class tqdm_notebook(std_tqdm): |
| """ |
| Experimental IPython/Jupyter Notebook widget using tqdm! |
| """ |
| @staticmethod |
| def status_printer(_, total=None, desc=None, ncols=None): |
| """ |
| Manage the printing of an IPython/Jupyter Notebook progress bar widget. |
| """ |
| # Fallback to text bar if there's no total |
| # DEPRECATED: replaced with an 'info' style bar |
| # if not total: |
| # return super(tqdm_notebook, tqdm_notebook).status_printer(file) |
| |
| # fp = file |
| |
| # Prepare IPython progress bar |
| if IProgress is None: # #187 #451 #558 #872 |
| raise ImportError(WARN_NOIPYW) |
| if total: |
| pbar = IProgress(min=0, max=total) |
| else: # No total? Show info style bar with no progress tqdm status |
| pbar = IProgress(min=0, max=1) |
| pbar.value = 1 |
| pbar.bar_style = 'info' |
| if ncols is None: |
| pbar.layout.width = "20px" |
| |
| ltext = HTML() |
| rtext = HTML() |
| if desc: |
| ltext.value = desc |
| container = TqdmHBox(children=[ltext, pbar, rtext]) |
| # Prepare layout |
| if ncols is not None: # use default style of ipywidgets |
| # ncols could be 100, "100px", "100%" |
| ncols = str(ncols) # ipywidgets only accepts string |
| try: |
| if int(ncols) > 0: # isnumeric and positive |
| ncols += 'px' |
| except ValueError: |
| pass |
| pbar.layout.flex = '2' |
| container.layout.width = ncols |
| container.layout.display = 'inline-flex' |
| container.layout.flex_flow = 'row wrap' |
| |
| return container |
| |
| def display(self, msg=None, pos=None, |
| # additional signals |
| close=False, bar_style=None, check_delay=True): |
| # Note: contrary to native tqdm, msg='' does NOT clear bar |
| # goal is to keep all infos if error happens so user knows |
| # at which iteration the loop failed. |
| |
| # Clear previous output (really necessary?) |
| # clear_output(wait=1) |
| |
| if not msg and not close: |
| d = self.format_dict |
| # remove {bar} |
| d['bar_format'] = (d['bar_format'] or "{l_bar}<bar/>{r_bar}").replace( |
| "{bar}", "<bar/>") |
| msg = self.format_meter(**d) |
| |
| ltext, pbar, rtext = self.container.children |
| pbar.value = self.n |
| |
| if msg: |
| # html escape special characters (like '&') |
| if '<bar/>' in msg: |
| left, right = map(escape, re.split(r'\|?<bar/>\|?', msg, 1)) |
| else: |
| left, right = '', escape(msg) |
| |
| # Update description |
| ltext.value = left |
| # never clear the bar (signal: msg='') |
| if right: |
| rtext.value = right |
| |
| # Change bar style |
| if bar_style: |
| # Hack-ish way to avoid the danger bar_style being overridden by |
| # success because the bar gets closed after the error... |
| if pbar.bar_style != 'danger' or bar_style != 'success': |
| pbar.bar_style = bar_style |
| |
| # Special signal to close the bar |
| if close and pbar.bar_style != 'danger': # hide only if no error |
| try: |
| self.container.close() |
| except AttributeError: |
| self.container.visible = False |
| self.container.layout.visibility = 'hidden' # IPYW>=8 |
| |
| if check_delay and self.delay > 0 and not self.displayed: |
| display(self.container) |
| self.displayed = True |
| |
| @property |
| def colour(self): |
| if hasattr(self, 'container'): |
| return self.container.children[-2].style.bar_color |
| |
| @colour.setter |
| def colour(self, bar_color): |
| if hasattr(self, 'container'): |
| self.container.children[-2].style.bar_color = bar_color |
| |
| def __init__(self, *args, **kwargs): |
| """ |
| Supports the usual `tqdm.tqdm` parameters as well as those listed below. |
| |
| Parameters |
| ---------- |
| display : Whether to call `display(self.container)` immediately |
| [default: True]. |
| """ |
| kwargs = kwargs.copy() |
| # Setup default output |
| file_kwarg = kwargs.get('file', sys.stderr) |
| if file_kwarg is sys.stderr or file_kwarg is None: |
| kwargs['file'] = sys.stdout # avoid the red block in IPython |
| |
| # Initialize parent class + avoid printing by using gui=True |
| kwargs['gui'] = True |
| # convert disable = None to False |
| kwargs['disable'] = bool(kwargs.get('disable', False)) |
| colour = kwargs.pop('colour', None) |
| display_here = kwargs.pop('display', True) |
| super(tqdm_notebook, self).__init__(*args, **kwargs) |
| if self.disable or not kwargs['gui']: |
| self.disp = lambda *_, **__: None |
| return |
| |
| # Get bar width |
| self.ncols = '100%' if self.dynamic_ncols else kwargs.get("ncols", None) |
| |
| # Replace with IPython progress bar display (with correct total) |
| unit_scale = 1 if self.unit_scale is True else self.unit_scale or 1 |
| total = self.total * unit_scale if self.total else self.total |
| self.container = self.status_printer(self.fp, total, self.desc, self.ncols) |
| self.container.pbar = proxy(self) |
| self.displayed = False |
| if display_here and self.delay <= 0: |
| display(self.container) |
| self.displayed = True |
| self.disp = self.display |
| self.colour = colour |
| |
| # Print initial bar state |
| if not self.disable: |
| self.display(check_delay=False) |
| |
| def __iter__(self): |
| try: |
| it = super(tqdm_notebook, self).__iter__() |
| for obj in it: |
| # return super(tqdm...) will not catch exception |
| yield obj |
| # NB: except ... [ as ...] breaks IPython async KeyboardInterrupt |
| except: # NOQA |
| self.disp(bar_style='danger') |
| raise |
| # NB: don't `finally: close()` |
| # since this could be a shared bar which the user will `reset()` |
| |
| def update(self, n=1): |
| try: |
| return super(tqdm_notebook, self).update(n=n) |
| # NB: except ... [ as ...] breaks IPython async KeyboardInterrupt |
| except: # NOQA |
| # cannot catch KeyboardInterrupt when using manual tqdm |
| # as the interrupt will most likely happen on another statement |
| self.disp(bar_style='danger') |
| raise |
| # NB: don't `finally: close()` |
| # since this could be a shared bar which the user will `reset()` |
| |
| def close(self): |
| if self.disable: |
| return |
| super(tqdm_notebook, self).close() |
| # Try to detect if there was an error or KeyboardInterrupt |
| # in manual mode: if n < total, things probably got wrong |
| if self.total and self.n < self.total: |
| self.disp(bar_style='danger', check_delay=False) |
| else: |
| if self.leave: |
| self.disp(bar_style='success', check_delay=False) |
| else: |
| self.disp(close=True, check_delay=False) |
| |
| def clear(self, *_, **__): |
| pass |
| |
| def reset(self, total=None): |
| """ |
| Resets to 0 iterations for repeated use. |
| |
| Consider combining with `leave=True`. |
| |
| Parameters |
| ---------- |
| total : int or float, optional. Total to use for the new bar. |
| """ |
| if self.disable: |
| return super(tqdm_notebook, self).reset(total=total) |
| _, pbar, _ = self.container.children |
| pbar.bar_style = '' |
| if total is not None: |
| pbar.max = total |
| if not self.total and self.ncols is None: # no longer unknown total |
| pbar.layout.width = None # reset width |
| return super(tqdm_notebook, self).reset(total=total) |
| |
| |
| def tnrange(*args, **kwargs): |
| """ |
| A shortcut for `tqdm.notebook.tqdm(xrange(*args), **kwargs)`. |
| On Python3+, `range` is used instead of `xrange`. |
| """ |
| return tqdm_notebook(_range(*args), **kwargs) |
| |
| |
| # Aliases |
| tqdm = tqdm_notebook |
| trange = tnrange |