Skip to content

Commit f19bb2e

Browse files
committed
reformatted and added more type hinting
1 parent 877fb7f commit f19bb2e

1 file changed

Lines changed: 120 additions & 69 deletions

File tree

progressbar/bar.py

Lines changed: 120 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,40 @@
11
from __future__ import annotations
22

33
import logging
4-
import math
54
import os
65
import sys
76
import time
87
import timeit
98
import warnings
9+
from abc import ABC
1010
from copy import deepcopy
1111
from 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

2723
logger = logging.getLogger(__name__)
2824

29-
3025
T = types.TypeVar('T')
3126

3227

3328
class 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

5964
class 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

125151
class ResizableMixin(ProgressBarMixinBase):
126152

@@ -157,9 +183,17 @@ def finish(self): # pragma: no cover
157183

158184

159185
class 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

Comments
 (0)