11from __future__ import annotations
22
33import logging
4- import math
54import os
65import sys
76import time
87import timeit
98import warnings
9+ from abc import ABC
1010from copy import deepcopy
1111from datetime import datetime
1212
13- from python_utils import types
14-
15- try : # pragma: no cover
16- from collections import abc
17- except ImportError : # pragma: no cover
18- import collections as abc
19-
20- from python_utils import converters
13+ import math
14+ from python_utils import converters , types
2115
22- from . import widgets
23- from . import widgets as widgets_module # Avoid name collision
24- from . import base
25- from . import utils
16+ from . import (
17+ base ,
18+ utils ,
19+ widgets ,
20+ widgets as widgets_module , # Avoid name collision
21+ )
2622
2723logger = logging .getLogger (__name__ )
2824
29-
3025T = types .TypeVar ('T' )
3126
3227
3328class ProgressBarMixinBase (object ):
29+ _started = False
30+ _finished = False
31+ term_width : int = 80
3432
3533 def __init__ (self , ** kwargs ):
36- self . _finished = False
34+ pass
3735
3836 def start (self , ** kwargs ):
39- pass
37+ self . _started = True
4038
4139 def update (self , value = None ):
4240 pass
@@ -45,23 +43,36 @@ def finish(self): # pragma: no cover
4543 self ._finished = True
4644
4745 def __del__ (self ):
48- if not self ._finished : # pragma: no cover
46+ if not self ._finished and self . _started : # pragma: no cover
4947 try :
5048 self .finish ()
5149 except Exception :
52- pass
50+ # Never raise during cleanup. We're too late now
51+ logging .debug (
52+ 'Exception raised during ProgressBar cleanup' ,
53+ exc_info = True
54+ )
5355
56+ def __getstate__ (self ):
57+ return self .__dict__
5458
55- class ProgressBarBase (abc .Iterable , ProgressBarMixinBase ):
59+
60+ class ProgressBarBase (types .Iterable , ProgressBarMixinBase , ABC ):
5661 pass
5762
5863
5964class DefaultFdMixin (ProgressBarMixinBase ):
60-
61- def __init__ (self , fd : types .IO = sys .stderr ,
62- is_terminal : bool | None = None ,
63- line_breaks : bool | None = None ,
64- enable_colors : bool | None = None , ** kwargs ):
65+ fd : types .IO = sys .stderr
66+ is_ansi_terminal : bool = False
67+ line_breaks : bool = True
68+ enable_colors : bool = False
69+
70+ def __init__ (
71+ self , fd : types .IO = sys .stderr ,
72+ is_terminal : bool | None = None ,
73+ line_breaks : bool | None = None ,
74+ enable_colors : bool | None = None , ** kwargs
75+ ):
6576 if fd is sys .stdout :
6677 fd = utils .streams .original_stdout
6778
@@ -73,22 +84,27 @@ def __init__(self, fd: types.IO = sys.stderr,
7384
7485 # Check if this is an interactive terminal
7586 self .is_terminal = utils .is_terminal (
76- fd , is_terminal or self .is_ansi_terminal )
87+ fd , is_terminal or self .is_ansi_terminal
88+ )
7789
7890 # Check if it should overwrite the current line (suitable for
7991 # iteractive terminals) or write line breaks (suitable for log files)
8092 if line_breaks is None :
81- line_breaks = utils .env_flag ('PROGRESSBAR_LINE_BREAKS' ,
82- not self .is_terminal )
83- self .line_breaks = line_breaks
93+ line_breaks = utils .env_flag (
94+ 'PROGRESSBAR_LINE_BREAKS' ,
95+ not self .is_terminal
96+ )
97+ self .line_breaks = bool (line_breaks )
8498
8599 # Check if ANSI escape characters are enabled (suitable for iteractive
86100 # terminals), or should be stripped off (suitable for log files)
87101 if enable_colors is None :
88- enable_colors = utils .env_flag ('PROGRESSBAR_ENABLE_COLORS' ,
89- self .is_ansi_terminal )
102+ enable_colors = utils .env_flag (
103+ 'PROGRESSBAR_ENABLE_COLORS' ,
104+ self .is_ansi_terminal
105+ )
90106
91- self .enable_colors = enable_colors
107+ self .enable_colors = bool ( enable_colors )
92108
93109 ProgressBarMixinBase .__init__ (self , ** kwargs )
94110
@@ -121,6 +137,16 @@ def finish(self, *args, **kwargs): # pragma: no cover
121137
122138 self .fd .flush ()
123139
140+ def _format_line (self ):
141+ 'Joins the widgets and justifies the line'
142+
143+ widgets = '' .join (self ._to_unicode (self ._format_widgets ()))
144+
145+ if self .left_justify :
146+ return widgets .ljust (self .term_width )
147+ else :
148+ return widgets .rjust (self .term_width )
149+
124150
125151class ResizableMixin (ProgressBarMixinBase ):
126152
@@ -157,9 +183,17 @@ def finish(self): # pragma: no cover
157183
158184
159185class StdRedirectMixin (DefaultFdMixin ):
160-
161- def __init__ (self , redirect_stderr : bool = False ,
162- redirect_stdout : bool = False , ** kwargs ):
186+ redirect_stderr : bool = False
187+ redirect_stdout : bool = False
188+ stdout : types .IO
189+ stderr : types .IO
190+ _stdout : types .IO
191+ _stderr : types .IO
192+
193+ def __init__ (
194+ self , redirect_stderr : bool = False ,
195+ redirect_stdout : bool = False , ** kwargs
196+ ):
163197 DefaultFdMixin .__init__ (self , ** kwargs )
164198 self .redirect_stderr = redirect_stderr
165199 self .redirect_stdout = redirect_stdout
@@ -199,7 +233,12 @@ def finish(self, end='\n'):
199233 utils .streams .unwrap_stderr ()
200234
201235
202- class ProgressBar (StdRedirectMixin , ResizableMixin , ProgressBarBase ):
236+ class ProgressBar (
237+ StdRedirectMixin ,
238+ ResizableMixin ,
239+ ProgressBarBase ,
240+ types .Generic [T ],
241+ ):
203242 '''The ProgressBar class which updates and prints the bar.
204243
205244 Args:
@@ -287,31 +326,39 @@ class ProgressBar(StdRedirectMixin, ResizableMixin, ProgressBarBase):
287326 # update every 50 milliseconds (up to a 20 times per second)
288327 _MINIMUM_UPDATE_INTERVAL = 0.050
289328
290- def __init__ (self , min_value = 0 , max_value = None , widgets = None ,
291- left_justify = True , initial_value = 0 , poll_interval = None ,
292- widget_kwargs = None , custom_len = utils .len_color ,
293- max_error = True , prefix = None , suffix = None , variables = None ,
294- min_poll_interval = None , ** kwargs ):
329+ def __init__ (
330+ self , min_value = 0 , max_value = None , widgets = None ,
331+ left_justify = True , initial_value = 0 , poll_interval = None ,
332+ widget_kwargs = None , custom_len = utils .len_color ,
333+ max_error = True , prefix = None , suffix = None , variables = None ,
334+ min_poll_interval = None , ** kwargs
335+ ):
295336 '''
296337 Initializes a progress bar with sane defaults
297338 '''
298339 StdRedirectMixin .__init__ (self , ** kwargs )
299340 ResizableMixin .__init__ (self , ** kwargs )
300341 ProgressBarBase .__init__ (self , ** kwargs )
301342 if not max_value and kwargs .get ('maxval' ) is not None :
302- warnings .warn ('The usage of `maxval` is deprecated, please use '
303- '`max_value` instead' , DeprecationWarning )
343+ warnings .warn (
344+ 'The usage of `maxval` is deprecated, please use '
345+ '`max_value` instead' , DeprecationWarning
346+ )
304347 max_value = kwargs .get ('maxval' )
305348
306349 if not poll_interval and kwargs .get ('poll' ):
307- warnings .warn ('The usage of `poll` is deprecated, please use '
308- '`poll_interval` instead' , DeprecationWarning )
350+ warnings .warn (
351+ 'The usage of `poll` is deprecated, please use '
352+ '`poll_interval` instead' , DeprecationWarning
353+ )
309354 poll_interval = kwargs .get ('poll' )
310355
311356 if max_value :
312357 if min_value > max_value :
313- raise ValueError ('Max value needs to be bigger than the min '
314- 'value' )
358+ raise ValueError (
359+ 'Max value needs to be bigger than the min '
360+ 'value'
361+ )
315362 self .min_value = min_value
316363 self .max_value = max_value
317364 self .max_error = max_error
@@ -346,10 +393,13 @@ def __init__(self, min_value=0, max_value=None, widgets=None,
346393 # comparison is run for _every_ update. With billions of updates
347394 # (downloading a 1GiB file for example) this adds up.
348395 poll_interval = utils .deltas_to_seconds (poll_interval , default = None )
349- min_poll_interval = utils .deltas_to_seconds (min_poll_interval ,
350- default = None )
396+ min_poll_interval = utils .deltas_to_seconds (
397+ min_poll_interval ,
398+ default = None
399+ )
351400 self ._MINIMUM_UPDATE_INTERVAL = utils .deltas_to_seconds (
352- self ._MINIMUM_UPDATE_INTERVAL )
401+ self ._MINIMUM_UPDATE_INTERVAL
402+ )
353403
354404 # Note that the _MINIMUM_UPDATE_INTERVAL sets the minimum in case of
355405 # low values.
@@ -520,7 +570,8 @@ def default_widgets(self):
520570 widgets .Percentage (** self .widget_kwargs ),
521571 ' ' , widgets .SimpleProgress (
522572 format = '(%s)' % widgets .SimpleProgress .DEFAULT_FORMAT ,
523- ** self .widget_kwargs ),
573+ ** self .widget_kwargs
574+ ),
524575 ' ' , widgets .Bar (** self .widget_kwargs ),
525576 ' ' , widgets .Timer (** self .widget_kwargs ),
526577 ' ' , widgets .AdaptiveETA (** self .widget_kwargs ),
@@ -590,7 +641,7 @@ def _format_widgets(self):
590641
591642 for index , widget in enumerate (self .widgets ):
592643 if isinstance (widget , widgets .WidgetBase ) \
593- and not widget .check_size (self ):
644+ and not widget .check_size (self ):
594645 continue
595646 elif isinstance (widget , widgets .AutoWidthWidgetBase ):
596647 result .append (widget )
@@ -621,16 +672,6 @@ def _to_unicode(cls, args):
621672 for arg in args :
622673 yield converters .to_unicode (arg )
623674
624- def _format_line (self ):
625- 'Joins the widgets and justifies the line'
626-
627- widgets = '' .join (self ._to_unicode (self ._format_widgets ()))
628-
629- if self .left_justify :
630- return widgets .ljust (self .term_width )
631- else :
632- return widgets .rjust (self .term_width )
633-
634675 def _needs_update (self ):
635676 'Returns whether the ProgressBar should redraw the line.'
636677 delta = timeit .default_timer () - self ._last_update_timer
@@ -671,7 +712,8 @@ def update(self, value=None, force=False, **kwargs):
671712 elif self .max_error :
672713 raise ValueError (
673714 'Value %s is out of range, should be between %s and %s'
674- % (value , self .min_value , self .max_value ))
715+ % (value , self .min_value , self .max_value )
716+ )
675717 else :
676718 self .max_value = value
677719
@@ -684,7 +726,8 @@ def update(self, value=None, force=False, **kwargs):
684726 if key not in self .variables :
685727 raise TypeError (
686728 'update() got an unexpected keyword ' +
687- 'argument {0!r}' .format (key ))
729+ 'argument {0!r}' .format (key )
730+ )
688731 elif self .variables [key ] != kwargs [key ]:
689732 self .variables [key ] = kwargs [key ]
690733 variables_changed = True
@@ -738,15 +781,21 @@ def start(self, max_value=None, init=True):
738781 self .widgets = self .default_widgets ()
739782
740783 if self .prefix :
741- self .widgets .insert (0 , widgets .FormatLabel (
742- self .prefix , new_style = True ))
784+ self .widgets .insert (
785+ 0 , widgets .FormatLabel (
786+ self .prefix , new_style = True
787+ )
788+ )
743789 # Unset the prefix variable after applying so an extra start()
744790 # won't keep copying it
745791 self .prefix = None
746792
747793 if self .suffix :
748- self .widgets .append (widgets .FormatLabel (
749- self .suffix , new_style = True ))
794+ self .widgets .append (
795+ widgets .FormatLabel (
796+ self .suffix , new_style = True
797+ )
798+ )
750799 # Unset the suffix variable after applying so an extra start()
751800 # won't keep copying it
752801 self .suffix = None
@@ -805,8 +854,10 @@ def currval(self):
805854 Legacy method to make progressbar-2 compatible with the original
806855 progressbar package
807856 '''
808- warnings .warn ('The usage of `currval` is deprecated, please use '
809- '`value` instead' , DeprecationWarning )
857+ warnings .warn (
858+ 'The usage of `currval` is deprecated, please use '
859+ '`value` instead' , DeprecationWarning
860+ )
810861 return self .value
811862
812863
0 commit comments