diff options
Diffstat (limited to 'libs/tqdm/notebook.py')
-rw-r--r-- | libs/tqdm/notebook.py | 201 |
1 files changed, 123 insertions, 78 deletions
diff --git a/libs/tqdm/notebook.py b/libs/tqdm/notebook.py index 570510370..1f488d25f 100644 --- a/libs/tqdm/notebook.py +++ b/libs/tqdm/notebook.py @@ -1,61 +1,62 @@ """ IPython/Jupyter Notebook progressbar decorator for iterators. -Includes a default (x)range iterator printing to stderr. +Includes a default `range` iterator printing to `stderr`. Usage: - >>> from tqdm.notebook import trange[, tqdm] - >>> for i in trange(10): #same as: for i in tqdm(xrange(10)) - ... ... +>>> 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 division, absolute_import +from __future__ import absolute_import, division + # import compatibility functions and utilities +import re import sys -from .utils import _range +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 - IPYW = 0 try: # IPython 4.x import ipywidgets IPY = 4 - try: - IPYW = int(ipywidgets.__version__.split('.')[0]) - except AttributeError: # __version__ may not exist in old versions - pass 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.*") + 'ignore', message=".*The `IPython.html` package has been deprecated.*") try: - import IPython.html.widgets as ipywidgets + 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, HTML + from IPython.html.widgets import HBox IPY = 3 else: + from ipywidgets import HTML from ipywidgets import FloatProgress as IProgress - from ipywidgets import HBox, HTML + from ipywidgets import HBox except ImportError: try: # IPython 2.x - from IPython.html.widgets import FloatProgressWidget as IProgress - from IPython.html.widgets import ContainerWidget as HBox 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 @@ -68,16 +69,35 @@ if True: # pragma: no cover except ImportError: # Py2 from cgi import escape - __author__ = {"github.com/": ["lrq3000", "casperdcl", "alexanderkuk"]} __all__ = ['tqdm_notebook', 'tnrange', 'tqdm', 'trange'] +class TqdmHBox(HBox): + """`ipywidgets.HBox` with a pretty representation""" + def _repr_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._repr_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): """ @@ -91,28 +111,25 @@ class tqdm_notebook(std_tqdm): # fp = file # Prepare IPython progress bar - try: - 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' - except NameError: - # #187 #451 #558 + if IProgress is None: # #187 #451 #558 #872 raise ImportError( - "FloatProgress not found. Please update jupyter and ipywidgets." + "IProgress not found. Please update jupyter and ipywidgets." " See https://ipywidgets.readthedocs.io/en/stable" "/user_install.html") - + 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: - pbar.description = desc - if IPYW >= 7: - pbar.style.description_width = 'initial' - # Prepare status text - ptext = HTML() - # Only way to place text to the right of the bar is to use a container - container = HBox(children=[pbar, ptext]) + 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%" @@ -126,13 +143,12 @@ class tqdm_notebook(std_tqdm): container.layout.width = ncols container.layout.display = 'inline-flex' container.layout.flex_flow = 'row wrap' - display(container) return container def display(self, msg=None, pos=None, # additional signals - close=False, bar_style=None): + 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. @@ -141,38 +157,33 @@ class tqdm_notebook(std_tqdm): # clear_output(wait=1) if not msg and not close: - msg = self.__repr__() + 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) - pbar, ptext = self.container.children + 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, msg.split('<bar/>', 1)) + left, right = map(escape, re.split(r'\|?<bar/>\|?', msg, 1)) else: left, right = '', escape(msg) - # remove inesthetical pipes - if left and left[-1] == '|': - left = left[:-1] - if right and right[0] == '|': - right = right[1:] - # Update description - pbar.description = left - if IPYW >= 7: - pbar.style.description_width = 'initial' - + ltext.value = left # never clear the bar (signal: msg='') if right: - ptext.value = 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 not (pbar.bar_style == 'danger' and bar_style == 'success'): + if pbar.bar_style != 'danger' or bar_style != 'success': pbar.bar_style = bar_style # Special signal to close the bar @@ -182,7 +193,30 @@ class tqdm_notebook(std_tqdm): except AttributeError: self.container.visible = False + 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: @@ -190,13 +224,13 @@ class tqdm_notebook(std_tqdm): # Initialize parent class + avoid printing by using gui=True kwargs['gui'] = True - kwargs.setdefault('bar_format', '{l_bar}{bar}{r_bar}') - kwargs['bar_format'] = kwargs['bar_format'].replace('{bar}', '<bar/>') # 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.sp = lambda *_, **__: None + self.disp = lambda *_, **__: None return # Get bar width @@ -205,53 +239,59 @@ class tqdm_notebook(std_tqdm): # 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.sp = self.display + 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() + self.display(check_delay=False) - def __iter__(self, *args, **kwargs): + def __iter__(self): try: - for obj in super(tqdm_notebook, self).__iter__(*args, **kwargs): + for obj in super(tqdm_notebook, self).__iter__(): # return super(tqdm...) will not catch exception yield obj # NB: except ... [ as ...] breaks IPython async KeyboardInterrupt except: # NOQA - self.sp(bar_style='danger') + 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, *args, **kwargs): + def update(self, n=1): try: - super(tqdm_notebook, self).update(*args, **kwargs) + 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.sp(bar_style='danger') + 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, *args, **kwargs): - super(tqdm_notebook, self).close(*args, **kwargs) + 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.sp(bar_style='danger') + self.disp(bar_style='danger', check_delay=False) else: if self.leave: - self.sp(bar_style='success') + self.disp(bar_style='success', check_delay=False) else: - self.sp(close=True) + self.disp(close=True, check_delay=False) - def moveto(self, *args, **kwargs): - # void -> avoid extraneous `\n` in IPython output cell - return + def clear(self, *_, **__): + pass def reset(self, total=None): """ @@ -263,9 +303,14 @@ class tqdm_notebook(std_tqdm): ---------- 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, _ = self.container.children 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) |