Coverage for /Volumes/workspace/python-progressbar/.tox/py37/lib/python3.7/site-packages/progressbar/bar.py: 100%
326 statements
« prev ^ index » next coverage.py v6.5.0, created at 2022-10-18 12:46 +0200
« prev ^ index » next coverage.py v6.5.0, created at 2022-10-18 12:46 +0200
1from __future__ import annotations
3import logging
4import math
5import os
6import sys
7import time
8import timeit
9import warnings
10from copy import deepcopy
11from datetime import datetime
13from python_utils import types
15try: # pragma: no cover
16 from collections import abc
17except ImportError: # pragma: no cover
18 import collections as abc
20from python_utils import converters
22from . import widgets
23from . import widgets as widgets_module # Avoid name collision
24from . import base
25from . import utils
27logger = logging.getLogger(__name__)
30class ProgressBarMixinBase(object):
32 def __init__(self, **kwargs):
33 self._finished = False
35 def start(self, **kwargs):
36 pass
38 def update(self, value=None):
39 pass
41 def finish(self): # pragma: no cover
42 self._finished = True
44 def __del__(self):
45 if not self._finished: # pragma: no cover
46 try:
47 self.finish()
48 except Exception:
49 pass
52class ProgressBarBase(abc.Iterable, ProgressBarMixinBase):
53 pass
56class DefaultFdMixin(ProgressBarMixinBase):
58 def __init__(self, fd: types.IO = sys.stderr,
59 is_terminal: bool | None = None,
60 line_breaks: bool | None = None,
61 enable_colors: bool | None = None, **kwargs):
62 if fd is sys.stdout:
63 fd = utils.streams.original_stdout
65 elif fd is sys.stderr:
66 fd = utils.streams.original_stderr
68 self.fd = fd
69 self.is_ansi_terminal = utils.is_ansi_terminal(fd)
71 # Check if this is an interactive terminal
72 self.is_terminal = utils.is_terminal(
73 fd, is_terminal or self.is_ansi_terminal)
75 # Check if it should overwrite the current line (suitable for
76 # iteractive terminals) or write line breaks (suitable for log files)
77 if line_breaks is None:
78 line_breaks = utils.env_flag('PROGRESSBAR_LINE_BREAKS',
79 not self.is_terminal)
80 self.line_breaks = line_breaks
82 # Check if ANSI escape characters are enabled (suitable for iteractive
83 # terminals), or should be stripped off (suitable for log files)
84 if enable_colors is None:
85 enable_colors = utils.env_flag('PROGRESSBAR_ENABLE_COLORS',
86 self.is_ansi_terminal)
88 self.enable_colors = enable_colors
90 ProgressBarMixinBase.__init__(self, **kwargs)
92 def update(self, *args, **kwargs):
93 ProgressBarMixinBase.update(self, *args, **kwargs)
95 line = converters.to_unicode(self._format_line())
96 if not self.enable_colors:
97 line = utils.no_color(line)
99 if self.line_breaks:
100 line = line.rstrip() + '\n'
101 else:
102 line = '\r' + line
104 try: # pragma: no cover
105 self.fd.write(line)
106 except UnicodeEncodeError: # pragma: no cover
107 self.fd.write(line.encode('ascii', 'replace'))
109 def finish(self, *args, **kwargs): # pragma: no cover
110 if self._finished:
111 return
113 end = kwargs.pop('end', '\n')
114 ProgressBarMixinBase.finish(self, *args, **kwargs)
116 if end and not self.line_breaks:
117 self.fd.write(end)
119 self.fd.flush()
122class ResizableMixin(ProgressBarMixinBase):
124 def __init__(self, term_width: int | None = None, **kwargs):
125 ProgressBarMixinBase.__init__(self, **kwargs)
127 self.signal_set = False
128 if term_width:
129 self.term_width = term_width
130 else: # pragma: no cover
131 try:
132 self._handle_resize()
133 import signal
134 self._prev_handle = signal.getsignal(signal.SIGWINCH)
135 signal.signal(signal.SIGWINCH, self._handle_resize)
136 self.signal_set = True
137 except Exception:
138 pass
140 def _handle_resize(self, signum=None, frame=None):
141 'Tries to catch resize signals sent from the terminal.'
143 w, h = utils.get_terminal_size()
144 self.term_width = w
146 def finish(self): # pragma: no cover
147 ProgressBarMixinBase.finish(self)
148 if self.signal_set:
149 try:
150 import signal
151 signal.signal(signal.SIGWINCH, self._prev_handle)
152 except Exception: # pragma no cover
153 pass
156class StdRedirectMixin(DefaultFdMixin):
158 def __init__(self, redirect_stderr: bool = False,
159 redirect_stdout: bool = False, **kwargs):
160 DefaultFdMixin.__init__(self, **kwargs)
161 self.redirect_stderr = redirect_stderr
162 self.redirect_stdout = redirect_stdout
163 self._stdout = self.stdout = sys.stdout
164 self._stderr = self.stderr = sys.stderr
166 def start(self, *args, **kwargs):
167 if self.redirect_stdout:
168 utils.streams.wrap_stdout()
170 if self.redirect_stderr:
171 utils.streams.wrap_stderr()
173 self._stdout = utils.streams.original_stdout
174 self._stderr = utils.streams.original_stderr
176 self.stdout = utils.streams.stdout
177 self.stderr = utils.streams.stderr
179 utils.streams.start_capturing(self)
180 DefaultFdMixin.start(self, *args, **kwargs)
182 def update(self, value: float = None):
183 if not self.line_breaks and utils.streams.needs_clear():
184 self.fd.write('\r' + ' ' * self.term_width + '\r')
186 utils.streams.flush()
187 DefaultFdMixin.update(self, value=value)
189 def finish(self, end='\n'):
190 DefaultFdMixin.finish(self, end=end)
191 utils.streams.stop_capturing(self)
192 if self.redirect_stdout:
193 utils.streams.unwrap_stdout()
195 if self.redirect_stderr:
196 utils.streams.unwrap_stderr()
199class ProgressBar(StdRedirectMixin, ResizableMixin, ProgressBarBase):
200 '''The ProgressBar class which updates and prints the bar.
202 Args:
203 min_value (int): The minimum/start value for the progress bar
204 max_value (int): The maximum/end value for the progress bar.
205 Defaults to `_DEFAULT_MAXVAL`
206 widgets (list): The widgets to render, defaults to the result of
207 `default_widget()`
208 left_justify (bool): Justify to the left if `True` or the right if
209 `False`
210 initial_value (int): The value to start with
211 poll_interval (float): The update interval in seconds.
212 Note that if your widgets include timers or animations, the actual
213 interval may be smaller (faster updates). Also note that updates
214 never happens faster than `min_poll_interval` which can be used for
215 reduced output in logs
216 min_poll_interval (float): The minimum update interval in seconds.
217 The bar will _not_ be updated faster than this, despite changes in
218 the progress, unless `force=True`. This is limited to be at least
219 `_MINIMUM_UPDATE_INTERVAL`. If available, it is also bound by the
220 environment variable PROGRESSBAR_MINIMUM_UPDATE_INTERVAL
221 widget_kwargs (dict): The default keyword arguments for widgets
222 custom_len (function): Method to override how the line width is
223 calculated. When using non-latin characters the width
224 calculation might be off by default
225 max_error (bool): When True the progressbar will raise an error if it
226 goes beyond it's set max_value. Otherwise the max_value is simply
227 raised when needed
228 prefix (str): Prefix the progressbar with the given string
229 suffix (str): Prefix the progressbar with the given string
230 variables (dict): User-defined variables variables that can be used
231 from a label using `format='{variables.my_var}'`. These values can
232 be updated using `bar.update(my_var='newValue')` This can also be
233 used to set initial values for variables' widgets
235 A common way of using it is like:
237 >>> progress = ProgressBar().start()
238 >>> for i in range(100):
239 ... progress.update(i + 1)
240 ... # do something
241 ...
242 >>> progress.finish()
244 You can also use a ProgressBar as an iterator:
246 >>> progress = ProgressBar()
247 >>> some_iterable = range(100)
248 >>> for i in progress(some_iterable):
249 ... # do something
250 ... pass
251 ...
253 Since the progress bar is incredibly customizable you can specify
254 different widgets of any type in any order. You can even write your own
255 widgets! However, since there are already a good number of widgets you
256 should probably play around with them before moving on to create your own
257 widgets.
259 The term_width parameter represents the current terminal width. If the
260 parameter is set to an integer then the progress bar will use that,
261 otherwise it will attempt to determine the terminal width falling back to
262 80 columns if the width cannot be determined.
264 When implementing a widget's update method you are passed a reference to
265 the current progress bar. As a result, you have access to the
266 ProgressBar's methods and attributes. Although there is nothing preventing
267 you from changing the ProgressBar you should treat it as read only.
269 Useful methods and attributes include (Public API):
270 - value: current progress (min_value <= value <= max_value)
271 - max_value: maximum (and final) value
272 - end_time: not None if the bar has finished (reached 100%)
273 - start_time: the time when start() method of ProgressBar was called
274 - seconds_elapsed: seconds elapsed since start_time and last call to
275 update
276 '''
278 _DEFAULT_MAXVAL = base.UnknownLength
279 # update every 50 milliseconds (up to a 20 times per second)
280 _MINIMUM_UPDATE_INTERVAL = 0.050
282 def __init__(self, min_value=0, max_value=None, widgets=None,
283 left_justify=True, initial_value=0, poll_interval=None,
284 widget_kwargs=None, custom_len=utils.len_color,
285 max_error=True, prefix=None, suffix=None, variables=None,
286 min_poll_interval=None, **kwargs):
287 '''
288 Initializes a progress bar with sane defaults
289 '''
290 StdRedirectMixin.__init__(self, **kwargs)
291 ResizableMixin.__init__(self, **kwargs)
292 ProgressBarBase.__init__(self, **kwargs)
293 if not max_value and kwargs.get('maxval') is not None:
294 warnings.warn('The usage of `maxval` is deprecated, please use '
295 '`max_value` instead', DeprecationWarning)
296 max_value = kwargs.get('maxval')
298 if not poll_interval and kwargs.get('poll'):
299 warnings.warn('The usage of `poll` is deprecated, please use '
300 '`poll_interval` instead', DeprecationWarning)
301 poll_interval = kwargs.get('poll')
303 if max_value:
304 if min_value > max_value:
305 raise ValueError('Max value needs to be bigger than the min '
306 'value')
307 self.min_value = min_value
308 self.max_value = max_value
309 self.max_error = max_error
311 # Only copy the widget if it's safe to copy. Most widgets are so we
312 # assume this to be true
313 if widgets is None:
314 self.widgets = widgets
315 else:
316 self.widgets = []
317 for widget in widgets:
318 if getattr(widget, 'copy', True):
319 widget = deepcopy(widget)
320 self.widgets.append(widget)
322 self.widgets = widgets
323 self.prefix = prefix
324 self.suffix = suffix
325 self.widget_kwargs = widget_kwargs or {}
326 self.left_justify = left_justify
327 self.value = initial_value
328 self._iterable = None
329 self.custom_len = custom_len
330 self.initial_start_time = kwargs.get('start_time')
331 self.init()
333 # Convert a given timedelta to a floating point number as internal
334 # interval. We're not using timedelta's internally for two reasons:
335 # 1. Backwards compatibility (most important one)
336 # 2. Performance. Even though the amount of time it takes to compare a
337 # timedelta with a float versus a float directly is negligible, this
338 # comparison is run for _every_ update. With billions of updates
339 # (downloading a 1GiB file for example) this adds up.
340 poll_interval = utils.deltas_to_seconds(poll_interval, default=None)
341 min_poll_interval = utils.deltas_to_seconds(min_poll_interval,
342 default=None)
343 self._MINIMUM_UPDATE_INTERVAL = utils.deltas_to_seconds(
344 self._MINIMUM_UPDATE_INTERVAL)
346 # Note that the _MINIMUM_UPDATE_INTERVAL sets the minimum in case of
347 # low values.
348 self.poll_interval = poll_interval
349 self.min_poll_interval = max(
350 min_poll_interval or self._MINIMUM_UPDATE_INTERVAL,
351 self._MINIMUM_UPDATE_INTERVAL,
352 float(os.environ.get('PROGRESSBAR_MINIMUM_UPDATE_INTERVAL', 0)),
353 )
355 # A dictionary of names that can be used by Variable and FormatWidget
356 self.variables = utils.AttributeDict(variables or {})
357 for widget in (self.widgets or []):
358 if isinstance(widget, widgets_module.VariableMixin):
359 if widget.name not in self.variables:
360 self.variables[widget.name] = None
362 @property
363 def dynamic_messages(self): # pragma: no cover
364 return self.variables
366 @dynamic_messages.setter
367 def dynamic_messages(self, value): # pragma: no cover
368 self.variables = value
370 def init(self):
371 '''
372 (re)initialize values to original state so the progressbar can be
373 used (again)
374 '''
375 self.previous_value = None
376 self.last_update_time = None
377 self.start_time = None
378 self.updates = 0
379 self.end_time = None
380 self.extra = dict()
381 self._last_update_timer = timeit.default_timer()
383 @property
384 def percentage(self):
385 '''Return current percentage, returns None if no max_value is given
387 >>> progress = ProgressBar()
388 >>> progress.max_value = 10
389 >>> progress.min_value = 0
390 >>> progress.value = 0
391 >>> progress.percentage
392 0.0
393 >>>
394 >>> progress.value = 1
395 >>> progress.percentage
396 10.0
397 >>> progress.value = 10
398 >>> progress.percentage
399 100.0
400 >>> progress.min_value = -10
401 >>> progress.percentage
402 100.0
403 >>> progress.value = 0
404 >>> progress.percentage
405 50.0
406 >>> progress.value = 5
407 >>> progress.percentage
408 75.0
409 >>> progress.value = -5
410 >>> progress.percentage
411 25.0
412 >>> progress.max_value = None
413 >>> progress.percentage
414 '''
415 if self.max_value is None or self.max_value is base.UnknownLength:
416 return None
417 elif self.max_value:
418 todo = self.value - self.min_value
419 total = self.max_value - self.min_value
420 percentage = 100.0 * todo / total
421 else:
422 percentage = 100.0
424 return percentage
426 def get_last_update_time(self):
427 if self._last_update_time:
428 return datetime.fromtimestamp(self._last_update_time)
430 def set_last_update_time(self, value):
431 if value:
432 self._last_update_time = time.mktime(value.timetuple())
433 else:
434 self._last_update_time = None
436 last_update_time = property(get_last_update_time, set_last_update_time)
438 def data(self):
439 '''
441 Returns:
442 dict:
443 - `max_value`: The maximum value (can be None with
444 iterators)
445 - `start_time`: Start time of the widget
446 - `last_update_time`: Last update time of the widget
447 - `end_time`: End time of the widget
448 - `value`: The current value
449 - `previous_value`: The previous value
450 - `updates`: The total update count
451 - `total_seconds_elapsed`: The seconds since the bar started
452 - `seconds_elapsed`: The seconds since the bar started modulo
453 60
454 - `minutes_elapsed`: The minutes since the bar started modulo
455 60
456 - `hours_elapsed`: The hours since the bar started modulo 24
457 - `days_elapsed`: The hours since the bar started
458 - `time_elapsed`: The raw elapsed `datetime.timedelta` object
459 - `percentage`: Percentage as a float or `None` if no max_value
460 is available
461 - `dynamic_messages`: Deprecated, use `variables` instead.
462 - `variables`: Dictionary of user-defined variables for the
463 :py:class:`~progressbar.widgets.Variable`'s
465 '''
466 self._last_update_time = time.time()
467 self._last_update_timer = timeit.default_timer()
468 elapsed = self.last_update_time - self.start_time
469 # For Python 2.7 and higher we have _`timedelta.total_seconds`, but we
470 # want to support older versions as well
471 total_seconds_elapsed = utils.deltas_to_seconds(elapsed)
472 return dict(
473 # The maximum value (can be None with iterators)
474 max_value=self.max_value,
475 # Start time of the widget
476 start_time=self.start_time,
477 # Last update time of the widget
478 last_update_time=self.last_update_time,
479 # End time of the widget
480 end_time=self.end_time,
481 # The current value
482 value=self.value,
483 # The previous value
484 previous_value=self.previous_value,
485 # The total update count
486 updates=self.updates,
487 # The seconds since the bar started
488 total_seconds_elapsed=total_seconds_elapsed,
489 # The seconds since the bar started modulo 60
490 seconds_elapsed=(elapsed.seconds % 60)
491 + (elapsed.microseconds / 1000000.),
492 # The minutes since the bar started modulo 60
493 minutes_elapsed=(elapsed.seconds / 60) % 60,
494 # The hours since the bar started modulo 24
495 hours_elapsed=(elapsed.seconds / (60 * 60)) % 24,
496 # The hours since the bar started
497 days_elapsed=(elapsed.seconds / (60 * 60 * 24)),
498 # The raw elapsed `datetime.timedelta` object
499 time_elapsed=elapsed,
500 # Percentage as a float or `None` if no max_value is available
501 percentage=self.percentage,
502 # Dictionary of user-defined
503 # :py:class:`progressbar.widgets.Variable`'s
504 variables=self.variables,
505 # Deprecated alias for `variables`
506 dynamic_messages=self.variables,
507 )
509 def default_widgets(self):
510 if self.max_value:
511 return [
512 widgets.Percentage(**self.widget_kwargs),
513 ' ', widgets.SimpleProgress(
514 format='(%s)' % widgets.SimpleProgress.DEFAULT_FORMAT,
515 **self.widget_kwargs),
516 ' ', widgets.Bar(**self.widget_kwargs),
517 ' ', widgets.Timer(**self.widget_kwargs),
518 ' ', widgets.AdaptiveETA(**self.widget_kwargs),
519 ]
520 else:
521 return [
522 widgets.AnimatedMarker(**self.widget_kwargs),
523 ' ', widgets.BouncingBar(**self.widget_kwargs),
524 ' ', widgets.Counter(**self.widget_kwargs),
525 ' ', widgets.Timer(**self.widget_kwargs),
526 ]
528 def __call__(self, iterable, max_value=None):
529 'Use a ProgressBar to iterate through an iterable'
530 if max_value is not None:
531 self.max_value = max_value
532 elif self.max_value is None:
533 try:
534 self.max_value = len(iterable)
535 except TypeError: # pragma: no cover
536 self.max_value = base.UnknownLength
538 self._iterable = iter(iterable)
539 return self
541 def __iter__(self):
542 return self
544 def __next__(self):
545 try:
546 value = next(self._iterable)
547 if self.start_time is None:
548 self.start()
549 else:
550 self.update(self.value + 1)
551 return value
552 except StopIteration:
553 self.finish()
554 raise
555 except GeneratorExit: # pragma: no cover
556 self.finish(dirty=True)
557 raise
559 def __exit__(self, exc_type, exc_value, traceback):
560 self.finish(dirty=bool(exc_type))
562 def __enter__(self):
563 return self
565 # Create an alias so that Python 2.x won't complain about not being
566 # an iterator.
567 next = __next__
569 def __iadd__(self, value):
570 'Updates the ProgressBar by adding a new value.'
571 self.update(self.value + value)
572 return self
574 def _format_widgets(self):
575 result = []
576 expanding = []
577 width = self.term_width
578 data = self.data()
580 for index, widget in enumerate(self.widgets):
581 if isinstance(widget, widgets.WidgetBase) \
582 and not widget.check_size(self):
583 continue
584 elif isinstance(widget, widgets.AutoWidthWidgetBase):
585 result.append(widget)
586 expanding.insert(0, index)
587 elif isinstance(widget, str):
588 result.append(widget)
589 width -= self.custom_len(widget)
590 else:
591 widget_output = converters.to_unicode(widget(self, data))
592 result.append(widget_output)
593 width -= self.custom_len(widget_output)
595 count = len(expanding)
596 while expanding:
597 portion = max(int(math.ceil(width * 1. / count)), 0)
598 index = expanding.pop()
599 widget = result[index]
600 count -= 1
602 widget_output = widget(self, data, portion)
603 width -= self.custom_len(widget_output)
604 result[index] = widget_output
606 return result
608 @classmethod
609 def _to_unicode(cls, args):
610 for arg in args:
611 yield converters.to_unicode(arg)
613 def _format_line(self):
614 'Joins the widgets and justifies the line'
616 widgets = ''.join(self._to_unicode(self._format_widgets()))
618 if self.left_justify:
619 return widgets.ljust(self.term_width)
620 else:
621 return widgets.rjust(self.term_width)
623 def _needs_update(self):
624 'Returns whether the ProgressBar should redraw the line.'
625 delta = timeit.default_timer() - self._last_update_timer
626 if delta < self.min_poll_interval:
627 # Prevent updating too often
628 return False
629 elif self.poll_interval and delta > self.poll_interval:
630 # Needs to redraw timers and animations
631 return True
633 # Update if value increment is not large enough to
634 # add more bars to progressbar (according to current
635 # terminal width)
636 try:
637 divisor = self.max_value / self.term_width # float division
638 if self.value // divisor != self.previous_value // divisor:
639 return True
640 except Exception:
641 # ignore any division errors
642 pass
644 # No need to redraw yet
645 return False
647 def update(self, value=None, force=False, **kwargs):
648 'Updates the ProgressBar to a new value.'
649 if self.start_time is None:
650 self.start()
651 return self.update(value, force=force, **kwargs)
653 if value is not None and value is not base.UnknownLength:
654 if self.max_value is base.UnknownLength:
655 # Can't compare against unknown lengths so just update
656 pass
657 elif self.min_value <= value <= self.max_value: # pragma: no cover
658 # Correct value, let's accept
659 pass
660 elif self.max_error:
661 raise ValueError(
662 'Value %s is out of range, should be between %s and %s'
663 % (value, self.min_value, self.max_value))
664 else:
665 self.max_value = value
667 self.previous_value = self.value
668 self.value = value
670 # Save the updated values for dynamic messages
671 variables_changed = False
672 for key in kwargs:
673 if key not in self.variables:
674 raise TypeError(
675 'update() got an unexpected keyword ' +
676 'argument {0!r}'.format(key))
677 elif self.variables[key] != kwargs[key]:
678 self.variables[key] = kwargs[key]
679 variables_changed = True
681 if self._needs_update() or variables_changed or force:
682 self.updates += 1
683 ResizableMixin.update(self, value=value)
684 ProgressBarBase.update(self, value=value)
685 StdRedirectMixin.update(self, value=value)
687 # Only flush if something was actually written
688 self.fd.flush()
690 def start(self, max_value=None, init=True):
691 '''Starts measuring time, and prints the bar at 0%.
693 It returns self so you can use it like this:
695 Args:
696 max_value (int): The maximum value of the progressbar
697 reinit (bool): Initialize the progressbar, this is useful if you
698 wish to reuse the same progressbar but can be disabled if
699 data needs to be passed along to the next run
701 >>> pbar = ProgressBar().start()
702 >>> for i in range(100):
703 ... # do something
704 ... pbar.update(i+1)
705 ...
706 >>> pbar.finish()
707 '''
708 if init:
709 self.init()
711 # Prevent multiple starts
712 if self.start_time is not None: # pragma: no cover
713 return self
715 if max_value is not None:
716 self.max_value = max_value
718 if self.max_value is None:
719 self.max_value = self._DEFAULT_MAXVAL
721 StdRedirectMixin.start(self, max_value=max_value)
722 ResizableMixin.start(self, max_value=max_value)
723 ProgressBarBase.start(self, max_value=max_value)
725 # Constructing the default widgets is only done when we know max_value
726 if self.widgets is None:
727 self.widgets = self.default_widgets()
729 if self.prefix:
730 self.widgets.insert(0, widgets.FormatLabel(
731 self.prefix, new_style=True))
732 # Unset the prefix variable after applying so an extra start()
733 # won't keep copying it
734 self.prefix = None
736 if self.suffix:
737 self.widgets.append(widgets.FormatLabel(
738 self.suffix, new_style=True))
739 # Unset the suffix variable after applying so an extra start()
740 # won't keep copying it
741 self.suffix = None
743 for widget in self.widgets:
744 interval = getattr(widget, 'INTERVAL', None)
745 if interval is not None:
746 interval = utils.deltas_to_seconds(interval)
748 self.poll_interval = min(
749 self.poll_interval or interval,
750 interval,
751 )
753 self.num_intervals = max(100, self.term_width)
754 # The `next_update` is kept for compatibility with external libs:
755 # https://github.com/WoLpH/python-progressbar/issues/207
756 self.next_update = 0
758 if self.max_value is not base.UnknownLength and self.max_value < 0:
759 raise ValueError('max_value out of range, got %r' % self.max_value)
761 now = datetime.now()
762 self.start_time = self.initial_start_time or now
763 self.last_update_time = now
764 self._last_update_timer = timeit.default_timer()
765 self.update(self.min_value, force=True)
767 return self
769 def finish(self, end='\n', dirty=False):
770 '''
771 Puts the ProgressBar bar in the finished state.
773 Also flushes and disables output buffering if this was the last
774 progressbar running.
776 Args:
777 end (str): The string to end the progressbar with, defaults to a
778 newline
779 dirty (bool): When True the progressbar kept the current state and
780 won't be set to 100 percent
781 '''
783 if not dirty:
784 self.end_time = datetime.now()
785 self.update(self.max_value, force=True)
787 StdRedirectMixin.finish(self, end=end)
788 ResizableMixin.finish(self)
789 ProgressBarBase.finish(self)
792class DataTransferBar(ProgressBar):
793 '''A progress bar with sensible defaults for downloads etc.
795 This assumes that the values its given are numbers of bytes.
796 '''
798 def default_widgets(self):
799 if self.max_value:
800 return [
801 widgets.Percentage(),
802 ' of ', widgets.DataSize('max_value'),
803 ' ', widgets.Bar(),
804 ' ', widgets.Timer(),
805 ' ', widgets.AdaptiveETA(),
806 ]
807 else:
808 return [
809 widgets.AnimatedMarker(),
810 ' ', widgets.DataSize(),
811 ' ', widgets.Timer(),
812 ]
815class NullBar(ProgressBar):
816 '''
817 Progress bar that does absolutely nothing. Useful for single verbosity
818 flags
819 '''
821 def start(self, *args, **kwargs):
822 return self
824 def update(self, *args, **kwargs):
825 return self
827 def finish(self, *args, **kwargs):
828 return self