codekingpro/portable-devtools
115k
1"""2IPython/Jupyter Notebook progressbar decorator for iterators.3Includes a default `range` iterator printing to `stderr`.4 5Usage:6>>> from tqdm.notebook import trange, tqdm7>>> for i in trange(10):8... ...9"""10# import compatibility functions and utilities11import re12import sys13from html import escape14from weakref import proxy15 16# to inherit from the tqdm class17from .std import tqdm as std_tqdm18 19if True: # pragma: no cover20 # import IPython/Jupyter base widget and display utilities21 IPY = 022 try: # IPython 4.x23 import ipywidgets24 IPY = 425 except ImportError: # IPython 3.x / 2.x26 IPY = 3227 import warnings28 with warnings.catch_warnings():29 warnings.filterwarnings(30 'ignore', message=".*The `IPython.html` package has been deprecated.*")31 try:32 import IPython.html.widgets as ipywidgets # NOQA: F40133 except ImportError:34 pass35 36 try: # IPython 4.x / 3.x37 if IPY == 32:38 from IPython.html.widgets import HTML39 from IPython.html.widgets import FloatProgress as IProgress40 from IPython.html.widgets import HBox41 IPY = 342 else:43 from ipywidgets import HTML44 from ipywidgets import FloatProgress as IProgress45 from ipywidgets import HBox46 except ImportError:47 try: # IPython 2.x48 from IPython.html.widgets import HTML49 from IPython.html.widgets import ContainerWidget as HBox50 from IPython.html.widgets import FloatProgressWidget as IProgress51 IPY = 252 except ImportError:53 IPY = 054 IProgress = None55 HBox = object56 57 try:58 from IPython.display import display # , clear_output59 except ImportError:60 pass61 62__author__ = {"github.com/": ["lrq3000", "casperdcl", "alexanderkuk"]}63__all__ = ['tqdm_notebook', 'tnrange', 'tqdm', 'trange']64WARN_NOIPYW = ("IProgress not found. Please update jupyter and ipywidgets."65 " See https://ipywidgets.readthedocs.io/en/stable"66 "/user_install.html")67 68 69class TqdmHBox(HBox):70 """`ipywidgets.HBox` with a pretty representation"""71 def _json_(self, pretty=None):72 pbar = getattr(self, 'pbar', None)73 if pbar is None:74 return {}75 d = pbar.format_dict76 if pretty is not None:77 d["ascii"] = not pretty78 return d79 80 def __repr__(self, pretty=False):81 pbar = getattr(self, 'pbar', None)82 if pbar is None:83 return super().__repr__()84 return pbar.format_meter(**self._json_(pretty))85 86 def _repr_pretty_(self, pp, *_, **__):87 pp.text(self.__repr__(True))88 89 90class tqdm_notebook(std_tqdm):91 """92 Experimental IPython/Jupyter Notebook widget using tqdm!93 """94 @staticmethod95 def status_printer(_, total=None, desc=None, ncols=None):96 """97 Manage the printing of an IPython/Jupyter Notebook progress bar widget.98 """99 # Fallback to text bar if there's no total100 # DEPRECATED: replaced with an 'info' style bar101 # if not total:102 # return super(tqdm_notebook, tqdm_notebook).status_printer(file)103 104 # fp = file105 106 # Prepare IPython progress bar107 if IProgress is None: # #187 #451 #558 #872108 raise ImportError(WARN_NOIPYW)109 if total:110 pbar = IProgress(min=0, max=total)111 else: # No total? Show info style bar with no progress tqdm status112 pbar = IProgress(min=0, max=1)113 pbar.value = 1114 pbar.bar_style = 'info'115 if ncols is None:116 pbar.layout.width = "20px"117 118 ltext = HTML()119 rtext = HTML()120 if desc:121 ltext.value = desc122 container = TqdmHBox(children=[ltext, pbar, rtext])123 # Prepare layout124 if ncols is not None: # use default style of ipywidgets125 # ncols could be 100, "100px", "100%"126 ncols = str(ncols) # ipywidgets only accepts string127 try:128 if int(ncols) > 0: # isnumeric and positive129 ncols += 'px'130 except ValueError:131 pass132 pbar.layout.flex = '2'133 container.layout.width = ncols134 container.layout.display = 'inline-flex'135 container.layout.flex_flow = 'row wrap'136 137 return container138 139 def display(self, msg=None, pos=None,140 # additional signals141 close=False, bar_style=None, check_delay=True):142 # Note: contrary to native tqdm, msg='' does NOT clear bar143 # goal is to keep all infos if error happens so user knows144 # at which iteration the loop failed.145 146 # Clear previous output (really necessary?)147 # clear_output(wait=1)148 149 if not msg and not close:150 d = self.format_dict151 # remove {bar}152 d['bar_format'] = (d['bar_format'] or "{l_bar}<bar/>{r_bar}").replace(153 "{bar}", "<bar/>")154 msg = self.format_meter(**d)155 156 ltext, pbar, rtext = self.container.children157 pbar.value = self.n158 159 if msg:160 msg = msg.replace(' ', '\u2007') # fix html space padding161 # html escape special characters (like '&')162 if '<bar/>' in msg:163 left, right = map(escape, re.split(r'\|?<bar/>\|?', msg, maxsplit=1))164 else:165 left, right = '', escape(msg)166 167 # Update description168 ltext.value = left169 # never clear the bar (signal: msg='')170 if right:171 rtext.value = right172 173 # Change bar style174 if bar_style:175 # Hack-ish way to avoid the danger bar_style being overridden by176 # success because the bar gets closed after the error...177 if pbar.bar_style != 'danger' or bar_style != 'success':178 pbar.bar_style = bar_style179 180 # Special signal to close the bar181 if close and pbar.bar_style != 'danger': # hide only if no error182 try:183 self.container.close()184 except AttributeError:185 self.container.visible = False186 self.container.layout.visibility = 'hidden' # IPYW>=8187 188 if check_delay and self.delay > 0 and not self.displayed:189 display(self.container)190 self.displayed = True191 192 @property193 def colour(self):194 if hasattr(self, 'container'):195 return self.container.children[-2].style.bar_color196 197 @colour.setter198 def colour(self, bar_color):199 if hasattr(self, 'container'):200 self.container.children[-2].style.bar_color = bar_color201 202 def __init__(self, *args, **kwargs):203 """204 Supports the usual `tqdm.tqdm` parameters as well as those listed below.205 206 Parameters207 ----------208 display : Whether to call `display(self.container)` immediately209 [default: True].210 """211 kwargs = kwargs.copy()212 # Setup default output213 file_kwarg = kwargs.get('file', sys.stderr)214 if file_kwarg is sys.stderr or file_kwarg is None:215 kwargs['file'] = sys.stdout # avoid the red block in IPython216 217 # Initialize parent class + avoid printing by using gui=True218 kwargs['gui'] = True219 # convert disable = None to False220 kwargs['disable'] = bool(kwargs.get('disable', False))221 colour = kwargs.pop('colour', None)222 display_here = kwargs.pop('display', True)223 super().__init__(*args, **kwargs)224 if self.disable or not kwargs['gui']:225 self.disp = lambda *_, **__: None226 return227 228 # Get bar width229 self.ncols = '100%' if self.dynamic_ncols else kwargs.get("ncols", None)230 231 # Replace with IPython progress bar display (with correct total)232 unit_scale = 1 if self.unit_scale is True else self.unit_scale or 1233 total = self.total * unit_scale if self.total else self.total234 self.container = self.status_printer(self.fp, total, self.desc, self.ncols)235 self.container.pbar = proxy(self)236 self.displayed = False237 if display_here and self.delay <= 0:238 display(self.container)239 self.displayed = True240 self.disp = self.display241 self.colour = colour242 243 # Print initial bar state244 if not self.disable:245 self.display(check_delay=False)246 247 def __iter__(self):248 try:249 it = super().__iter__()250 yield from it251 # NB: except ... [ as ...] breaks IPython async KeyboardInterrupt252 except: # NOQA253 self.disp(bar_style='danger')254 raise255 # NB: don't `finally: close()`256 # since this could be a shared bar which the user will `reset()`257 258 def update(self, n=1):259 try:260 return super().update(n=n)261 # NB: except ... [ as ...] breaks IPython async KeyboardInterrupt262 except: # NOQA263 # cannot catch KeyboardInterrupt when using manual tqdm264 # as the interrupt will most likely happen on another statement265 self.disp(bar_style='danger')266 raise267 # NB: don't `finally: close()`268 # since this could be a shared bar which the user will `reset()`269 270 def close(self):271 if self.disable:272 return273 super().close()274 # Try to detect if there was an error or KeyboardInterrupt275 # in manual mode: if n < total, things probably got wrong276 if self.total and self.n < self.total:277 self.disp(bar_style='danger', check_delay=False)278 else:279 if self.leave:280 self.disp(bar_style='success', check_delay=False)281 else:282 self.disp(close=True, check_delay=False)283 284 def clear(self, *_, **__):285 pass286 287 def reset(self, total=None):288 """289 Resets to 0 iterations for repeated use.290 291 Consider combining with `leave=True`.292 293 Parameters294 ----------295 total : int or float, optional. Total to use for the new bar.296 """297 if self.disable:298 return super().reset(total=total)299 _, pbar, _ = self.container.children300 pbar.bar_style = ''301 if total is not None:302 pbar.max = total303 if not self.total and self.ncols is None: # no longer unknown total304 pbar.layout.width = None # reset width305 return super().reset(total=total)306 307 308def tnrange(*args, **kwargs):309 """Shortcut for `tqdm.notebook.tqdm(range(*args), **kwargs)`."""310 return tqdm_notebook(range(*args), **kwargs)311 312 313# Aliases314tqdm = tqdm_notebook315trange = tnrange316 