diff --git a/docs/conf.py b/docs/conf.py index c4ed327..a769e40 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,3 +1,5 @@ +from __future__ import annotations + # # Progress Bar documentation build configuration file, created by # sphinx-quickstart on Tue Aug 20 11:47:33 2013. @@ -9,7 +11,6 @@ # # All configuration values have a default; values that are commented out # serve to show the default. - import datetime import os import sys @@ -59,7 +60,7 @@ # General information about the project. project = 'Progress Bar' -project_slug = ''.join(project.capitalize().split()) +project_slug: str = ''.join(project.capitalize().split()) copyright = f'{datetime.date.today().year}, {metadata.__author__}' # The version info for the project you're documenting, acts as replacement for @@ -67,9 +68,9 @@ # built documents. # # The short X.Y version. -version = metadata.__version__ +version: str = metadata.__version__ # The full version, including alpha/beta/rc tags. -release = metadata.__version__ +release: str = metadata.__version__ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -202,7 +203,7 @@ # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). -latex_documents = [ +latex_documents: list[tuple[str, ...]] = [ ( 'index', f'{project_slug}.tex', @@ -237,7 +238,7 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [ +man_pages: list[tuple[str, str, str, list[str], int]] = [ ( 'index', project_slug.lower(), @@ -256,7 +257,7 @@ # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) -texinfo_documents = [ +texinfo_documents: list[tuple[str, ...]] = [ ( 'index', project_slug, @@ -284,10 +285,10 @@ # -- Options for Epub output --------------------------------------------- # Bibliographic Dublin Core info. -epub_title = project -epub_author = metadata.__author__ -epub_publisher = metadata.__author__ -epub_copyright = copyright +epub_title: str = project +epub_author: str = metadata.__author__ +epub_publisher: str = metadata.__author__ +epub_copyright: str = copyright # The language of the text. It defaults to the language option # or en if the language is not set. @@ -340,4 +341,6 @@ # Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = {'python': ('https://docs.python.org/3', None)} +intersphinx_mapping: dict[str, tuple[str, None]] = { + 'python': ('https://docs.python.org/3', None) +} diff --git a/docs/progressbar.terminal.os_specific.rst b/docs/progressbar.terminal.os_specific.rst deleted file mode 100644 index b00648e..0000000 --- a/docs/progressbar.terminal.os_specific.rst +++ /dev/null @@ -1,16 +0,0 @@ -progressbar.terminal.os\_specific package -========================================= - -Submodules ----------- - -.. toctree:: - :maxdepth: 4 - -Module contents ---------------- - -.. automodule:: progressbar.terminal.os_specific - :members: - :undoc-members: - :show-inheritance: diff --git a/examples.py b/examples.py index bf265d1..aa71179 100644 --- a/examples.py +++ b/examples.py @@ -15,7 +15,7 @@ def example(fn): - '''Wrap the examples so they generate readable output''' + """Wrap the examples so they generate readable output""" @functools.wraps(fn) def wrapped(*args, **kwargs): @@ -33,30 +33,32 @@ def wrapped(*args, **kwargs): @example -def fast_example(): - '''Updates bar really quickly to cause flickering''' +def fast_example() -> None: + """Updates bar really quickly to cause flickering""" with progressbar.ProgressBar(widgets=[progressbar.Bar()]) as bar: for i in range(100): bar.update(int(i / 10), force=True) @example -def shortcut_example(): +def shortcut_example() -> None: for _ in progressbar.progressbar(range(10)): time.sleep(0.1) @example -def prefixed_shortcut_example(): +def prefixed_shortcut_example() -> None: for _ in progressbar.progressbar(range(10), prefix='Hi: '): time.sleep(0.1) @example -def parallel_bars_multibar_example(): +def parallel_bars_multibar_example() -> None: if os.name == 'nt': - print('Skipping multibar example on Windows due to threading ' - 'incompatibilities with the example code.') + print( + 'Skipping multibar example on Windows due to threading ' + 'incompatibilities with the example code.' + ) return BARS = 5 @@ -85,7 +87,7 @@ def do_something(bar): @example -def multiple_bars_line_offset_example(): +def multiple_bars_line_offset_example() -> None: BARS = 5 N = 100 @@ -117,16 +119,16 @@ def multiple_bars_line_offset_example(): @example -def templated_shortcut_example(): +def templated_shortcut_example() -> None: for _ in progressbar.progressbar(range(10), suffix='{seconds_elapsed:.1}'): time.sleep(0.1) @example -def job_status_example(): +def job_status_example() -> None: with progressbar.ProgressBar( - redirect_stdout=True, - widgets=[progressbar.widgets.JobStatusBar('status')], + redirect_stdout=True, + widgets=[progressbar.widgets.JobStatusBar('status')], ) as bar: for _ in range(30): print('random', random.random()) @@ -142,7 +144,7 @@ def job_status_example(): @example -def with_example_stdout_redirection(): +def with_example_stdout_redirection() -> None: with progressbar.ProgressBar(max_value=10, redirect_stdout=True) as p: for i in range(10): if i % 3 == 0: @@ -153,7 +155,7 @@ def with_example_stdout_redirection(): @example -def basic_widget_example(): +def basic_widget_example() -> None: widgets = [progressbar.Percentage(), progressbar.Bar()] bar = progressbar.ProgressBar(widgets=widgets, max_value=10).start() for i in range(10): @@ -164,7 +166,7 @@ def basic_widget_example(): @example -def color_bar_example(): +def color_bar_example() -> None: widgets = [ '\x1b[33mColorful example\x1b[39m', progressbar.Percentage(), @@ -179,7 +181,7 @@ def color_bar_example(): @example -def color_bar_animated_marker_example(): +def color_bar_animated_marker_example() -> None: widgets = [ # Colored animated marker with colored fill: progressbar.Bar( @@ -200,7 +202,7 @@ def color_bar_animated_marker_example(): @example -def multi_range_bar_example(): +def multi_range_bar_example() -> None: markers = [ '\x1b[32m█\x1b[39m', # Done '\x1b[33m#\x1b[39m', # Processing @@ -229,7 +231,7 @@ def multi_range_bar_example(): @example -def multi_progress_bar_example(left=True): +def multi_progress_bar_example(left: bool = True) -> None: jobs = [ # Each job takes between 1 and 10 steps to complete [0, random.randint(1, 10)] @@ -261,7 +263,7 @@ def multi_progress_bar_example(left=True): @example -def granular_progress_example(): +def granular_progress_example() -> None: widgets = [ progressbar.GranularBar(markers=' ▏▎▍▌▋▊▉█', left='', right='|'), progressbar.GranularBar(markers=' ▁▂▃▄▅▆▇█', left='', right='|'), @@ -278,7 +280,7 @@ def granular_progress_example(): @example -def percentage_label_bar_example(): +def percentage_label_bar_example() -> None: widgets = [progressbar.PercentageLabelBar()] bar = progressbar.ProgressBar(widgets=widgets, max_value=10).start() for i in range(10): @@ -289,7 +291,7 @@ def percentage_label_bar_example(): @example -def file_transfer_example(): +def file_transfer_example() -> None: widgets = [ 'Test: ', progressbar.Percentage(), @@ -309,11 +311,11 @@ def file_transfer_example(): @example -def custom_file_transfer_example(): +def custom_file_transfer_example() -> None: class CrazyFileTransferSpeed(progressbar.FileTransferSpeed): - ''' + """ It's bigger between 45 and 80 percent - ''' + """ def update(self, bar): if 45 < bar.percentage() < 80: @@ -343,7 +345,7 @@ def update(self, bar): @example -def double_bar_example(): +def double_bar_example() -> None: widgets = [ progressbar.Bar('>'), ' ', @@ -360,7 +362,7 @@ def double_bar_example(): @example -def basic_file_transfer(): +def basic_file_transfer() -> None: widgets = [ 'Test: ', progressbar.Percentage(), @@ -381,7 +383,7 @@ def basic_file_transfer(): @example -def simple_progress(): +def simple_progress() -> None: bar = progressbar.ProgressBar( widgets=[progressbar.SimpleProgress()], max_value=17, @@ -393,7 +395,7 @@ def simple_progress(): @example -def basic_progress(): +def basic_progress() -> None: bar = progressbar.ProgressBar().start() for i in range(10): time.sleep(0.1) @@ -402,7 +404,7 @@ def basic_progress(): @example -def progress_with_automatic_max(): +def progress_with_automatic_max() -> None: # Progressbar can guess max_value automatically. bar = progressbar.ProgressBar() for _ in bar(range(8)): @@ -410,7 +412,7 @@ def progress_with_automatic_max(): @example -def progress_with_unavailable_max(): +def progress_with_unavailable_max() -> None: # Progressbar can't guess max_value. bar = progressbar.ProgressBar(max_value=8) for _ in bar(i for i in range(8)): @@ -418,7 +420,7 @@ def progress_with_unavailable_max(): @example -def animated_marker(): +def animated_marker() -> None: bar = progressbar.ProgressBar( widgets=['Working: ', progressbar.AnimatedMarker()] ) @@ -427,7 +429,7 @@ def animated_marker(): @example -def filling_bar_animated_marker(): +def filling_bar_animated_marker() -> None: bar = progressbar.ProgressBar( widgets=[ progressbar.Bar( @@ -440,7 +442,7 @@ def filling_bar_animated_marker(): @example -def counter_and_timer(): +def counter_and_timer() -> None: widgets = [ 'Processed: ', progressbar.Counter('Counter: %(value)05d'), @@ -454,7 +456,7 @@ def counter_and_timer(): @example -def format_label(): +def format_label() -> None: widgets = [ progressbar.FormatLabel('Processed: %(value)d lines (in: %(elapsed)s)') ] @@ -464,7 +466,7 @@ def format_label(): @example -def animated_balloons(): +def animated_balloons() -> None: widgets = ['Balloon: ', progressbar.AnimatedMarker(markers='.oO@* ')] bar = progressbar.ProgressBar(widgets=widgets) for _ in bar(i for i in range(24)): @@ -472,7 +474,7 @@ def animated_balloons(): @example -def animated_arrows(): +def animated_arrows() -> None: # You may need python 3.x to see this correctly try: widgets = ['Arrows: ', progressbar.AnimatedMarker(markers='←↖↑↗→↘↓↙')] @@ -484,7 +486,7 @@ def animated_arrows(): @example -def animated_filled_arrows(): +def animated_filled_arrows() -> None: # You may need python 3.x to see this correctly try: widgets = ['Arrows: ', progressbar.AnimatedMarker(markers='◢◣◤◥')] @@ -496,7 +498,7 @@ def animated_filled_arrows(): @example -def animated_wheels(): +def animated_wheels() -> None: # You may need python 3.x to see this correctly try: widgets = ['Wheels: ', progressbar.AnimatedMarker(markers='◐◓◑◒')] @@ -508,7 +510,7 @@ def animated_wheels(): @example -def format_label_bouncer(): +def format_label_bouncer() -> None: widgets = [ progressbar.FormatLabel('Bouncer: value %(value)d - '), progressbar.BouncingBar(), @@ -519,7 +521,7 @@ def format_label_bouncer(): @example -def format_label_rotating_bouncer(): +def format_label_rotating_bouncer() -> None: widgets = [ progressbar.FormatLabel('Animated Bouncer: value %(value)d - '), progressbar.BouncingBar(marker=progressbar.RotatingMarker()), @@ -531,9 +533,9 @@ def format_label_rotating_bouncer(): @example -def with_right_justify(): +def with_right_justify() -> None: with progressbar.ProgressBar( - max_value=10, term_width=20, left_justify=False + max_value=10, term_width=20, left_justify=False ) as progress: assert progress.term_width is not None for i in range(10): @@ -541,38 +543,39 @@ def with_right_justify(): @example -def exceeding_maximum(): +def exceeding_maximum() -> None: with progressbar.ProgressBar(max_value=1) as progress, contextlib.suppress( - ValueError): + ValueError + ): progress.update(2) @example -def reaching_maximum(): +def reaching_maximum() -> None: progress = progressbar.ProgressBar(max_value=1) with contextlib.suppress(RuntimeError): progress.update(1) @example -def stdout_redirection(): +def stdout_redirection() -> None: with progressbar.ProgressBar(redirect_stdout=True) as progress: print('', file=sys.stdout) progress.update(0) @example -def stderr_redirection(): +def stderr_redirection() -> None: with progressbar.ProgressBar(redirect_stderr=True) as progress: print('', file=sys.stderr) progress.update(0) @example -def rotating_bouncing_marker(): +def rotating_bouncing_marker() -> None: widgets = [progressbar.BouncingBar(marker=progressbar.RotatingMarker())] with progressbar.ProgressBar( - widgets=widgets, max_value=20, term_width=10 + widgets=widgets, max_value=20, term_width=10 ) as progress: for i in range(20): time.sleep(0.1) @@ -584,7 +587,7 @@ def rotating_bouncing_marker(): ) ] with progressbar.ProgressBar( - widgets=widgets, max_value=20, term_width=10 + widgets=widgets, max_value=20, term_width=10 ) as progress: for i in range(20): time.sleep(0.1) @@ -592,7 +595,7 @@ def rotating_bouncing_marker(): @example -def incrementing_bar(): +def incrementing_bar() -> None: bar = progressbar.ProgressBar( widgets=[ progressbar.Percentage(), @@ -608,7 +611,7 @@ def incrementing_bar(): @example -def increment_bar_with_output_redirection(): +def increment_bar_with_output_redirection() -> None: widgets = [ 'Test: ', progressbar.Percentage(), @@ -631,7 +634,7 @@ def increment_bar_with_output_redirection(): @example -def eta_types_demonstration(): +def eta_types_demonstration() -> None: widgets = [ progressbar.Percentage(), ' ETA: ', @@ -665,7 +668,7 @@ def eta_types_demonstration(): @example -def adaptive_eta_without_value_change(): +def adaptive_eta_without_value_change() -> None: # Testing progressbar.AdaptiveETA when the value doesn't actually change bar = progressbar.ProgressBar( widgets=[ @@ -683,7 +686,7 @@ def adaptive_eta_without_value_change(): @example -def iterator_with_max_value(): +def iterator_with_max_value() -> None: # Testing using progressbar as an iterator with a max value bar = progressbar.ProgressBar() @@ -693,7 +696,7 @@ def iterator_with_max_value(): @example -def eta(): +def eta() -> None: widgets = [ 'Test: ', progressbar.Percentage(), @@ -712,7 +715,7 @@ def eta(): @example -def variables(): +def variables() -> None: # Use progressbar.Variable to keep track of some parameter(s) during # your calculations widgets = [ @@ -733,7 +736,7 @@ def variables(): @example -def user_variables(): +def user_variables() -> None: tasks = { 'Download': [ 'SDK', @@ -757,9 +760,9 @@ def user_variables(): num_subtasks = sum(len(x) for x in tasks.values()) with progressbar.ProgressBar( - prefix='{variables.task} >> {variables.subtask}', - variables={'task': '--', 'subtask': '--'}, - max_value=10 * num_subtasks, + prefix='{variables.task} >> {variables.subtask}', + variables={'task': '--', 'subtask': '--'}, + max_value=10 * num_subtasks, ) as bar: for tasks_name, subtasks in tasks.items(): for subtask_name in subtasks: @@ -771,7 +774,7 @@ def user_variables(): @example -def format_custom_text(): +def format_custom_text() -> None: format_custom_text = progressbar.FormatCustomText( 'Spam: %(spam).1f kg, eggs: %(eggs)d', dict( @@ -793,7 +796,7 @@ def format_custom_text(): @example -def simple_api_example(): +def simple_api_example() -> None: bar = progressbar.ProgressBar(widget_kwargs=dict(fill='█')) for _ in bar(range(200)): time.sleep(0.02) @@ -838,7 +841,7 @@ def gen(): time.sleep(0.02) -def test(*tests): +def test(*tests) -> None: if tests: no_tests = True for example in examples: diff --git a/progressbar/__about__.py b/progressbar/__about__.py index d374388..785fff8 100644 --- a/progressbar/__about__.py +++ b/progressbar/__about__.py @@ -1,4 +1,4 @@ -'''Text progress bar library for Python. +"""Text progress bar library for Python. A text progress bar is typically used to display the progress of a long running operation, providing a visual cue that processing is underway. @@ -9,19 +9,19 @@ The progressbar module is very easy to use, yet very powerful. It will also automatically enable features like auto-resizing when the system supports it. -''' +""" __title__ = 'Python Progressbar' __package_name__ = 'progressbar2' __author__ = 'Rick van Hattem (Wolph)' -__description__ = ' '.join( - ''' +__description__: str = ' '.join( + """ A Python Progressbar library to provide visual (yet text based) progress to long running operations. -'''.strip().split(), +""".strip().split(), ) __email__ = 'wolph@wol.ph' -__version__ = '4.4.3' +__version__ = '4.5.0' __license__ = 'BSD' __copyright__ = 'Copyright 2015 Rick van Hattem (Wolph)' __url__ = 'https://github.com/WoLpH/python-progressbar' diff --git a/progressbar/__main__.py b/progressbar/__main__.py index 431aa31..0bfd7fb 100644 --- a/progressbar/__main__.py +++ b/progressbar/__main__.py @@ -6,13 +6,13 @@ import sys import typing from pathlib import Path -from typing import BinaryIO, TextIO +from typing import IO, BinaryIO, TextIO import progressbar def size_to_bytes(size_str: str) -> int: - ''' + """ Convert a size string with suffixes 'k', 'm', etc., to bytes. Note: This function also supports '@' as a prefix to a file path to get the @@ -28,7 +28,7 @@ def size_to_bytes(size_str: str) -> int: 1024 >>> size_to_bytes('1024p') 1125899906842624 - ''' + """ # Define conversion rates suffix_exponent = { @@ -58,17 +58,17 @@ def size_to_bytes(size_str: str) -> int: def create_argument_parser() -> argparse.ArgumentParser: - ''' + """ Create the argument parser for the `progressbar` command. - ''' + """ parser = argparse.ArgumentParser( - description=''' + description=""" Monitor the progress of data through a pipe. Note that this is a Python implementation of the original `pv` command that is functional but not yet feature complete. - ''' + """ ) # Display switches @@ -133,7 +133,10 @@ def create_argument_parser() -> argparse.ArgumentParser: '-n', '--numeric', action='store_true', help='Numeric output.' ) parser.add_argument( - '-q', '--quiet', action='store_true', help='No output.' + '-q', + '--quiet', + action='store_true', + help='No output.', ) # Output modifiers @@ -269,8 +272,8 @@ def create_argument_parser() -> argparse.ArgumentParser: return parser -def main(argv: list[str] | None = None): # noqa: C901 - ''' +def main(argv: list[str] | None = None) -> None: # noqa: C901 + """ Main function for the `progressbar` command. Args: @@ -278,7 +281,7 @@ def main(argv: list[str] | None = None): # noqa: C901 Returns: None - ''' + """ parser: argparse.ArgumentParser = create_argument_parser() args: argparse.Namespace = parser.parse_args(argv) @@ -287,7 +290,7 @@ def main(argv: list[str] | None = None): # noqa: C901 args.output, args.line_mode, stack ) - input_paths: list[BinaryIO | TextIO | Path] = [] + input_paths: list[BinaryIO | TextIO | Path | IO[typing.Any]] = [] total_size: int = 0 filesize_available: bool = True for filename in args.input: diff --git a/progressbar/algorithms.py b/progressbar/algorithms.py index bb8586e..cf0faf2 100644 --- a/progressbar/algorithms.py +++ b/progressbar/algorithms.py @@ -11,18 +11,18 @@ def __init__(self, **kwargs): @abc.abstractmethod def update(self, new_value: float, elapsed: timedelta) -> float: - '''Updates the algorithm with a new value and returns the smoothed + """Updates the algorithm with a new value and returns the smoothed value. - ''' + """ raise NotImplementedError class ExponentialMovingAverage(SmoothingAlgorithm): - ''' + """ The Exponential Moving Average (EMA) is an exponentially weighted moving average that reduces the lag that's typically associated with a simple moving average. It's more responsive to recent changes in data. - ''' + """ def __init__(self, alpha: float = 0.5) -> None: self.alpha = alpha @@ -34,11 +34,11 @@ def update(self, new_value: float, elapsed: timedelta) -> float: class DoubleExponentialMovingAverage(SmoothingAlgorithm): - ''' + """ The Double Exponential Moving Average (DEMA) is essentially an EMA of an EMA, which reduces the lag that's typically associated with a simple EMA. It's more responsive to recent changes in data. - ''' + """ def __init__(self, alpha: float = 0.5) -> None: self.alpha = alpha diff --git a/progressbar/bar.py b/progressbar/bar.py index 8fe0f5d..c267fa2 100644 --- a/progressbar/bar.py +++ b/progressbar/bar.py @@ -9,9 +9,11 @@ import sys import time import timeit +import typing import warnings from copy import deepcopy from datetime import datetime +from types import FrameType from python_utils import converters, types @@ -32,6 +34,7 @@ # float also accepts integers and longs but we don't want an explicit union # due to type checking complexity NumberT = float +ValueT = typing.Union[NumberT, typing.Type[base.UnknownLength], None] T = types.TypeVar('T') @@ -86,7 +89,7 @@ class ProgressBarMixinBase(abc.ABC): min_value: NumberT #: Maximum (and final) value. Beyond this value an error will be raised #: unless the `max_error` parameter is `False`. - max_value: NumberT | types.Type[base.UnknownLength] + max_value: ValueT #: The time the progressbar reached `max_value` or when `finish()` was #: called. end_time: types.Optional[datetime] @@ -114,13 +117,13 @@ def set_last_update_time(self, value: types.Optional[datetime]): last_update_time = property(get_last_update_time, set_last_update_time) - def __init__(self, **kwargs): # noqa: B027 + def __init__(self, **kwargs: typing.Any): # noqa: B027 pass - def start(self, **kwargs): + def start(self, **kwargs: typing.Any): self._started = True - def update(self, value=None): # noqa: B027 + def update(self, value: ValueT = None): # noqa: B027 pass def finish(self): # pragma: no cover @@ -148,12 +151,12 @@ def finished(self) -> bool: return self._finished -class ProgressBarBase(types.Iterable, ProgressBarMixinBase): +class ProgressBarBase(types.Iterable[NumberT], ProgressBarMixinBase): _index_counter = itertools.count() index: int = -1 label: str = '' - def __init__(self, **kwargs): + def __init__(self, **kwargs: typing.Any): self.index = next(self._index_counter) super().__init__(**kwargs) @@ -194,7 +197,7 @@ def __init__( line_breaks: bool | None = None, enable_colors: progressbar.env.ColorSupport | None = None, line_offset: int = 0, - **kwargs, + **kwargs: typing.Any, ): if fd is sys.stdout: fd = utils.streams.original_stdout @@ -236,7 +239,7 @@ def _determine_enable_colors( self, enable_colors: progressbar.env.ColorSupport | None, ) -> progressbar.env.ColorSupport: - ''' + """ Determines the color support for the progress bar. This method checks the `enable_colors` parameter and the environment @@ -262,7 +265,7 @@ def _determine_enable_colors( Raises: ValueError: If `enable_colors` is not None, True, False, or an instance of `progressbar.env.ColorSupport`. - ''' + """ color_support: progressbar.env.ColorSupport if enable_colors is None: colors = ( @@ -295,7 +298,7 @@ def _determine_enable_colors( def print(self, *args: types.Any, **kwargs: types.Any) -> None: print(*args, file=self.fd, **kwargs) - def start(self, **kwargs): + def start(self, **kwargs: typing.Any): os_specific.set_console_mode() super().start() @@ -332,7 +335,7 @@ def finish( self.fd.flush() def _format_line(self): - 'Joins the widgets and justifies the line.' + "Joins the widgets and justifies the line." widgets = ''.join(self._to_unicode(self._format_widgets())) if self.left_justify: @@ -377,13 +380,13 @@ def _format_widgets(self): return result @classmethod - def _to_unicode(cls, args): + def _to_unicode(cls, args: typing.Any): for arg in args: yield converters.to_unicode(arg) class ResizableMixin(ProgressBarMixinBase): - def __init__(self, term_width: int | None = None, **kwargs): + def __init__(self, term_width: int | None = None, **kwargs: typing.Any): ProgressBarMixinBase.__init__(self, **kwargs) self.signal_set = False @@ -398,12 +401,15 @@ def __init__(self, term_width: int | None = None, **kwargs): signal.SIGWINCH # type: ignore ) signal.signal( - signal.SIGWINCH, self._handle_resize # type: ignore + signal.SIGWINCH, + self._handle_resize, # type: ignore ) self.signal_set = True - def _handle_resize(self, signum=None, frame=None): - 'Tries to catch resize signals sent from the terminal.' + def _handle_resize( + self, signum: int | None = None, frame: None | FrameType = None + ): + "Tries to catch resize signals sent from the terminal." w, h = utils.get_terminal_size() self.term_width = w @@ -414,17 +420,18 @@ def finish(self): # pragma: no cover import signal signal.signal( - signal.SIGWINCH, self._prev_handle # type: ignore + signal.SIGWINCH, + self._prev_handle, # type: ignore ) class StdRedirectMixin(DefaultFdMixin): redirect_stderr: bool = False redirect_stdout: bool = False - stdout: utils.WrappingIO | base.IO - stderr: utils.WrappingIO | base.IO - _stdout: base.IO - _stderr: base.IO + stdout: utils.WrappingIO | base.IO[typing.Any] + stderr: utils.WrappingIO | base.IO[typing.Any] + _stdout: base.IO[typing.Any] + _stderr: base.IO[typing.Any] def __init__( self, @@ -438,7 +445,7 @@ def __init__( self._stdout = self.stdout = sys.stdout self._stderr = self.stderr = sys.stderr - def start(self, *args, **kwargs): + def start(self, *args: typing.Any, **kwargs: typing.Any): if self.redirect_stdout: utils.streams.wrap_stdout() @@ -454,7 +461,7 @@ def start(self, *args, **kwargs): utils.streams.start_capturing(self) DefaultFdMixin.start(self, *args, **kwargs) - def update(self, value: types.Optional[float] = None): + def update(self, value: types.Optional[NumberT] = None): if not self.line_breaks and utils.streams.needs_clear(): self.fd.write('\r' + ' ' * self.term_width + '\r') @@ -476,7 +483,7 @@ class ProgressBar( ResizableMixin, ProgressBarBase, ): - '''The ProgressBar class which updates and prints the bar. + """The ProgressBar class which updates and prints the bar. Args: min_value (int): The minimum/start value for the progress bar @@ -520,7 +527,6 @@ class ProgressBar( >>> for i in range(100): ... progress.update(i + 1) ... # do something - ... >>> progress.finish() You can also use a ProgressBar as an iterator: @@ -530,7 +536,6 @@ class ProgressBar( >>> for i in progress(some_iterable): ... # do something ... pass - ... Since the progress bar is incredibly customizable you can specify different widgets of any type in any order. You can even write your own @@ -547,7 +552,7 @@ class ProgressBar( the current progress bar. As a result, you have access to the ProgressBar's methods and attributes. Although there is nothing preventing you from changing the ProgressBar you should treat it as read only. - ''' + """ _iterable: types.Optional[types.Iterator] @@ -560,7 +565,7 @@ class ProgressBar( def __init__( self, min_value: NumberT = 0, - max_value: NumberT | types.Type[base.UnknownLength] | None = None, + max_value: ValueT = None, widgets: types.Optional[ types.Sequence[widgets_module.WidgetBase | str] ] = None, @@ -576,7 +581,7 @@ def __init__( min_poll_interval=None, **kwargs, ): # sourcery skip: low-code-quality - '''Initializes a progress bar with sane defaults.''' + """Initializes a progress bar with sane defaults.""" StdRedirectMixin.__init__(self, **kwargs) ResizableMixin.__init__(self, **kwargs) ProgressBarBase.__init__(self, **kwargs) @@ -670,10 +675,10 @@ def dynamic_messages(self, value): # pragma: no cover self.variables = value def init(self): - ''' + """ (re)initialize values to original state so the progressbar can be used (again). - ''' + """ self.previous_value = None self.last_update_time = None self.start_time = None @@ -684,7 +689,7 @@ def init(self): @property def percentage(self) -> float | None: - '''Return current percentage, returns None if no max_value is given. + """Return current percentage, returns None if no max_value is given. >>> progress = ProgressBar() >>> progress.max_value = 10 @@ -713,7 +718,7 @@ def percentage(self) -> float | None: 25.0 >>> progress.max_value = None >>> progress.percentage - ''' + """ if self.max_value is None or self.max_value is base.UnknownLength: return None elif self.max_value: @@ -726,7 +731,7 @@ def percentage(self) -> float | None: return percentage def data(self) -> types.Dict[str, types.Any]: - ''' + """ Returns: dict: @@ -752,7 +757,7 @@ def data(self) -> types.Dict[str, types.Any]: - `variables`: Dictionary of user-defined variables for the :py:class:`~progressbar.widgets.Variable`'s. - ''' + """ self._last_update_time = time.time() self._last_update_timer = timeit.default_timer() elapsed = self.last_update_time - self.start_time # type: ignore @@ -824,7 +829,7 @@ def default_widgets(self): ] def __call__(self, iterable, max_value=None): - 'Use a ProgressBar to iterate through an iterable.' + "Use a ProgressBar to iterate through an iterable." if max_value is not None: self.max_value = max_value elif self.max_value is None: @@ -871,15 +876,17 @@ def __enter__(self): next = __next__ def __iadd__(self, value): - 'Updates the ProgressBar by adding a new value.' + "Updates the ProgressBar by adding a new value." return self.increment(value) - def increment(self, value=1, *args, **kwargs): + def increment( + self, value: NumberT = 1, *args: typing.Any, **kwargs: typing.Any + ): self.update(self.value + value, *args, **kwargs) return self def _needs_update(self): - 'Returns whether the ProgressBar should redraw the line.' + "Returns whether the ProgressBar should redraw the line." if self.paused: return False delta = timeit.default_timer() - self._last_update_timer @@ -902,8 +909,10 @@ def _needs_update(self): # No need to redraw yet return False - def update(self, value=None, force=False, **kwargs): - 'Updates the ProgressBar to a new value.' + def update( + self, value: ValueT = None, force: bool = False, **kwargs: typing.Any + ): + "Updates the ProgressBar to a new value." if self.start_time is None: self.start() @@ -927,10 +936,10 @@ def update(self, value=None, force=False, **kwargs): f'{self.min_value} and {self.max_value}', ) else: - value = self.max_value + value = typing.cast(NumberT, self.max_value) self.previous_value = self.value - self.value = value # type: ignore + self.value = value # Save the updated values for dynamic messages variables_changed = self._update_variables(kwargs) @@ -951,7 +960,7 @@ def _update_variables(self, kwargs): variables_changed = True return variables_changed - def _update_parents(self, value): + def _update_parents(self, value: ValueT): self.updates += 1 ResizableMixin.update(self, value=value) ProgressBarBase.update(self, value=value) @@ -960,8 +969,14 @@ def _update_parents(self, value): # Only flush if something was actually written self.fd.flush() - def start(self, max_value=None, init=True, *args, **kwargs): - '''Starts measuring time, and prints the bar at 0%. + def start( + self, + max_value: NumberT | None = None, + init: bool = True, + *args: typing.Any, + **kwargs: typing.Any, + ) -> ProgressBar: + """Starts measuring time, and prints the bar at 0%. It returns self so you can use it like this: @@ -973,11 +988,10 @@ def start(self, max_value=None, init=True, *args, **kwargs): >>> pbar = ProgressBar().start() >>> for i in range(100): - ... # do something - ... pbar.update(i+1) - ... + ... # do something + ... pbar.update(i + 1) >>> pbar.finish() - ''' + """ if init: self.init() @@ -1052,8 +1066,8 @@ def _calculate_poll_interval(self) -> None: interval, ) - def finish(self, end='\n', dirty=False): - ''' + def finish(self, end: str = '\n', dirty: bool = False): + """ Puts the ProgressBar bar in the finished state. Also flushes and disables output buffering if this was the last @@ -1064,7 +1078,7 @@ def finish(self, end='\n', dirty=False): newline dirty (bool): When True the progressbar kept the current state and won't be set to 100 percent - ''' + """ if not dirty: self.end_time = datetime.now() self.update(self.max_value, force=True) @@ -1075,10 +1089,10 @@ def finish(self, end='\n', dirty=False): @property def currval(self): - ''' + """ Legacy method to make progressbar-2 compatible with the original progressbar package. - ''' + """ warnings.warn( 'The usage of `currval` is deprecated, please use ' '`value` instead', @@ -1089,10 +1103,10 @@ def currval(self): class DataTransferBar(ProgressBar): - '''A progress bar with sensible defaults for downloads etc. + """A progress bar with sensible defaults for downloads etc. This assumes that the values its given are numbers of bytes. - ''' + """ def default_widgets(self): if self.max_value: @@ -1118,16 +1132,16 @@ def default_widgets(self): class NullBar(ProgressBar): - ''' + """ Progress bar that does absolutely nothing. Useful for single verbosity flags. - ''' + """ - def start(self, *args, **kwargs): + def start(self, *args: typing.Any, **kwargs: typing.Any): return self - def update(self, *args, **kwargs): + def update(self, *args: typing.Any, **kwargs: typing.Any): return self - def finish(self, *args, **kwargs): + def finish(self, *args: typing.Any, **kwargs: typing.Any): return self diff --git a/progressbar/base.py b/progressbar/base.py index f3f2ef5..2401832 100644 --- a/progressbar/base.py +++ b/progressbar/base.py @@ -1,13 +1,16 @@ -from python_utils import types +from __future__ import annotations + +import typing +from typing import IO, TextIO class FalseMeta(type): @classmethod - def __bool__(cls): # pragma: no cover + def __bool__(cls) -> bool: # pragma: no cover return False @classmethod - def __cmp__(cls, other): # pragma: no cover + def __cmp__(cls, other: typing.Any) -> int: # pragma: no cover return -1 __nonzero__ = __bool__ @@ -21,11 +24,13 @@ class Undefined(metaclass=FalseMeta): pass -try: # pragma: no cover - IO = types.IO # type: ignore - TextIO = types.TextIO # type: ignore -except AttributeError: # pragma: no cover - from typing.io import IO, TextIO # type: ignore - assert IO is not None assert TextIO is not None + +__all__ = ( + 'FalseMeta', + 'UnknownLength', + 'Undefined', + 'IO', + 'TextIO', +) diff --git a/progressbar/env.py b/progressbar/env.py index 54e3729..3871c2e 100644 --- a/progressbar/env.py +++ b/progressbar/env.py @@ -6,8 +6,6 @@ import re import typing -from . import base - @typing.overload def env_flag(name: str, default: bool) -> bool: ... @@ -17,14 +15,14 @@ def env_flag(name: str, default: bool) -> bool: ... def env_flag(name: str, default: bool | None = None) -> bool | None: ... -def env_flag(name, default=None): - ''' +def env_flag(name: str, default: bool | None = None) -> bool | None: + """ Accepts environt variables formatted as y/n, yes/no, 1/0, true/false, on/off, and returns it as a boolean. If the environment variable is not defined, or has an unknown value, returns `default` - ''' + """ v = os.getenv(name) if v and v.lower() in ('y', 'yes', 't', 'true', 'on', '1'): return True @@ -34,7 +32,7 @@ def env_flag(name, default=None): class ColorSupport(enum.IntEnum): - '''Color support for the terminal.''' + """Color support for the terminal.""" NONE = 0 XTERM = 16 @@ -43,8 +41,8 @@ class ColorSupport(enum.IntEnum): WINDOWS = 8 @classmethod - def from_env(cls): - '''Get the color support from the environment. + def from_env(cls) -> ColorSupport: + """Get the color support from the environment. If any of the environment variables contain `24bit` or `truecolor`, we will enable true color/24 bit support. If they contain `256`, we @@ -56,7 +54,7 @@ def from_env(cls): Note that the highest available value will be used! Having `COLORTERM=truecolor` will override `TERM=xterm-256color`. - ''' + """ variables = ( 'FORCE_COLOR', 'PROGRESSBAR_ENABLE_COLORS', @@ -99,7 +97,7 @@ def from_env(cls): def is_ansi_terminal( - fd: base.IO, + fd: typing.IO[typing.Any], is_terminal: bool | None = None, ) -> bool | None: # pragma: no cover if is_terminal is None: @@ -120,7 +118,7 @@ def is_ansi_terminal( # use ansi. ansi terminals will typically define one of the 2 # environment variables. with contextlib.suppress(Exception): - is_tty = fd.isatty() + is_tty: bool = fd.isatty() # Try and match any of the huge amount of Linux/Unix ANSI consoles if is_tty and ANSI_TERM_RE.match(os.environ.get('TERM', '')): is_terminal = True @@ -140,7 +138,10 @@ def is_ansi_terminal( return is_terminal -def is_terminal(fd: base.IO, is_terminal: bool | None = None) -> bool | None: +def is_terminal( + fd: typing.IO[typing.Any], + is_terminal: bool | None = None, +) -> bool | None: if is_terminal is None: # Full ansi support encompasses what we expect from a terminal is_terminal = is_ansi_terminal(fd) or None @@ -183,4 +184,6 @@ def is_terminal(fd: base.IO, is_terminal: bool | None = None) -> bool | None: 'tmux', 'vt(10[02]|220|320)', ) -ANSI_TERM_RE = re.compile(f"^({'|'.join(ANSI_TERMS)})", re.IGNORECASE) +ANSI_TERM_RE: re.Pattern[str] = re.compile( + f"^({'|'.join(ANSI_TERMS)})", re.IGNORECASE +) diff --git a/progressbar/multi.py b/progressbar/multi.py index ae3dd23..8900b89 100644 --- a/progressbar/multi.py +++ b/progressbar/multi.py @@ -20,7 +20,7 @@ class SortKey(str, enum.Enum): - ''' + """ Sort keys for the MultiBar. This is a string enum, so you can use any @@ -30,7 +30,7 @@ class SortKey(str, enum.Enum): progressbars. This means that sorting by dynamic attributes such as `value` might result in more rendering which can have a small performance impact. - ''' + """ CREATED = 'index' LABEL = 'label' @@ -124,7 +124,7 @@ def __init__( super().__init__(bars or {}) def __setitem__(self, key: str, bar: bar.ProgressBar): - '''Add a progressbar to the multibar.''' + """Add a progressbar to the multibar.""" if bar.label != key or not key: # pragma: no branch bar.label = key bar.fd = stream.LastLineStream(self.fd) @@ -141,13 +141,13 @@ def __setitem__(self, key: str, bar: bar.ProgressBar): super().__setitem__(key, bar) def __delitem__(self, key): - '''Remove a progressbar from the multibar.''' + """Remove a progressbar from the multibar.""" super().__delitem__(key) self._finished_at.pop(key, None) self._labeled.discard(key) def __getitem__(self, key): - '''Get (and create if needed) a progressbar from the multibar.''' + """Get (and create if needed) a progressbar from the multibar.""" try: return super().__getitem__(key) except KeyError: @@ -170,7 +170,7 @@ def _label_bar(self, bar: bar.ProgressBar): bar.widgets.append(self.label_format.format(label=bar.label)) def render(self, flush: bool = True, force: bool = False): - '''Render the multibar to the given stream.''' + """Render the multibar to the given stream.""" now = timeit.default_timer() expired = now - self.remove_finished if self.remove_finished else None @@ -280,7 +280,7 @@ def print( clear=True, **kwargs, ): - ''' + """ Print to the progressbar stream without overwriting the progressbars. Args: @@ -290,7 +290,7 @@ def print( flush: Whether to flush the output to the stream clear: If True, the line will be cleared before printing. **kwargs: Additional keyword arguments to pass to print - ''' + """ with self._print_lock: if offset is None: offset = len(self._previous_output) @@ -322,10 +322,10 @@ def flush(self): self.fd.flush() def run(self, join=True): - ''' + """ Start the multibar render loop and run the progressbars until they have force _thread_finished. - ''' + """ while not self._thread_finished.is_set(): # pragma: no branch self.render() time.sleep(self.update_interval) diff --git a/progressbar/shortcuts.py b/progressbar/shortcuts.py index b16f19a..edf0a5b 100644 --- a/progressbar/shortcuts.py +++ b/progressbar/shortcuts.py @@ -3,7 +3,7 @@ def progressbar( iterator, - min_value=0, + min_value: int = 0, max_value=None, widgets=None, prefix=None, diff --git a/progressbar/terminal/base.py b/progressbar/terminal/base.py index 895887b..1141e52 100644 --- a/progressbar/terminal/base.py +++ b/progressbar/terminal/base.py @@ -19,14 +19,14 @@ ) from .os_specific import getch -ESC = '\x1B' +ESC = '\x1b' class CSI: _code: str _template = ESC + '[{args}{code}' - def __init__(self, code, *default_args): + def __init__(self, code: str, *default_args) -> None: self._code = code self._default_args = default_args @@ -46,72 +46,72 @@ def __call__(self): #: Cursor Position [row;column] (default = [1,1]) -CUP = CSI('H', 1, 1) +CUP: CSI = CSI('H', 1, 1) #: Cursor Up Ps Times (default = 1) (CUU) -UP = CSI('A', 1) +UP: CSI = CSI('A', 1) #: Cursor Down Ps Times (default = 1) (CUD) -DOWN = CSI('B', 1) +DOWN: CSI = CSI('B', 1) #: Cursor Forward Ps Times (default = 1) (CUF) -RIGHT = CSI('C', 1) +RIGHT: CSI = CSI('C', 1) #: Cursor Backward Ps Times (default = 1) (CUB) -LEFT = CSI('D', 1) +LEFT: CSI = CSI('D', 1) #: Cursor Next Line Ps Times (default = 1) (CNL) #: Same as Cursor Down Ps Times -NEXT_LINE = CSI('E', 1) +NEXT_LINE: CSI = CSI('E', 1) #: Cursor Preceding Line Ps Times (default = 1) (CPL) #: Same as Cursor Up Ps Times -PREVIOUS_LINE = CSI('F', 1) +PREVIOUS_LINE: CSI = CSI('F', 1) #: Cursor Character Absolute [column] (default = [row,1]) (CHA) -COLUMN = CSI('G', 1) +COLUMN: CSI = CSI('G', 1) #: Erase in Display (ED) -CLEAR_SCREEN = CSI('J', 0) +CLEAR_SCREEN: CSI = CSI('J', 0) #: Erase till end of screen -CLEAR_SCREEN_TILL_END = CSINoArg('0J') +CLEAR_SCREEN_TILL_END: CSINoArg = CSINoArg('0J') #: Erase till start of screen -CLEAR_SCREEN_TILL_START = CSINoArg('1J') +CLEAR_SCREEN_TILL_START: CSINoArg = CSINoArg('1J') #: Erase whole screen -CLEAR_SCREEN_ALL = CSINoArg('2J') +CLEAR_SCREEN_ALL: CSINoArg = CSINoArg('2J') #: Erase whole screen and history -CLEAR_SCREEN_ALL_AND_HISTORY = CSINoArg('3J') +CLEAR_SCREEN_ALL_AND_HISTORY: CSINoArg = CSINoArg('3J') #: Erase in Line (EL) -CLEAR_LINE_ALL = CSI('K') +CLEAR_LINE_ALL: CSI = CSI('K') #: Erase in Line from Cursor to End of Line (default) -CLEAR_LINE_RIGHT = CSINoArg('0K') +CLEAR_LINE_RIGHT: CSINoArg = CSINoArg('0K') #: Erase in Line from Cursor to Beginning of Line -CLEAR_LINE_LEFT = CSINoArg('1K') +CLEAR_LINE_LEFT: CSINoArg = CSINoArg('1K') #: Erase Line containing Cursor -CLEAR_LINE = CSINoArg('2K') +CLEAR_LINE: CSINoArg = CSINoArg('2K') #: Scroll up Ps lines (default = 1) (SU) #: Scroll down Ps lines (default = 1) (SD) -SCROLL_UP = CSI('S') -SCROLL_DOWN = CSI('T') +SCROLL_UP: CSI = CSI('S') +SCROLL_DOWN: CSI = CSI('T') #: Save Cursor Position (SCP) -SAVE_CURSOR = CSINoArg('s') +SAVE_CURSOR: CSINoArg = CSINoArg('s') #: Restore Cursor Position (RCP) -RESTORE_CURSOR = CSINoArg('u') +RESTORE_CURSOR: CSINoArg = CSINoArg('u') #: Cursor Visibility (DECTCEM) -HIDE_CURSOR = CSINoArg('?25l') -SHOW_CURSOR = CSINoArg('?25h') +HIDE_CURSOR: CSINoArg = CSINoArg('?25l') +SHOW_CURSOR: CSINoArg = CSINoArg('?25h') # @@ -170,11 +170,11 @@ def __call__(self, stream) -> tuple[int, int]: return types.cast(types.Tuple[int, int], tuple(res_list)) - def row(self, stream): + def row(self, stream) -> int: row, _ = self(stream) return row - def column(self, stream): + def column(self, stream) -> int: _, column = self(stream) return column @@ -198,8 +198,8 @@ class WindowsColors(enum.Enum): INTENSE_WHITE = 255, 255, 255 @staticmethod - def from_rgb(rgb: types.Tuple[int, int, int]): - ''' + def from_rgb(rgb: types.Tuple[int, int, int]) -> WindowsColors: + """ Find the closest WindowsColors to the given RGB color. >>> WindowsColors.from_rgb((0, 0, 0)) @@ -216,7 +216,7 @@ def from_rgb(rgb: types.Tuple[int, int, int]): >>> WindowsColors.from_rgb((128, 0, 128)) - ''' + """ def color_distance(rgb1, rgb2): return sum((c1 - c2) ** 2 for c1, c2 in zip(rgb1, rgb2)) @@ -228,17 +228,17 @@ def color_distance(rgb1, rgb2): class WindowsColor: - ''' + """ Windows compatible color class for when ANSI is not supported. Currently a no-op because it is not possible to buffer these colors. >>> WindowsColor(WindowsColors.RED)('test') 'test' - ''' + """ __slots__ = ('color',) - def __init__(self, color: Color): + def __init__(self, color: Color) -> None: self.color = color def __call__(self, text): @@ -259,15 +259,15 @@ def __str__(self): return self.rgb @property - def rgb(self): + def rgb(self) -> str: return f'rgb({self.red}, {self.green}, {self.blue})' @property - def hex(self): + def hex(self) -> str: return f'#{self.red:02x}{self.green:02x}{self.blue:02x}' @property - def to_ansi_16(self): + def to_ansi_16(self) -> int: # Using int instead of round because it maps slightly better red = int(self.red / 255) green = int(self.green / 255) @@ -275,7 +275,7 @@ def to_ansi_16(self): return (blue << 2) | (green << 1) | red @property - def to_ansi_256(self): + def to_ansi_256(self) -> int: red = round(self.red / 255 * 5) green = round(self.green / 255 * 5) blue = round(self.blue / 255 * 5) @@ -283,10 +283,10 @@ def to_ansi_256(self): @property def to_windows(self): - ''' + """ Convert an RGB color (0-255 per channel) to the closest color in the Windows 16 color scheme. - ''' + """ return WindowsColors.from_rgb((self.red, self.green, self.blue)) def interpolate(self, end: RGB, step: float) -> RGB: @@ -298,21 +298,21 @@ def interpolate(self, end: RGB, step: float) -> RGB: class HSL(collections.namedtuple('HSL', ['hue', 'saturation', 'lightness'])): - ''' + """ Hue, Saturation, Lightness color. Hue is a value between 0 and 360, saturation and lightness are between 0(%) and 100(%). - ''' + """ __slots__ = () @classmethod def from_rgb(cls, rgb: RGB) -> HSL: - ''' + """ Convert a 0-255 RGB color to a 0-255 HLS color. - ''' + """ hls = colorsys.rgb_to_hls( rgb.red / 255, rgb.green / 255, @@ -349,7 +349,7 @@ class Color( ), ColorBase, ): - ''' + """ Color base class. This class contains the colors in RGB (Red, Green, Blue), HSL (Hue, @@ -359,7 +359,7 @@ class Color( To make a custom color the only required arguments are the RGB values. The other values will be automatically interpolated from that if needed, but you can be more explicitly if you wish. - ''' + """ __slots__ = () @@ -367,21 +367,21 @@ def __call__(self, value: str) -> str: return self.fg(value) @property - def fg(self): + def fg(self) -> SGRColor | WindowsColor: if env.COLOR_SUPPORT is env.ColorSupport.WINDOWS: return WindowsColor(self) else: return SGRColor(self, 38, 39) @property - def bg(self): + def bg(self) -> DummyColor | SGRColor: if env.COLOR_SUPPORT is env.ColorSupport.WINDOWS: return DummyColor() else: return SGRColor(self, 48, 49) @property - def underline(self): + def underline(self) -> DummyColor | SGRColor: if env.COLOR_SUPPORT is env.ColorSupport.WINDOWS: return DummyColor() else: @@ -418,10 +418,10 @@ def interpolate(self, end: Color, step: float) -> Color: def __str__(self): return self.name - def __repr__(self): + def __repr__(self) -> str: return f'{self.__class__.__name__}({self.name!r})' - def __hash__(self): + def __hash__(self) -> int: return hash(self.rgb) @@ -475,7 +475,7 @@ def interpolate(cls, color_a: Color, color_b: Color, step: float) -> Color: class ColorGradient(ColorBase): - def __init__(self, *colors: Color, interpolate=Colors.interpolate): + def __init__(self, *colors: Color, interpolate=Colors.interpolate) -> None: assert colors self.colors = colors self.interpolate = interpolate @@ -484,7 +484,7 @@ def __call__(self, value: float) -> Color: return self.get_color(value) def get_color(self, value: float) -> Color: - 'Map a value from 0 to 1 to a color.' + "Map a value from 0 to 1 to a color." if ( value == pbase.Undefined or value == pbase.UnknownLength @@ -543,12 +543,12 @@ def apply_colors( bg_none: Color | None = None, **kwargs: types.Any, ) -> str: - '''Apply colors/gradients to a string depending on the given percentage. + """Apply colors/gradients to a string depending on the given percentage. When percentage is `None`, the `fg_none` and `bg_none` colors will be used. Otherwise, the `fg` and `bg` colors will be used. If the colors are gradients, the color will be interpolated depending on the percentage. - ''' + """ if percentage is None: if fg_none is not None: text = fg_none.fg(text) @@ -570,7 +570,7 @@ class DummyColor: def __call__(self, text): return text - def __repr__(self): + def __repr__(self) -> str: return 'DummyColor()' @@ -580,7 +580,7 @@ class SGR(CSI): _code = 'm' __slots__ = '_start_code', '_end_code' - def __init__(self, start_code: int, end_code: int): + def __init__(self, start_code: int, end_code: int) -> None: self._start_code = start_code self._end_code = end_code @@ -599,7 +599,7 @@ def __call__(self, text, *args): class SGRColor(SGR): __slots__ = '_color', '_start_code', '_end_code' - def __init__(self, color: Color, start_code: int, end_code: int): + def __init__(self, color: Color, start_code: int, end_code: int) -> None: self._color = color super().__init__(start_code, end_code) @@ -608,16 +608,16 @@ def _start_template(self): return CSI.__call__(self, self._start_code, self._color.ansi) -encircled = SGR(52, 54) -framed = SGR(51, 54) -overline = SGR(53, 55) -bold = SGR(1, 22) -gothic = SGR(20, 10) -italic = SGR(3, 23) -strike_through = SGR(9, 29) -fast_blink = SGR(6, 25) -slow_blink = SGR(5, 25) -underline = SGR(4, 24) -double_underline = SGR(21, 24) -faint = SGR(2, 22) -inverse = SGR(7, 27) +encircled: SGR = SGR(52, 54) +framed: SGR = SGR(51, 54) +overline: SGR = SGR(53, 55) +bold: SGR = SGR(1, 22) +gothic: SGR = SGR(20, 10) +italic: SGR = SGR(3, 23) +strike_through: SGR = SGR(9, 29) +fast_blink: SGR = SGR(6, 25) +slow_blink: SGR = SGR(5, 25) +underline: SGR = SGR(4, 24) +double_underline: SGR = SGR(21, 24) +faint: SGR = SGR(2, 22) +inverse: SGR = SGR(7, 27) diff --git a/progressbar/terminal/colors.py b/progressbar/terminal/colors.py index 53354ac..37e5ea9 100644 --- a/progressbar/terminal/colors.py +++ b/progressbar/terminal/colors.py @@ -1,3 +1,5 @@ +from __future__ import annotations + # Based on: https://www.ditig.com/256-colors-cheat-sheet import os @@ -1035,7 +1037,7 @@ grey89 = Colors.register(RGB(228, 228, 228), HSL(0, 0, 89), 'Grey89', 254) grey93 = Colors.register(RGB(238, 238, 238), HSL(0, 0, 93), 'Grey93', 255) -dark_gradient = ColorGradient( +dark_gradient: ColorGradient = ColorGradient( red1, orange_red1, dark_orange, @@ -1045,7 +1047,7 @@ green_yellow, green1, ) -light_gradient = ColorGradient( +light_gradient: ColorGradient = ColorGradient( red1, orange_red1, dark_orange, @@ -1055,16 +1057,16 @@ yellow4, green3, ) -bg_gradient = ColorGradient(black) +bg_gradient: ColorGradient = ColorGradient(black) # Check if the background is light or dark. This is by no means a foolproof # method, but there is no reliable way to detect this. -_colorfgbg = os.environ.get('COLORFGBG', '15;0').split(';') +_colorfgbg: list[str] = os.environ.get('COLORFGBG', '15;0').split(';') if _colorfgbg[-1] == str(white.xterm): # pragma: no cover # Light background - gradient = light_gradient + gradient: ColorGradient = light_gradient primary = black else: # Default, expect a dark background - gradient = dark_gradient + gradient: ColorGradient = dark_gradient primary = white diff --git a/progressbar/terminal/os_specific/posix.py b/progressbar/terminal/os_specific/posix.py index 52a9560..3481998 100644 --- a/progressbar/terminal/os_specific/posix.py +++ b/progressbar/terminal/os_specific/posix.py @@ -3,7 +3,7 @@ import tty -def getch(): +def getch() -> str: fd = sys.stdin.fileno() old_settings = termios.tcgetattr(fd) # type: ignore try: diff --git a/progressbar/terminal/os_specific/windows.py b/progressbar/terminal/os_specific/windows.py index 425d349..8d1f3f4 100644 --- a/progressbar/terminal/os_specific/windows.py +++ b/progressbar/terminal/os_specific/windows.py @@ -1,10 +1,11 @@ # ruff: noqa: N801 -''' +""" Windows specific code for the terminal. Note that the naming convention here is non-pythonic because we are matching the Windows API naming. -''' +""" + from __future__ import annotations import ctypes @@ -43,7 +44,7 @@ class WindowsConsoleModeFlags(enum.IntFlag): DISABLE_NEWLINE_AUTO_RETURN = 0x0008 ENABLE_LVB_GRID_WORLDWIDE = 0x0010 - def __str__(self): + def __str__(self) -> str: return f'{self.name} (0x{self.value:04X})' @@ -148,7 +149,7 @@ def set_text_color(color) -> None: _kernel32.SetConsoleTextAttribute(_h_console_output, color) -def print_color(text, color): +def print_color(text, color) -> None: set_text_color(color) print(text) # noqa: T201 set_text_color(7) # Reset to default color, grey diff --git a/progressbar/terminal/stream.py b/progressbar/terminal/stream.py index ee02a9d..eb8de2a 100644 --- a/progressbar/terminal/stream.py +++ b/progressbar/terminal/stream.py @@ -9,7 +9,7 @@ class TextIOOutputWrapper(base.TextIO): # pragma: no cover - def __init__(self, stream: base.TextIO): + def __init__(self, stream: base.TextIO) -> None: self.stream = stream def close(self) -> None: @@ -76,21 +76,25 @@ class LineOffsetStreamWrapper(TextIOOutputWrapper): UP = '\033[F' DOWN = '\033[B' - def __init__(self, lines=0, stream=sys.stderr): + def __init__( + self, lines: int = 0, stream: typing.TextIO = sys.stderr + ) -> None: self.lines = lines super().__init__(stream) - def write(self, data): + def write(self, data: str) -> int: + data = data.rstrip('\n') # Move the cursor up self.stream.write(self.UP * self.lines) # Print a carriage return to reset the cursor position self.stream.write('\r') # Print the data without newlines so we don't change the position - self.stream.write(data.rstrip('\n')) + self.stream.write(data) # Move the cursor down self.stream.write(self.DOWN * self.lines) self.flush() + return len(data) class LastLineStream(TextIOOutputWrapper): diff --git a/progressbar/utils.py b/progressbar/utils.py index 9b167a8..6323ae8 100644 --- a/progressbar/utils.py +++ b/progressbar/utils.py @@ -32,10 +32,10 @@ def deltas_to_seconds( - *deltas, + *deltas: None | datetime.timedelta | float, default: types.Optional[types.Type[ValueError]] = ValueError, ) -> int | float | None: - ''' + """ Convert timedeltas and seconds as int to seconds as float while coalescing. >>> deltas_to_seconds(datetime.timedelta(seconds=1, milliseconds=234)) @@ -58,7 +58,7 @@ def deltas_to_seconds( ValueError: No valid deltas passed to `deltas_to_seconds` >>> deltas_to_seconds(default=0.0) 0.0 - ''' + """ for delta in deltas: if delta is None: continue @@ -77,12 +77,12 @@ def deltas_to_seconds( def no_color(value: StringT) -> StringT: - ''' + """ Return the `value` without ANSI escape codes. >>> no_color(b'\u001b[1234]abc') b'abc' - >>> str(no_color(u'\u001b[1234]abc')) + >>> str(no_color('\u001b[1234]abc')) 'abc' >>> str(no_color('\u001b[1234]abc')) 'abc' @@ -90,7 +90,7 @@ def no_color(value: StringT) -> StringT: Traceback (most recent call last): ... TypeError: `value` must be a string or bytes, got 123 - ''' + """ if isinstance(value, bytes): pattern: bytes = bytes(terminal.ESC, 'ascii') + b'\\[.*?[@-~]' return re.sub(pattern, b'', value) # type: ignore @@ -101,16 +101,16 @@ def no_color(value: StringT) -> StringT: def len_color(value: types.StringTypes) -> int: - ''' + """ Return the length of `value` without ANSI escape codes. >>> len_color(b'\u001b[1234]abc') 3 - >>> len_color(u'\u001b[1234]abc') + >>> len_color('\u001b[1234]abc') 3 >>> len_color('\u001b[1234]abc') 3 - ''' + """ return len(no_color(value)) @@ -225,7 +225,7 @@ def __exit__( class StreamWrapper: - '''Wrap stdout and stderr globally.''' + """Wrap stdout and stderr globally.""" stdout: base.TextIO | WrappingIO stderr: base.TextIO | WrappingIO @@ -243,7 +243,7 @@ class StreamWrapper: capturing: int = 0 listeners: set - def __init__(self): + def __init__(self) -> None: self.stdout = self.original_stdout = sys.stdout self.stderr = self.original_stderr = sys.stderr self.original_excepthook = sys.excepthook @@ -373,13 +373,18 @@ def flush(self) -> None: sys.stderr, ) - def excepthook(self, exc_type, exc_value, exc_traceback): + def excepthook( + self, + exc_type: type[BaseException], + exc_value: BaseException, + exc_traceback: types.TracebackType | None, + ) -> None: self.original_excepthook(exc_type, exc_value, exc_traceback) self.flush() class AttributeDict(dict): - ''' + """ A dict that can be accessed with .attribute. >>> attrs = AttributeDict(spam=123) @@ -422,7 +427,7 @@ class AttributeDict(dict): Traceback (most recent call last): ... AttributeError: No such attribute: spam - ''' + """ def __getattr__(self, name: str) -> int: if name in self: @@ -440,6 +445,6 @@ def __delattr__(self, name: str) -> None: raise AttributeError(f'No such attribute: {name}') -logger = logging.getLogger(__name__) +logger: logging.Logger = logging.getLogger(__name__) streams = StreamWrapper() atexit.register(streams.flush) diff --git a/progressbar/widgets.py b/progressbar/widgets.py index fd9408e..c8c3cdf 100644 --- a/progressbar/widgets.py +++ b/progressbar/widgets.py @@ -17,7 +17,7 @@ from .terminal import colors if types.TYPE_CHECKING: - from .bar import ProgressBarMixinBase + from .bar import NumberT, ProgressBarMixinBase logger = logging.getLogger(__name__) @@ -43,7 +43,7 @@ def render_input(progress, data, width): def create_wrapper(wrapper): - '''Convert a wrapper tuple or format string to a format string. + """Convert a wrapper tuple or format string to a format string. >>> create_wrapper('') @@ -52,7 +52,7 @@ def create_wrapper(wrapper): >>> print(create_wrapper(('a', 'b'))) a{}b - ''' + """ if isinstance(wrapper, tuple) and len(wrapper) == 2: a, b = wrapper wrapper = (a or '') + '{}' + (b or '') @@ -71,10 +71,10 @@ def create_wrapper(wrapper): def wrapper(function, wrapper_): - '''Wrap the output of a function in a template string or a tuple with + """Wrap the output of a function in a template string or a tuple with begin/end strings. - ''' + """ wrapper_ = create_wrapper(wrapper_) if not wrapper_: return function @@ -99,16 +99,16 @@ def _marker(progress, data, width): if isinstance(marker, str): marker = converters.to_unicode(marker) - assert ( - utils.len_color(marker) == 1 - ), 'Markers are required to be 1 char' + # Ruff is silly at times... the format is not compatible with the check + marker_length_error = 'Markers are required to be 1 char' + assert utils.len_color(marker) == 1, marker_length_error return wrapper(_marker, wrap) else: return wrapper(marker, wrap) class FormatWidgetMixin(abc.ABC): - '''Mixin to format widgets using a formatstring. + """Mixin to format widgets using a formatstring. Variables available: - max_value: The maximum value (can be None with iterators) @@ -121,7 +121,7 @@ class FormatWidgetMixin(abc.ABC): - time_elapsed: Shortcut for HH:MM:SS time since the bar started including days - percentage: Percentage as a float - ''' + """ def __init__(self, format: str, new_style: bool = False, **kwargs): self.new_style = new_style @@ -141,7 +141,7 @@ def __call__( data: Data, format: types.Optional[str] = None, ) -> str: - '''Formats the widget into a string.''' + """Formats the widget into a string.""" format_ = self.get_format(progress, data, format) try: if self.new_style: @@ -158,7 +158,7 @@ def __call__( class WidthWidgetMixin(abc.ABC): - '''Mixing to make sure widgets are only visible if the screen is within a + """Mixing to make sure widgets are only visible if the screen is within a specified size range so the progressbar fits on both large and small screens. @@ -180,7 +180,7 @@ class WidthWidgetMixin(abc.ABC): >>> Progress.term_width = 11 >>> WidthWidgetMixin(5, 10).check_size(Progress) False - ''' + """ def __init__(self, min_width=None, max_width=None, **kwargs): self.min_width = min_width @@ -208,7 +208,7 @@ class TFixedColors(typing.TypedDict): class WidgetBase(WidthWidgetMixin, metaclass=abc.ABCMeta): - '''The base class for all widgets. + """The base class for all widgets. The ProgressBar will call the widget's update value when the widget should be updated. The widget's size may change between calls, but the widget may @@ -234,16 +234,16 @@ class WidgetBase(WidthWidgetMixin, metaclass=abc.ABCMeta): progressbar can be reused. Some widgets such as the FormatCustomText require the shared state so this needs to be optional - ''' + """ copy = True @abc.abstractmethod def __call__(self, progress: ProgressBarMixinBase, data: Data) -> str: - '''Updates the widget. + """Updates the widget. progress - a reference to the calling ProgressBar - ''' + """ _fixed_colors: ClassVar[TFixedColors] = TFixedColors( fg_none=None, @@ -297,12 +297,12 @@ def __init__( class AutoWidthWidgetBase(WidgetBase, metaclass=abc.ABCMeta): - '''The base class for all variable width widgets. + """The base class for all variable width widgets. This widget is much like the \\hfill command in TeX, it will expand to fill the line. You can use more than one in the same line, and they will all have the same width, and together will fill the line. - ''' + """ @abc.abstractmethod def __call__( @@ -311,25 +311,25 @@ def __call__( data: Data, width: int = 0, ) -> str: - '''Updates the widget providing the total width the widget must fill. + """Updates the widget providing the total width the widget must fill. progress - a reference to the calling ProgressBar width - The total width the widget must fill - ''' + """ class TimeSensitiveWidgetBase(WidgetBase, metaclass=abc.ABCMeta): - '''The base class for all time sensitive widgets. + """The base class for all time sensitive widgets. Some widgets like timers would become out of date unless updated at least every `INTERVAL` - ''' + """ INTERVAL = datetime.timedelta(milliseconds=100) class FormatLabel(FormatWidgetMixin, WidgetBase): - '''Displays a formatted label. + """Displays a formatted label. >>> label = FormatLabel('%(value)s', min_width=5, max_width=10) >>> class Progress: @@ -338,7 +338,7 @@ class FormatLabel(FormatWidgetMixin, WidgetBase): >>> str(label(Progress, dict(value='test'))) 'test :: test ' - ''' + """ mapping: ClassVar[types.Dict[str, types.Tuple[str, types.Any]]] = dict( finished=('end_time', None), @@ -371,7 +371,7 @@ def __call__( class Timer(FormatLabel, TimeSensitiveWidgetBase): - '''WidgetBase which displays the elapsed seconds.''' + """WidgetBase which displays the elapsed seconds.""" def __init__(self, format='Elapsed Time: %(elapsed)s', **kwargs): if '%s' in format and '%(elapsed)s' not in format: @@ -385,7 +385,7 @@ def __init__(self, format='Elapsed Time: %(elapsed)s', **kwargs): class SamplesMixin(TimeSensitiveWidgetBase, metaclass=abc.ABCMeta): - ''' + """ Mixing for widgets that average multiple measurements. Note that samples can be either an integer or a timedelta to indicate a @@ -414,7 +414,7 @@ class SamplesMixin(TimeSensitiveWidgetBase, metaclass=abc.ABCMeta): >>> samples(progress, None, True) == (datetime.timedelta(seconds=1), 0) True - ''' + """ def __init__( self, @@ -482,7 +482,7 @@ def __call__( class ETA(Timer): - '''WidgetBase which attempts to estimate the time of arrival.''' + """WidgetBase which attempts to estimate the time of arrival.""" def __init__( self, @@ -510,7 +510,7 @@ def _calculate_eta( value, elapsed, ): - '''Updates the widget to show the ETA or total time when finished.''' + """Updates the widget to show the ETA or total time when finished.""" if elapsed: # The max() prevents zero division errors per_item = elapsed.total_seconds() / max(value, 1e-6) @@ -526,7 +526,7 @@ def __call__( value=None, elapsed=None, ): - '''Updates the widget to show the ETA or total time when finished.''' + """Updates the widget to show the ETA or total time when finished.""" if value is None: value = data['value'] @@ -560,12 +560,12 @@ def __call__( fmt = self.format_NA else: fmt = self.format_zero -: + return Timer.__call__(self, progress, data, format=fmt) class AbsoluteETA(ETA): - '''Widget which attempts to estimate the absolute time of arrival.''' + """Widget which attempts to estimate the absolute time of arrival.""" def _calculate_eta( self, @@ -598,11 +598,11 @@ def __init__( class AdaptiveETA(ETA, SamplesMixin): - '''WidgetBase which attempts to estimate the time of arrival. + """WidgetBase which attempts to estimate the time of arrival. Uses a sampled average of the speed based on the 10 last updates. Very convenient for resuming the progress halfway. - ''' + """ exponential_smoothing: bool exponential_smoothing_factor: float @@ -639,14 +639,14 @@ def __call__( class SmoothingETA(ETA): - ''' + """ WidgetBase which attempts to estimate the time of arrival using an exponential moving average (EMA) of the speed. EMA applies more weight to recent data points and less to older ones, and doesn't require storing all past values. This approach works well with varying data points and smooths out fluctuations effectively. - ''' + """ smoothing_algorithm: algorithms.SmoothingAlgorithm smoothing_parameters: dict[str, float] @@ -683,12 +683,12 @@ def __call__( class DataSize(FormatWidgetMixin, WidgetBase): - ''' + """ Widget for showing an amount of data transferred/processed. Automatically formats the value (assumed to be a count of bytes) with an appropriate sized unit, based on the IEC binary prefixes (powers of 1024). - ''' + """ def __init__( self, @@ -724,9 +724,9 @@ def __call__( class FileTransferSpeed(FormatWidgetMixin, TimeSensitiveWidgetBase): - ''' + """ Widget for showing the current transfer speed (useful for file transfers). - ''' + """ def __init__( self, @@ -753,7 +753,7 @@ def __call__( value=None, total_seconds_elapsed=None, ): - '''Updates the widget with the current SI prefixed speed.''' + """Updates the widget with the current SI prefixed speed.""" if value is None: value = data['value'] @@ -791,7 +791,7 @@ def __call__( class AdaptiveTransferSpeed(FileTransferSpeed, SamplesMixin): - '''Widget for showing the transfer speed based on the last X samples.''' + """Widget for showing the transfer speed based on the last X samples.""" def __init__(self, **kwargs): FileTransferSpeed.__init__(self, **kwargs) @@ -814,9 +814,9 @@ def __call__( class AnimatedMarker(TimeSensitiveWidgetBase): - '''An animated marker for the progress bar which defaults to appear as if + """An animated marker for the progress bar which defaults to appear as if it were rotating. - ''' + """ def __init__( self, @@ -835,9 +835,9 @@ def __init__( WidgetBase.__init__(self, **kwargs) def __call__(self, progress: ProgressBarMixinBase, data: Data, width=None): - '''Updates the widget to show the next marker or the first marker when + """Updates the widget to show the next marker or the first marker when finished. - ''' + """ if progress.end_time: return self.default @@ -871,7 +871,7 @@ def __call__(self, progress: ProgressBarMixinBase, data: Data, width=None): class Counter(FormatWidgetMixin, WidgetBase): - '''Displays the current count.''' + """Displays the current count.""" def __init__(self, format='%(value)d', **kwargs): FormatWidgetMixin.__init__(self, format=format, **kwargs) @@ -903,7 +903,7 @@ class ColoredMixin: class Percentage(FormatWidgetMixin, ColoredMixin, WidgetBase): - '''Displays the current percentage as a number with a percent sign.''' + """Displays the current percentage as a number with a percent sign.""" def __init__(self, format='%(percentage)3d%%', na='N/A%%', **kwargs): self.na = na @@ -927,10 +927,14 @@ def get_format( class SimpleProgress(FormatWidgetMixin, ColoredMixin, WidgetBase): - '''Returns progress as a count of the total (e.g.: "5 of 47").''' + """Returns progress as a count of the total (e.g.: "5 of 47").""" max_width_cache: dict[ - types.Union[str, tuple[float, float | types.Type[base.UnknownLength]]], + str + | tuple[ + NumberT | types.Type[base.UnknownLength] | None, + NumberT | types.Type[base.UnknownLength] | None, + ], types.Optional[int], ] @@ -939,7 +943,9 @@ class SimpleProgress(FormatWidgetMixin, ColoredMixin, WidgetBase): def __init__(self, format=DEFAULT_FORMAT, **kwargs): FormatWidgetMixin.__init__(self, format=format, **kwargs) WidgetBase.__init__(self, format=format, **kwargs) - self.max_width_cache = dict(default=self.max_width or 0) + self.max_width_cache = dict() + # Pyright isn't happy when we set the key in the initialiser + self.max_width_cache['default'] = self.max_width or 0 def __call__( self, @@ -999,7 +1005,7 @@ def __call__( class Bar(AutoWidthWidgetBase): - '''A progress bar which stretches to fill the line.''' + """A progress bar which stretches to fill the line.""" fg: terminal.OptionalColor | None = colors.gradient bg: terminal.OptionalColor | None = None @@ -1014,7 +1020,7 @@ def __init__( marker_wrap=None, **kwargs, ): - '''Creates a customizable progress bar. + """Creates a customizable progress bar. The callable takes the same parameters as the `__call__` method @@ -1023,7 +1029,7 @@ def __init__( right - string or callable object to use as a right border fill - character to use for the empty part of the progress bar fill_left - whether to fill from the left or the right - ''' + """ self.marker = create_marker(marker, marker_wrap) self.left = string_or_lambda(left) self.right = string_or_lambda(right) @@ -1039,7 +1045,7 @@ def __call__( width: int = 0, color=True, ): - '''Updates the progress bar and its subcomponents.''' + """Updates the progress bar and its subcomponents.""" left = converters.to_unicode(self.left(progress, data, width)) right = converters.to_unicode(self.right(progress, data, width)) width -= progress.custom_len(left) + progress.custom_len(right) @@ -1061,7 +1067,7 @@ def __call__( class ReverseBar(Bar): - '''A bar which has a marker that goes from right to left.''' + """A bar which has a marker that goes from right to left.""" def __init__( self, @@ -1072,14 +1078,14 @@ def __init__( fill_left=False, **kwargs, ): - '''Creates a customizable progress bar. + """Creates a customizable progress bar. marker - string or updatable object to use as a marker left - string or updatable object to use as a left border right - string or updatable object to use as a right border fill - character to use for the empty part of the progress bar fill_left - whether to fill from the left or the right - ''' + """ Bar.__init__( self, marker=marker, @@ -1092,7 +1098,7 @@ def __init__( class BouncingBar(Bar, TimeSensitiveWidgetBase): - '''A bar which has a marker which bounces from side to side.''' + """A bar which has a marker which bounces from side to side.""" INTERVAL = datetime.timedelta(milliseconds=100) @@ -1103,7 +1109,7 @@ def __call__( width: int = 0, color=True, ): - '''Updates the progress bar and its subcomponents.''' + """Updates the progress bar and its subcomponents.""" left = converters.to_unicode(self.left(progress, data, width)) right = converters.to_unicode(self.right(progress, data, width)) width -= progress.custom_len(left) + progress.custom_len(right) @@ -1162,7 +1168,7 @@ def __call__( class VariableMixin: - '''Mixin to display a custom user variable.''' + """Mixin to display a custom user variable.""" def __init__(self, name, **kwargs): if not isinstance(name, str): @@ -1173,19 +1179,15 @@ def __init__(self, name, **kwargs): class MultiRangeBar(Bar, VariableMixin): - ''' + """ A bar with multiple sub-ranges, each represented by a different symbol. The various ranges are represented on a user-defined variable, formatted as .. code-block:: python - [ - ['Symbol1', amount1], - ['Symbol2', amount2], - ... - ] - ''' + [['Symbol1', amount1], ['Symbol2', amount2], ...] + """ def __init__(self, name, markers, **kwargs): VariableMixin.__init__(self, name) @@ -1202,7 +1204,7 @@ def __call__( width: int = 0, color=True, ): - '''Updates the progress bar and its subcomponents.''' + """Updates the progress bar and its subcomponents.""" left = converters.to_unicode(self.left(progress, data, width)) right = converters.to_unicode(self.right(progress, data, width)) width -= progress.custom_len(left) + progress.custom_len(right) @@ -1283,7 +1285,7 @@ class GranularMarkers: class GranularBar(AutoWidthWidgetBase): - '''A progressbar that can display progress at a sub-character granularity + """A progressbar that can display progress at a sub-character granularity by using multiple marker characters. Examples of markers: @@ -1296,7 +1298,7 @@ class GranularBar(AutoWidthWidgetBase): The markers can be accessed through GranularMarkers. GranularMarkers.dots for example - ''' + """ def __init__( self, @@ -1305,14 +1307,14 @@ def __init__( right='|', **kwargs, ): - '''Creates a customizable progress bar. + """Creates a customizable progress bar. markers - string of characters to use as granular progress markers. The first character should represent 0% and the last 100%. Ex: ` .oO`. left - string or callable object to use as a left border right - string or callable object to use as a right border - ''' + """ self.markers = markers self.left = string_or_lambda(left) self.right = string_or_lambda(right) @@ -1330,11 +1332,9 @@ def __call__( width -= progress.custom_len(left) + progress.custom_len(right) max_value = progress.max_value - # mypy doesn't get that the first part of the if statement makes sure - # we get the correct type if ( max_value is not base.UnknownLength - and max_value > 0 # type: ignore + and typing.cast(float, max_value) > 0 ): percent = progress.value / max_value # type: ignore else: @@ -1357,7 +1357,7 @@ def __call__( class FormatLabelBar(FormatLabel, Bar): - '''A bar which has a formatted label in the center.''' + """A bar which has a formatted label in the center.""" def __init__(self, format, **kwargs): FormatLabel.__init__(self, format, **kwargs) @@ -1395,7 +1395,7 @@ def __call__( # type: ignore class PercentageLabelBar(Percentage, FormatLabelBar): - '''A bar which displays the current percentage in the center.''' + """A bar which displays the current percentage in the center.""" # %3d adds an extra space that makes it look off-center # %2d keeps the label somewhat consistently in-place @@ -1414,7 +1414,7 @@ def __call__( # type: ignore class Variable(FormatWidgetMixin, VariableMixin, WidgetBase): - '''Displays a custom variable.''' + """Displays a custom variable.""" def __init__( self, @@ -1424,7 +1424,7 @@ def __init__( precision=3, **kwargs, ): - '''Creates a Variable associated with the given name.''' + """Creates a Variable associated with the given name.""" self.format = format self.width = width self.precision = precision @@ -1462,11 +1462,11 @@ def __call__( class DynamicMessage(Variable): - '''Kept for backwards compatibility, please use `Variable` instead.''' + """Kept for backwards compatibility, please use `Variable` instead.""" class CurrentTime(FormatWidgetMixin, TimeSensitiveWidgetBase): - '''Widget which displays the current (date)time with seconds resolution.''' + """Widget which displays the current (date)time with seconds resolution.""" INTERVAL = datetime.timedelta(seconds=1) @@ -1503,7 +1503,7 @@ def current_time(self): class JobStatusBar(Bar, VariableMixin): - ''' + """ Widget which displays the job status as markers on the bar. The status updates can be given either as a boolean or as a string. If it's @@ -1523,7 +1523,7 @@ class JobStatusBar(Bar, VariableMixin): failure_fg_color: The foreground color to use for failed jobs. failure_bg_color: The background color to use for failed jobs. failure_marker: The marker to use for failed jobs. - ''' + """ success_fg_color: terminal.Color | None = colors.green success_bg_color: terminal.Color | None = None diff --git a/pyproject.toml b/pyproject.toml index 321bdfc..c569a2a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -188,5 +188,30 @@ exclude_lines = [ include= ['progressbar'] exclude= ['examples'] ignore= ['docs'] +#strict = [ +# 'progressbar/algorithms.py', +# 'progressbar/env.py', +# 'progressbar/shortcuts.py', +## 'progressbar/multi.py', +## 'progressbar/__init__.py', +# 'progressbar/terminal/__init__.py', +## 'progressbar/terminal/stream.py', +# 'progressbar/terminal/os_specific/__init__.py', +# 'progressbar/terminal/os_specific/posix.py', +## 'progressbar/terminal/os_specific/windows.py', +## 'progressbar/terminal/base.py', +## 'progressbar/terminal/colors.py', +## 'progressbar/widgets.py', +## 'progressbar/utils.py', +# 'progressbar/__about__.py', +## 'progressbar/bar.py', +# 'progressbar/__main__.py', +# 'progressbar/base.py', +#] reportIncompatibleMethodOverride = false +reportUnnecessaryIsInstance = false +reportUnnecessaryCast = false +reportUnnecessaryTypeAssertion = false +reportUnnecessaryComparison = false +reportUnnecessaryContains = false diff --git a/ruff.toml b/ruff.toml index bd1b288..f6efc61 100644 --- a/ruff.toml +++ b/ruff.toml @@ -3,7 +3,12 @@ target-version = 'py38' -src = ['progressbar'] +#src = ['progressbar'] +exclude = [ + '.venv', + '.tox', + 'test.py', +] lint.ignore = [ 'A001', # Variable {name} is shadowing a Python builtin @@ -27,7 +32,7 @@ lint.ignore = [ 'ISC001', # String concatenation with implicit str conversion 'SIM108', # Ternary operators are not always more readable ] -line-length = 80 +line-length = 79 lint.select = [ 'A', # flake8-builtins 'ASYNC', # flake8 async checker diff --git a/tests/conftest.py b/tests/conftest.py index 6a53b80..787e643 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,13 +1,16 @@ +from __future__ import annotations + import logging import time import timeit from datetime import datetime import freezegun -import progressbar import pytest -LOG_LEVELS = { +import progressbar + +LOG_LEVELS: dict[str, int] = { '0': logging.ERROR, '1': logging.WARNING, '2': logging.INFO, @@ -15,14 +18,14 @@ } -def pytest_configure(config): +def pytest_configure(config) -> None: logging.basicConfig( level=LOG_LEVELS.get(config.option.verbose, logging.DEBUG), ) @pytest.fixture(autouse=True) -def small_interval(monkeypatch): +def small_interval(monkeypatch) -> None: # Remove the update limit for tests by default monkeypatch.setattr( progressbar.ProgressBar, diff --git a/tests/original_examples.py b/tests/original_examples.py index b9ba57e..7f1db16 100644 --- a/tests/original_examples.py +++ b/tests/original_examples.py @@ -43,7 +43,7 @@ def wrapped(): @example -def example0(): +def example0() -> None: pbar = ProgressBar(widgets=[Percentage(), Bar()], maxval=300).start() for i in range(300): time.sleep(0.01) @@ -52,7 +52,7 @@ def example0(): @example -def example1(): +def example1() -> None: widgets = [ 'Test: ', Percentage(), @@ -71,7 +71,7 @@ def example1(): @example -def example2(): +def example2() -> None: class CrazyFileTransferSpeed(FileTransferSpeed): """It's bigger between 45 and 80 percent.""" @@ -100,7 +100,7 @@ def update(self, pbar): @example -def example3(): +def example3() -> None: widgets = [Bar('>'), ' ', ETA(), ' ', ReverseBar('<')] pbar = ProgressBar(widgets=widgets, maxval=10000).start() for i in range(1000): @@ -110,7 +110,7 @@ def example3(): @example -def example4(): +def example4() -> None: widgets = [ 'Test: ', Percentage(), @@ -130,7 +130,7 @@ def example4(): @example -def example5(): +def example5() -> None: pbar = ProgressBar(widgets=[SimpleProgress()], maxval=17).start() for i in range(17): time.sleep(0.2) @@ -139,7 +139,7 @@ def example5(): @example -def example6(): +def example6() -> None: pbar = ProgressBar().start() for i in range(100): time.sleep(0.01) @@ -148,28 +148,28 @@ def example6(): @example -def example7(): +def example7() -> None: pbar = ProgressBar() # Progressbar can guess maxval automatically. for _i in pbar(range(80)): time.sleep(0.01) @example -def example8(): +def example8() -> None: pbar = ProgressBar(maxval=80) # Progressbar can't guess maxval. for _i in pbar(i for i in range(80)): time.sleep(0.01) @example -def example9(): +def example9() -> None: pbar = ProgressBar(widgets=['Working: ', AnimatedMarker()]) for _i in pbar(i for i in range(50)): time.sleep(0.08) @example -def example10(): +def example10() -> None: widgets = ['Processed: ', Counter(), ' lines (', Timer(), ')'] pbar = ProgressBar(widgets=widgets) for _i in pbar(i for i in range(150)): @@ -177,7 +177,7 @@ def example10(): @example -def example11(): +def example11() -> None: widgets = [FormatLabel('Processed: %(value)d lines (in: %(elapsed)s)')] pbar = ProgressBar(widgets=widgets) for _i in pbar(i for i in range(150)): @@ -185,7 +185,7 @@ def example11(): @example -def example12(): +def example12() -> None: widgets = ['Balloon: ', AnimatedMarker(markers='.oO@* ')] pbar = ProgressBar(widgets=widgets) for _i in pbar(i for i in range(24)): @@ -193,7 +193,7 @@ def example12(): @example -def example13(): +def example13() -> None: # You may need python 3.x to see this correctly try: widgets = ['Arrows: ', AnimatedMarker(markers='←↖↑↗→↘↓↙')] @@ -205,7 +205,7 @@ def example13(): @example -def example14(): +def example14() -> None: # You may need python 3.x to see this correctly try: widgets = ['Arrows: ', AnimatedMarker(markers='◢◣◤◥')] @@ -217,7 +217,7 @@ def example14(): @example -def example15(): +def example15() -> None: # You may need python 3.x to see this correctly try: widgets = ['Wheels: ', AnimatedMarker(markers='◐◓◑◒')] @@ -229,7 +229,7 @@ def example15(): @example -def example16(): +def example16() -> None: widgets = [FormatLabel('Bouncer: value %(value)d - '), BouncingBar()] pbar = ProgressBar(widgets=widgets) for _i in pbar(i for i in range(180)): @@ -237,7 +237,7 @@ def example16(): @example -def example17(): +def example17() -> None: widgets = [ FormatLabel('Animated Bouncer: value %(value)d - '), BouncingBar(marker=RotatingMarker()), @@ -249,7 +249,7 @@ def example17(): @example -def example18(): +def example18() -> None: widgets = [Percentage(), ' ', Bar(), ' ', ETA(), ' ', AdaptiveETA()] pbar = ProgressBar(widgets=widgets, maxval=500) pbar.start() @@ -260,7 +260,7 @@ def example18(): @example -def example19(): +def example19() -> None: pbar = ProgressBar() for _i in pbar([]): pass @@ -268,8 +268,8 @@ def example19(): @example -def example20(): - '''Widgets that behave differently when length is unknown''' +def example20() -> None: + """Widgets that behave differently when length is unknown""" widgets = [ '[When length is unknown at first]', ' Progress: ', diff --git a/tests/test_algorithms.py b/tests/test_algorithms.py index 85027ce..a6cc646 100644 --- a/tests/test_algorithms.py +++ b/tests/test_algorithms.py @@ -1,47 +1,58 @@ from datetime import timedelta import pytest + from progressbar import algorithms -def test_ema_initialization(): +def test_ema_initialization() -> None: ema = algorithms.ExponentialMovingAverage() assert ema.alpha == 0.5 assert ema.value == 0 -@pytest.mark.parametrize('alpha, new_value, expected', [ - (0.5, 10, 5), - (0.1, 20, 2), - (0.9, 30, 27), - (0.3, 15, 4.5), - (0.7, 40, 28), - (0.5, 0, 0), - (0.2, 100, 20), - (0.8, 50, 40), -]) -def test_ema_update(alpha, new_value, expected): + +@pytest.mark.parametrize( + 'alpha, new_value, expected', + [ + (0.5, 10, 5), + (0.1, 20, 2), + (0.9, 30, 27), + (0.3, 15, 4.5), + (0.7, 40, 28), + (0.5, 0, 0), + (0.2, 100, 20), + (0.8, 50, 40), + ], +) +def test_ema_update(alpha, new_value: float, expected) -> None: ema = algorithms.ExponentialMovingAverage(alpha) result = ema.update(new_value, timedelta(seconds=1)) assert result == expected -def test_dema_initialization(): + +def test_dema_initialization() -> None: dema = algorithms.DoubleExponentialMovingAverage() assert dema.alpha == 0.5 assert dema.ema1 == 0 assert dema.ema2 == 0 -@pytest.mark.parametrize('alpha, new_value, expected', [ - (0.5, 10, 7.5), - (0.1, 20, 3.8), - (0.9, 30, 29.7), - (0.3, 15, 7.65), - (0.5, 0, 0), - (0.2, 100, 36.0), - (0.8, 50, 48.0), -]) -def test_dema_update(alpha, new_value, expected): + +@pytest.mark.parametrize( + 'alpha, new_value, expected', + [ + (0.5, 10, 7.5), + (0.1, 20, 3.8), + (0.9, 30, 29.7), + (0.3, 15, 7.65), + (0.5, 0, 0), + (0.2, 100, 36.0), + (0.8, 50, 48.0), + ], +) +def test_dema_update(alpha, new_value: float, expected) -> None: dema = algorithms.DoubleExponentialMovingAverage(alpha) result = dema.update(new_value, timedelta(seconds=1)) assert result == expected + # Additional test functions can be added here as needed. diff --git a/tests/test_backwards_compatibility.py b/tests/test_backwards_compatibility.py index 1f9a7a6..204a674 100644 --- a/tests/test_backwards_compatibility.py +++ b/tests/test_backwards_compatibility.py @@ -3,7 +3,7 @@ import progressbar -def test_progressbar_1_widgets(): +def test_progressbar_1_widgets() -> None: widgets = [ progressbar.AdaptiveETA(format='Time left: %s'), progressbar.Timer(format='Time passed: %s'), diff --git a/tests/test_color.py b/tests/test_color.py index dc7c2bb..90b9b1b 100644 --- a/tests/test_color.py +++ b/tests/test_color.py @@ -1,11 +1,12 @@ from __future__ import annotations import os -import typing +from typing import ClassVar + +import pytest import progressbar import progressbar.terminal -import pytest from progressbar import env, terminal, widgets from progressbar.terminal import Colors, apply_colors, colors @@ -21,7 +22,7 @@ @pytest.fixture(autouse=True) -def clear_env(monkeypatch: pytest.MonkeyPatch): +def clear_env(monkeypatch: pytest.MonkeyPatch) -> None: # Clear all environment variables that might affect the tests for variable in ENVIRONMENT_VARIABLES: monkeypatch.delenv(variable, raising=False) @@ -36,8 +37,10 @@ def clear_env(monkeypatch: pytest.MonkeyPatch): 'FORCE_COLOR', ], ) -def test_color_environment_variables(monkeypatch: pytest.MonkeyPatch, - variable): +def test_color_environment_variables( + monkeypatch: pytest.MonkeyPatch, + variable: str, +) -> None: if os.name == 'nt': # Windows has special handling so we need to disable that to make the # tests work properly @@ -84,7 +87,7 @@ def test_color_environment_variables(monkeypatch: pytest.MonkeyPatch, 'xterm', ], ) -def test_color_support_from_env(monkeypatch, variable, value): +def test_color_support_from_env(monkeypatch, variable, value) -> None: if os.name == 'nt': # Windows has special handling so we need to disable that to make the # tests work properly @@ -101,7 +104,7 @@ def test_color_support_from_env(monkeypatch, variable, value): 'JUPYTER_LINES', ], ) -def test_color_support_from_env_jupyter(monkeypatch, variable): +def test_color_support_from_env_jupyter(monkeypatch, variable) -> None: monkeypatch.setattr(env, 'JUPYTER', True) assert env.ColorSupport.from_env() == env.ColorSupport.XTERM_TRUECOLOR @@ -113,7 +116,7 @@ def test_color_support_from_env_jupyter(monkeypatch, variable): assert env.ColorSupport.from_env() == env.ColorSupport.NONE -def test_enable_colors_flags(): +def test_enable_colors_flags() -> None: bar = progressbar.ProgressBar(enable_colors=True) assert bar.enable_colors @@ -130,26 +133,24 @@ def test_enable_colors_flags(): class _TestFixedColorSupport(progressbar.widgets.WidgetBase): - _fixed_colors: typing.ClassVar[ - widgets.TFixedColors - ] = widgets.TFixedColors( + _fixed_colors: ClassVar[widgets.TFixedColors] = widgets.TFixedColors( fg_none=progressbar.widgets.colors.yellow, bg_none=None, ) - def __call__(self, *args, **kwargs): + def __call__(self, *args, **kwargs) -> None: pass class _TestFixedGradientSupport(progressbar.widgets.WidgetBase): - _gradient_colors: typing.ClassVar[ - widgets.TGradientColors - ] = widgets.TGradientColors( - fg=progressbar.widgets.colors.gradient, - bg=None, + _gradient_colors: ClassVar[widgets.TGradientColors] = ( + widgets.TGradientColors( + fg=progressbar.widgets.colors.gradient, + bg=None, + ) ) - def __call__(self, *args, **kwargs): + def __call__(self, *args, **kwargs) -> None: pass @@ -162,12 +163,12 @@ def __call__(self, *args, **kwargs): _TestFixedGradientSupport, ], ) -def test_color_widgets(widget): +def test_color_widgets(widget) -> None: assert widget().uses_colors print(f'{widget} has colors? {widget.uses_colors}') -def test_color_gradient(): +def test_color_gradient() -> None: gradient = terminal.ColorGradient(colors.red) assert gradient.get_color(0) == gradient.get_color(-1) assert gradient.get_color(1) == gradient.get_color(2) @@ -181,7 +182,9 @@ def test_color_gradient(): assert gradient.get_color(0.5) != colors.yellow gradient = terminal.ColorGradient( - colors.red, colors.yellow, interpolate=False, + colors.red, + colors.yellow, + interpolate=False, ) assert gradient.get_color(0) == colors.red assert gradient.get_color(1) == colors.yellow @@ -194,7 +197,7 @@ def test_color_gradient(): progressbar.Counter, ], ) -def test_no_color_widgets(widget): +def test_no_color_widgets(widget) -> None: assert not widget().uses_colors print(f'{widget} has colors? {widget.uses_colors}') @@ -206,7 +209,7 @@ def test_no_color_widgets(widget): ).uses_colors -def test_colors(monkeypatch): +def test_colors(monkeypatch) -> None: for colors_ in Colors.by_rgb.values(): for color in colors_: rgb = color.rgb @@ -217,9 +220,9 @@ def test_colors(monkeypatch): assert rgb.to_windows is not None with monkeypatch.context() as context: - context.setattr(env,'COLOR_SUPPORT', env.ColorSupport.XTERM) + context.setattr(env, 'COLOR_SUPPORT', env.ColorSupport.XTERM) assert color.underline - context.setattr(env,'COLOR_SUPPORT', env.ColorSupport.WINDOWS) + context.setattr(env, 'COLOR_SUPPORT', env.ColorSupport.WINDOWS) assert color.underline assert color.fg @@ -229,7 +232,7 @@ def test_colors(monkeypatch): assert color('test') -def test_color(): +def test_color() -> None: color = colors.red if os.name != 'nt': assert color('x') == color.fg('x') != 'x' @@ -261,7 +264,7 @@ def test_color(): (terminal.RGB(192, 192, 192), terminal.HSL(0, 0, 75)), ], ) -def test_rgb_to_hls(rgb, hls): +def test_rgb_to_hls(rgb, hls) -> None: assert terminal.HSL.from_rgb(rgb) == hls @@ -271,103 +274,111 @@ def test_rgb_to_hls(rgb, hls): ('test', None, None, None, None, None, 'test'), ('test', None, None, None, None, 1, 'test'), ( - 'test', - None, - None, - None, - colors.red, - None, - '\x1b[48;5;9mtest\x1b[49m', + 'test', + None, + None, + None, + colors.red, + None, + '\x1b[48;5;9mtest\x1b[49m', ), ( - 'test', - None, - colors.green, - None, - colors.red, - None, - '\x1b[48;5;9mtest\x1b[49m', + 'test', + None, + colors.green, + None, + colors.red, + None, + '\x1b[48;5;9mtest\x1b[49m', ), ('test', None, colors.red, None, None, 1, '\x1b[48;5;9mtest\x1b[49m'), ('test', None, colors.red, None, None, None, 'test'), ( - 'test', - colors.green, - None, - colors.red, - None, - None, - '\x1b[38;5;9mtest\x1b[39m', + 'test', + colors.green, + None, + colors.red, + None, + None, + '\x1b[38;5;9mtest\x1b[39m', ), ( - 'test', - colors.green, - colors.red, - None, - None, - 1, - '\x1b[48;5;9m\x1b[38;5;2mtest\x1b[39m\x1b[49m', + 'test', + colors.green, + colors.red, + None, + None, + 1, + '\x1b[48;5;9m\x1b[38;5;2mtest\x1b[39m\x1b[49m', ), ('test', colors.red, None, None, None, 1, '\x1b[38;5;9mtest\x1b[39m'), ('test', colors.red, None, None, None, None, 'test'), ('test', colors.red, colors.red, None, None, None, 'test'), ( - 'test', - colors.red, - colors.yellow, - None, - None, - 1, - '\x1b[48;5;11m\x1b[38;5;9mtest\x1b[39m\x1b[49m', + 'test', + colors.red, + colors.yellow, + None, + None, + 1, + '\x1b[48;5;11m\x1b[38;5;9mtest\x1b[39m\x1b[49m', ), ( - 'test', - colors.red, - colors.yellow, - None, - None, - 1, - '\x1b[48;5;11m\x1b[38;5;9mtest\x1b[39m\x1b[49m', + 'test', + colors.red, + colors.yellow, + None, + None, + 1, + '\x1b[48;5;11m\x1b[38;5;9mtest\x1b[39m\x1b[49m', ), ], ) -def test_apply_colors(text, fg, bg, fg_none, bg_none, percentage, expected, - monkeypatch): +def test_apply_colors( + text: str, + fg, + bg, + fg_none, + bg_none, + percentage: float | None, + expected, + monkeypatch, +) -> None: monkeypatch.setattr( env, 'COLOR_SUPPORT', env.ColorSupport.XTERM_256, ) assert ( - apply_colors( - text, - fg=fg, - bg=bg, - fg_none=fg_none, - bg_none=bg_none, - percentage=percentage, - ) - == expected + apply_colors( + text, + fg=fg, + bg=bg, + fg_none=fg_none, + bg_none=bg_none, + percentage=percentage, + ) + == expected ) -def test_windows_colors(monkeypatch): +def test_windows_colors(monkeypatch) -> None: monkeypatch.setattr(env, 'COLOR_SUPPORT', env.ColorSupport.WINDOWS) assert ( - apply_colors( - 'test', - fg=colors.red, - bg=colors.red, - fg_none=colors.red, - bg_none=colors.red, - percentage=1, - ) - == 'test' + apply_colors( + 'test', + fg=colors.red, + bg=colors.red, + fg_none=colors.red, + bg_none=colors.red, + percentage=1, + ) + == 'test' ) colors.red.underline('test') -def test_ansi_color(monkeypatch): +def test_ansi_color(monkeypatch) -> None: color = progressbar.terminal.Color( colors.red.rgb, colors.red.hls, @@ -389,5 +400,5 @@ def test_ansi_color(monkeypatch): assert color.ansi is not None or color_support == env.ColorSupport.NONE -def test_sgr_call(): +def test_sgr_call() -> None: assert progressbar.terminal.encircled('test') == '\x1b[52mtest\x1b[54m' diff --git a/tests/test_custom_widgets.py b/tests/test_custom_widgets.py index 477aef3..b0a272e 100644 --- a/tests/test_custom_widgets.py +++ b/tests/test_custom_widgets.py @@ -1,8 +1,9 @@ import time -import progressbar import pytest +import progressbar + class CrazyFileTransferSpeed(progressbar.FileTransferSpeed): "It's bigger between 45 and 80 percent" @@ -15,7 +16,7 @@ def update(self, pbar): return progressbar.FileTransferSpeed.update(self, pbar) -def test_crazy_file_transfer_speed_widget(): +def test_crazy_file_transfer_speed_widget() -> None: widgets = [ # CrazyFileTransferSpeed(), ' <<<', @@ -36,7 +37,7 @@ def test_crazy_file_transfer_speed_widget(): p.finish() -def test_variable_widget_widget(): +def test_variable_widget_widget() -> None: widgets = [ ' [', progressbar.Timer(), @@ -74,7 +75,7 @@ def test_variable_widget_widget(): p.finish() -def test_format_custom_text_widget(): +def test_format_custom_text_widget() -> None: widget = progressbar.FormatCustomText( 'Spam: %(spam).1f kg, eggs: %(eggs)d', dict( diff --git a/tests/test_data.py b/tests/test_data.py index ef6f5a3..4307163 100644 --- a/tests/test_data.py +++ b/tests/test_data.py @@ -1,6 +1,7 @@ -import progressbar import pytest +import progressbar + @pytest.mark.parametrize( 'value,expected', @@ -19,6 +20,6 @@ (2**90, '1024.0 YiB'), ], ) -def test_data_size(value, expected): +def test_data_size(value, expected) -> None: widget = progressbar.DataSize() assert widget(None, dict(value=value)) == expected diff --git a/tests/test_data_transfer_bar.py b/tests/test_data_transfer_bar.py index 495d6b7..7e5cfcc 100644 --- a/tests/test_data_transfer_bar.py +++ b/tests/test_data_transfer_bar.py @@ -2,14 +2,14 @@ from progressbar import DataTransferBar -def test_known_length(): +def test_known_length() -> None: dtb = DataTransferBar().start(max_value=50) for i in range(50): dtb.update(i) dtb.finish() -def test_unknown_length(): +def test_unknown_length() -> None: dtb = DataTransferBar().start(max_value=progressbar.UnknownLength) for i in range(50): dtb.update(i) diff --git a/tests/test_dill_pickle.py b/tests/test_dill_pickle.py index 8131a35..3731245 100644 --- a/tests/test_dill_pickle.py +++ b/tests/test_dill_pickle.py @@ -1,8 +1,9 @@ import dill # type: ignore + import progressbar -def test_dill(): +def test_dill() -> None: bar = progressbar.ProgressBar() assert bar._started is False assert bar._finished is False diff --git a/tests/test_empty.py b/tests/test_empty.py index ad0a430..f326e38 100644 --- a/tests/test_empty.py +++ b/tests/test_empty.py @@ -1,11 +1,11 @@ import progressbar -def test_empty_list(): +def test_empty_list() -> None: for x in progressbar.ProgressBar()([]): print(x) -def test_empty_iterator(): +def test_empty_iterator() -> None: for x in progressbar.ProgressBar(max_value=0)(iter([])): print(x) diff --git a/tests/test_end.py b/tests/test_end.py index e5af3f6..e7c69e3 100644 --- a/tests/test_end.py +++ b/tests/test_end.py @@ -1,9 +1,10 @@ -import progressbar import pytest +import progressbar + @pytest.fixture(autouse=True) -def large_interval(monkeypatch): +def large_interval(monkeypatch) -> None: # Remove the update limit for tests by default monkeypatch.setattr( progressbar.ProgressBar, @@ -12,7 +13,7 @@ def large_interval(monkeypatch): ) -def test_end(): +def test_end() -> None: m = 24514315 p = progressbar.ProgressBar( widgets=[progressbar.Percentage(), progressbar.Bar()], @@ -33,7 +34,7 @@ def test_end(): assert p.value == m -def test_end_100(monkeypatch): +def test_end_100(monkeypatch) -> None: assert progressbar.ProgressBar._MINIMUM_UPDATE_INTERVAL == 0.1 p = progressbar.ProgressBar( widgets=[progressbar.Percentage(), progressbar.Bar()], diff --git a/tests/test_failure.py b/tests/test_failure.py index 4c10546..5953284 100644 --- a/tests/test_failure.py +++ b/tests/test_failure.py @@ -1,11 +1,12 @@ import logging import time -import progressbar import pytest +import progressbar + -def test_missing_format_values(caplog): +def test_missing_format_values(caplog) -> None: caplog.set_level(logging.CRITICAL, logger='progressbar.widgets') with pytest.raises(KeyError): p = progressbar.ProgressBar( @@ -14,13 +15,13 @@ def test_missing_format_values(caplog): p.update(5) -def test_max_smaller_than_min(): +def test_max_smaller_than_min() -> None: with pytest.raises(ValueError): progressbar.ProgressBar(min_value=10, max_value=5) -def test_no_max_value(): - '''Looping up to 5 without max_value? No problem''' +def test_no_max_value() -> None: + """Looping up to 5 without max_value? No problem""" p = progressbar.ProgressBar() p.start() for i in range(5): @@ -28,24 +29,24 @@ def test_no_max_value(): p.update(i) -def test_correct_max_value(): - '''Looping up to 5 when max_value is 10? No problem''' +def test_correct_max_value() -> None: + """Looping up to 5 when max_value is 10? No problem""" p = progressbar.ProgressBar(max_value=10) for i in range(5): time.sleep(1) p.update(i) -def test_minus_max_value(): - '''negative max_value, shouldn't work''' +def test_minus_max_value() -> None: + """negative max_value, shouldn't work""" p = progressbar.ProgressBar(min_value=-2, max_value=-1) with pytest.raises(ValueError): p.update(-1) -def test_zero_max_value(): - '''max_value of zero, it could happen''' +def test_zero_max_value() -> None: + """max_value of zero, it could happen""" p = progressbar.ProgressBar(max_value=0) p.update(0) @@ -53,8 +54,8 @@ def test_zero_max_value(): p.update(1) -def test_one_max_value(): - '''max_value of one, another corner case''' +def test_one_max_value() -> None: + """max_value of one, another corner case""" p = progressbar.ProgressBar(max_value=1) p.update(0) @@ -64,23 +65,23 @@ def test_one_max_value(): p.update(2) -def test_changing_max_value(): - '''Changing max_value? No problem''' +def test_changing_max_value() -> None: + """Changing max_value? No problem""" p = progressbar.ProgressBar(max_value=10)(range(20), max_value=20) for _i in p: time.sleep(1) -def test_backwards(): - '''progressbar going backwards''' +def test_backwards() -> None: + """progressbar going backwards""" p = progressbar.ProgressBar(max_value=1) p.update(1) p.update(0) -def test_incorrect_max_value(): - '''Looping up to 10 when max_value is 5? This is madness!''' +def test_incorrect_max_value() -> None: + """Looping up to 10 when max_value is 5? This is madness!""" p = progressbar.ProgressBar(max_value=5) for i in range(5): time.sleep(1) @@ -92,24 +93,24 @@ def test_incorrect_max_value(): p.update(i) -def test_deprecated_maxval(): +def test_deprecated_maxval() -> None: with pytest.warns(DeprecationWarning): progressbar.ProgressBar(maxval=5) -def test_deprecated_poll(): +def test_deprecated_poll() -> None: with pytest.warns(DeprecationWarning): progressbar.ProgressBar(poll=5) -def test_deprecated_currval(): +def test_deprecated_currval() -> None: with pytest.warns(DeprecationWarning): bar = progressbar.ProgressBar(max_value=5) bar.update(2) assert bar.currval == 2 -def test_unexpected_update_keyword_arg(): +def test_unexpected_update_keyword_arg() -> None: p = progressbar.ProgressBar(max_value=10) with pytest.raises(TypeError): for i in range(10): @@ -117,23 +118,23 @@ def test_unexpected_update_keyword_arg(): p.update(i, foo=10) -def test_variable_not_str(): +def test_variable_not_str() -> None: with pytest.raises(TypeError): progressbar.Variable(1) -def test_variable_too_many_strs(): +def test_variable_too_many_strs() -> None: with pytest.raises(ValueError): progressbar.Variable('too long') -def test_negative_value(): +def test_negative_value() -> None: bar = progressbar.ProgressBar(max_value=10) with pytest.raises(ValueError): bar.update(value=-1) -def test_increment(): +def test_increment() -> None: bar = progressbar.ProgressBar(max_value=10) bar.increment() del bar diff --git a/tests/test_flush.py b/tests/test_flush.py index 014b690..edade1c 100644 --- a/tests/test_flush.py +++ b/tests/test_flush.py @@ -3,8 +3,8 @@ import progressbar -def test_flush(): - '''Left justify using the terminal width''' +def test_flush() -> None: + """Left justify using the terminal width""" p = progressbar.ProgressBar(poll_interval=0.001) p.print('hello') diff --git a/tests/test_iterators.py b/tests/test_iterators.py index ba48661..d4474e7 100644 --- a/tests/test_iterators.py +++ b/tests/test_iterators.py @@ -1,25 +1,26 @@ import time -import progressbar import pytest +import progressbar + -def test_list(): - '''Progressbar can guess max_value automatically.''' +def test_list() -> None: + """Progressbar can guess max_value automatically.""" p = progressbar.ProgressBar() for _i in p(range(10)): time.sleep(0.001) -def test_iterator_with_max_value(): - '''Progressbar can't guess max_value.''' +def test_iterator_with_max_value() -> None: + """Progressbar can't guess max_value.""" p = progressbar.ProgressBar(max_value=10) for _i in p(iter(range(10))): time.sleep(0.001) -def test_iterator_without_max_value_error(): - '''Progressbar can't guess max_value.''' +def test_iterator_without_max_value_error() -> None: + """Progressbar can't guess max_value.""" p = progressbar.ProgressBar() for _i in p(iter(range(10))): @@ -28,8 +29,8 @@ def test_iterator_without_max_value_error(): assert p.max_value is progressbar.UnknownLength -def test_iterator_without_max_value(): - '''Progressbar can't guess max_value.''' +def test_iterator_without_max_value() -> None: + """Progressbar can't guess max_value.""" p = progressbar.ProgressBar( widgets=[ progressbar.AnimatedMarker(), @@ -42,15 +43,15 @@ def test_iterator_without_max_value(): time.sleep(0.001) -def test_iterator_with_incorrect_max_value(): - '''Progressbar can't guess max_value.''' +def test_iterator_with_incorrect_max_value() -> None: + """Progressbar can't guess max_value.""" p = progressbar.ProgressBar(max_value=10) with pytest.raises(ValueError): for _i in p(iter(range(20))): time.sleep(0.001) -def test_adding_value(): +def test_adding_value() -> None: p = progressbar.ProgressBar(max_value=10) p.start() p.update(5) diff --git a/tests/test_job_status.py b/tests/test_job_status.py index f22484f..778b6ce 100644 --- a/tests/test_job_status.py +++ b/tests/test_job_status.py @@ -1,19 +1,22 @@ import time -import progressbar import pytest +import progressbar + -@pytest.mark.parametrize('status', [ - True, - False, - None, -]) -def test_status(status): +@pytest.mark.parametrize( + 'status', + [ + True, + False, + None, + ], +) +def test_status(status) -> None: with progressbar.ProgressBar( - widgets=[progressbar.widgets.JobStatusBar('status')], - ) as bar: + widgets=[progressbar.widgets.JobStatusBar('status')], + ) as bar: for _ in range(5): bar.increment(status=status, force=True) time.sleep(0.1) - diff --git a/tests/test_large_values.py b/tests/test_large_values.py index f251c32..2e7ad72 100644 --- a/tests/test_large_values.py +++ b/tests/test_large_values.py @@ -3,14 +3,14 @@ import progressbar -def test_large_max_value(): +def test_large_max_value() -> None: with progressbar.ProgressBar(max_value=1e10) as bar: for i in range(10): bar.update(i) time.sleep(0.1) -def test_value_beyond_max_value(): +def test_value_beyond_max_value() -> None: with progressbar.ProgressBar(max_value=10, max_error=False) as bar: for i in range(20): bar.update(i) diff --git a/tests/test_misc.py b/tests/test_misc.py index c07002f..b0725af 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -1,7 +1,7 @@ from progressbar import __about__ -def test_about(): +def test_about() -> None: assert __about__.__title__ assert __about__.__package_name__ assert __about__.__author__ diff --git a/tests/test_monitor_progress.py b/tests/test_monitor_progress.py index 0769391..4f99df9 100644 --- a/tests/test_monitor_progress.py +++ b/tests/test_monitor_progress.py @@ -1,3 +1,6 @@ +from __future__ import annotations + +# fmt: off import os import pprint @@ -5,7 +8,7 @@ pytest_plugins = 'pytester' -SCRIPT = ''' +SCRIPT = """ import sys sys.path.append({progressbar_path!r}) import time @@ -20,7 +23,7 @@ bar._MINIMUM_UPDATE_INTERVAL = 1e-9 for i in bar({items}): {loop_code} -''' +""" def _non_empty_lines(lines): @@ -29,11 +32,11 @@ def _non_empty_lines(lines): def _create_script( widgets=None, - items=None, - loop_code='fake_time.tick(1)', - term_width=60, + items: list[int] | None=None, + loop_code: str='fake_time.tick(1)', + term_width: int=60, **kwargs, -): +) -> str: if items is None: items = list(range(9)) kwargs['term_width'] = term_width @@ -62,12 +65,12 @@ def _create_script( return script -def test_list_example(testdir): - '''Run the simple example code in a python subprocess and then compare its +def test_list_example(testdir) -> None: + """Run the simple example code in a python subprocess and then compare its stderr to what we expect to see from it. We run it in a subprocess to best capture its stderr. We expect to see match_lines in order in the output. This test is just a sanity check to ensure that the progress - bar progresses from 1 to 10, it does not make sure that the''' + bar progresses from 1 to 10, it does not make sure that the""" result = testdir.runpython( testdir.makepyfile( @@ -94,12 +97,12 @@ def test_list_example(testdir): ]) -def test_generator_example(testdir): - '''Run the simple example code in a python subprocess and then compare its +def test_generator_example(testdir) -> None: + """Run the simple example code in a python subprocess and then compare its stderr to what we expect to see from it. We run it in a subprocess to best capture its stderr. We expect to see match_lines in order in the output. This test is just a sanity check to ensure that the progress - bar progresses from 1 to 10, it does not make sure that the''' + bar progresses from 1 to 10, it does not make sure that the""" result = testdir.runpython( testdir.makepyfile( _create_script( @@ -117,44 +120,45 @@ def test_generator_example(testdir): result.stderr.re_match_lines(lines) -def test_rapid_updates(testdir): - '''Run some example code that updates 10 times, then sleeps .1 seconds, +def test_rapid_updates(testdir) -> None: + """Run some example code that updates 10 times, then sleeps .1 seconds, this is meant to test that the progressbar progresses normally with - this sample code, since there were issues with it in the past''' + this sample code, since there were issues with it in the past""" result = testdir.runpython( testdir.makepyfile( _create_script( term_width=60, items=list(range(10)), - loop_code=''' + loop_code=""" if i < 5: fake_time.tick(1) else: fake_time.tick(2) - ''', + """, ), ), ) result.stderr.lines = _non_empty_lines(result.stderr.lines) pprint.pprint(result.stderr.lines, width=70) - result.stderr.fnmatch_lines([ - ' 0% (0 of 10) | | Elapsed Time: 0:00:00 ETA: --:--:--', - ' 10% (1 of 10) | | Elapsed Time: 0:00:01 ETA: 0:00:09', - ' 20% (2 of 10) |# | Elapsed Time: 0:00:02 ETA: 0:00:08', - ' 30% (3 of 10) |# | Elapsed Time: 0:00:03 ETA: 0:00:07', - ' 40% (4 of 10) |## | Elapsed Time: 0:00:04 ETA: 0:00:06', - ' 50% (5 of 10) |### | Elapsed Time: 0:00:05 ETA: 0:00:05', - ' 60% (6 of 10) |### | Elapsed Time: 0:00:07 ETA: 0:00:04', - ' 70% (7 of 10) |#### | Elapsed Time: 0:00:09 ETA: 0:00:03', - ' 80% (8 of 10) |#### | Elapsed Time: 0:00:11 ETA: 0:00:02', - ' 90% (9 of 10) |##### | Elapsed Time: 0:00:13 ETA: 0:00:01', - '100% (10 of 10) |#####| Elapsed Time: 0:00:15 Time: 0:00:15', - ], + result.stderr.fnmatch_lines( + [ + ' 0% (0 of 10) | | Elapsed Time: 0:00:00 ETA: --:--:--', + ' 10% (1 of 10) | | Elapsed Time: 0:00:01 ETA: 0:00:09', + ' 20% (2 of 10) |# | Elapsed Time: 0:00:02 ETA: 0:00:08', + ' 30% (3 of 10) |# | Elapsed Time: 0:00:03 ETA: 0:00:07', + ' 40% (4 of 10) |## | Elapsed Time: 0:00:04 ETA: 0:00:06', + ' 50% (5 of 10) |### | Elapsed Time: 0:00:05 ETA: 0:00:05', + ' 60% (6 of 10) |### | Elapsed Time: 0:00:07 ETA: 0:00:04', + ' 70% (7 of 10) |#### | Elapsed Time: 0:00:09 ETA: 0:00:03', + ' 80% (8 of 10) |#### | Elapsed Time: 0:00:11 ETA: 0:00:02', + ' 90% (9 of 10) |##### | Elapsed Time: 0:00:13 ETA: 0:00:01', + '100% (10 of 10) |#####| Elapsed Time: 0:00:15 Time: 0:00:15', + ], ) -def test_non_timed(testdir): +def test_non_timed(testdir) -> None: result = testdir.runpython( testdir.makepyfile( _create_script( @@ -177,7 +181,7 @@ def test_non_timed(testdir): ) -def test_line_breaks(testdir): +def test_line_breaks(testdir) -> None: result = testdir.runpython( testdir.makepyfile( _create_script( @@ -201,7 +205,7 @@ def test_line_breaks(testdir): ) -def test_no_line_breaks(testdir): +def test_no_line_breaks(testdir) -> None: result = testdir.runpython( testdir.makepyfile( _create_script( @@ -225,7 +229,7 @@ def test_no_line_breaks(testdir): ] -def test_percentage_label_bar(testdir): +def test_percentage_label_bar(testdir) -> None: result = testdir.runpython( testdir.makepyfile( _create_script( @@ -249,7 +253,7 @@ def test_percentage_label_bar(testdir): ] -def test_granular_bar(testdir): +def test_granular_bar(testdir) -> None: result = testdir.runpython( testdir.makepyfile( _create_script( @@ -273,7 +277,7 @@ def test_granular_bar(testdir): ] -def test_colors(testdir): +def test_colors(testdir) -> None: kwargs = dict( items=range(1), widgets=['\033[92mgreen\033[0m'], diff --git a/tests/test_multibar.py b/tests/test_multibar.py index 561e44f..8448420 100644 --- a/tests/test_multibar.py +++ b/tests/test_multibar.py @@ -2,15 +2,16 @@ import threading import time -import progressbar import pytest +import progressbar + N = 10 BARS = 3 SLEEP = 0.002 -def test_multi_progress_bar_out_of_range(): +def test_multi_progress_bar_out_of_range() -> None: widgets = [ progressbar.MultiProgressBar('multivalues'), ] @@ -23,7 +24,7 @@ def test_multi_progress_bar_out_of_range(): bar.update(multivalues=[-1]) -def test_multibar(): +def test_multibar() -> None: multibar = progressbar.MultiBar( sort_keyfunc=lambda bar: bar.label, remove_finished=0.005, @@ -100,7 +101,7 @@ def do_something(bar): progressbar.SortKey.PERCENTAGE, ], ) -def test_multibar_sorting(sort_key): +def test_multibar_sorting(sort_key) -> None: with progressbar.MultiBar() as multibar: for i in range(BARS): label = f'bar {i}' @@ -115,13 +116,13 @@ def test_multibar_sorting(sort_key): assert bar.finished() -def test_offset_bar(): +def test_offset_bar() -> None: with progressbar.ProgressBar(line_offset=2) as bar: for i in range(N): bar.update(i) -def test_multibar_show_finished(): +def test_multibar_show_finished() -> None: multibar = progressbar.MultiBar(show_finished=True) multibar['bar'] = progressbar.ProgressBar(max_value=N) multibar.render(force=True) @@ -139,13 +140,13 @@ def test_multibar_show_finished(): multibar.render(force=True) -def test_multibar_show_initial(): +def test_multibar_show_initial() -> None: multibar = progressbar.MultiBar(show_initial=False) multibar['bar'] = progressbar.ProgressBar(max_value=N) multibar.render(force=True) -def test_multibar_empty_key(): +def test_multibar_empty_key() -> None: multibar = progressbar.MultiBar() multibar[''] = progressbar.ProgressBar(max_value=N) @@ -157,12 +158,10 @@ def test_multibar_empty_key(): multibar.render(force=True) -def test_multibar_print(): - +def test_multibar_print() -> None: bars = 5 n = 10 - def print_sometimes(bar, probability): for i in bar(range(n)): # Sleep up to 0.1 seconds @@ -183,23 +182,24 @@ def print_sometimes(bar, probability): threading.Thread(target=print_sometimes, args=(bar, 0.5)).start() threading.Thread(target=print_sometimes, args=(bar, 1)).start() - for i in range(5): multibar.print(f'{i}', flush=False) multibar.update(force=True, flush=False) multibar.update(force=True, flush=True) -def test_multibar_no_format(): + +def test_multibar_no_format() -> None: with progressbar.MultiBar( - initial_format=None, finished_format=None) as multibar: + initial_format=None, finished_format=None + ) as multibar: bar = multibar['a'] for i in bar(range(5)): bar.print(i) -def test_multibar_finished(): +def test_multibar_finished() -> None: multibar = progressbar.MultiBar(initial_format=None, finished_format=None) bar = multibar['bar'] = progressbar.ProgressBar(max_value=5) bar2 = multibar['bar2'] @@ -214,10 +214,10 @@ def test_multibar_finished(): multibar.render(force=True) - -def test_multibar_finished_format(): +def test_multibar_finished_format() -> None: multibar = progressbar.MultiBar( - finished_format='Finished {label}', show_finished=True) + finished_format='Finished {label}', show_finished=True + ) bar = multibar['bar'] = progressbar.ProgressBar(max_value=5) bar2 = multibar['bar2'] multibar.render(force=True) @@ -236,7 +236,7 @@ def test_multibar_finished_format(): multibar.render(force=True) -def test_multibar_threads(): +def test_multibar_threads() -> None: multibar = progressbar.MultiBar(finished_format=None, show_finished=True) bar = multibar['bar'] = progressbar.ProgressBar(max_value=5) multibar.start() diff --git a/tests/test_progressbar.py b/tests/test_progressbar.py index d329424..eb79e66 100644 --- a/tests/test_progressbar.py +++ b/tests/test_progressbar.py @@ -3,23 +3,24 @@ import time import original_examples # type: ignore -import progressbar import pytest +import progressbar + # Import hack to allow for parallel Tox try: import examples except ImportError: import sys - _project_dir = os.path.dirname(os.path.dirname(__file__)) + _project_dir: str = os.path.dirname(os.path.dirname(__file__)) sys.path.append(_project_dir) import examples sys.path.remove(_project_dir) -def test_examples(monkeypatch): +def test_examples(monkeypatch) -> None: for example in examples.examples: with contextlib.suppress(ValueError): example() @@ -27,21 +28,21 @@ def test_examples(monkeypatch): @pytest.mark.filterwarnings('ignore:.*maxval.*:DeprecationWarning') @pytest.mark.parametrize('example', original_examples.examples) -def test_original_examples(example, monkeypatch): +def test_original_examples(example, monkeypatch) -> None: monkeypatch.setattr(progressbar.ProgressBar, '_MINIMUM_UPDATE_INTERVAL', 1) monkeypatch.setattr(time, 'sleep', lambda t: None) example() @pytest.mark.parametrize('example', examples.examples) -def test_examples_nullbar(monkeypatch, example): +def test_examples_nullbar(monkeypatch, example) -> None: # Patch progressbar to use null bar instead of regular progress bar monkeypatch.setattr(progressbar, 'ProgressBar', progressbar.NullBar) assert progressbar.ProgressBar._MINIMUM_UPDATE_INTERVAL < 0.0001 example() -def test_reuse(): +def test_reuse() -> None: bar = progressbar.ProgressBar() bar.start() for i in range(10): @@ -59,7 +60,7 @@ def test_reuse(): bar.finish() -def test_dirty(): +def test_dirty() -> None: bar = progressbar.ProgressBar() bar.start() assert bar.started() @@ -70,7 +71,8 @@ def test_dirty(): assert bar.started() -def test_negative_maximum(): +def test_negative_maximum() -> None: with pytest.raises(ValueError), progressbar.ProgressBar( - max_value=-1) as progress: + max_value=-1 + ) as progress: progress.start() diff --git a/tests/test_progressbar_command.py b/tests/test_progressbar_command.py index 05a3ab0..3dd82d6 100644 --- a/tests/test_progressbar_command.py +++ b/tests/test_progressbar_command.py @@ -1,10 +1,11 @@ import io -import progressbar.__main__ as main import pytest +import progressbar.__main__ as main + -def test_size_to_bytes(): +def test_size_to_bytes() -> None: assert main.size_to_bytes('1') == 1 assert main.size_to_bytes('1k') == 1024 assert main.size_to_bytes('1m') == 1048576 @@ -18,7 +19,7 @@ def test_size_to_bytes(): assert main.size_to_bytes('1024p') == 1152921504606846976 -def test_filename_to_bytes(tmp_path): +def test_filename_to_bytes(tmp_path) -> None: file = tmp_path / 'test' file.write_text('test') assert main.size_to_bytes(f'@{file}') == 4 @@ -27,11 +28,25 @@ def test_filename_to_bytes(tmp_path): main.size_to_bytes(f'@{tmp_path / "nonexistent"}') -def test_create_argument_parser(): +def test_create_argument_parser() -> None: parser = main.create_argument_parser() args = parser.parse_args( - ['-p', '-t', '-e', '-r', '-a', '-b', '-8', '-T', '-n', '-q', - 'input', '-o', 'output']) + [ + '-p', + '-t', + '-e', + '-r', + '-a', + '-b', + '-8', + '-T', + '-n', + '-q', + 'input', + '-o', + 'output', + ] + ) assert args.progress is True assert args.timer is True assert args.eta is True @@ -48,21 +63,48 @@ def test_create_argument_parser(): assert args.output == 'output' -def test_main_binary(capsys): +def test_main_binary(capsys) -> None: # Call the main function with different command line arguments main.main( - ['-p', '-t', '-e', '-r', '-a', '-b', '-8', '-T', '-n', '-q', __file__]) + [ + '-p', + '-t', + '-e', + '-r', + '-a', + '-b', + '-8', + '-T', + '-n', + '-q', + __file__, + ] + ) captured = capsys.readouterr() assert 'test_main(capsys):' in captured.out -def test_main_lines(capsys): +def test_main_lines(capsys) -> None: # Call the main function with different command line arguments main.main( - ['-p', '-t', '-e', '-r', '-a', '-b', '-8', '-T', '-n', '-q', '-l', - '-s', f'@{__file__}', - __file__]) + [ + '-p', + '-t', + '-e', + '-r', + '-a', + '-b', + '-8', + '-T', + '-n', + '-q', + '-l', + '-s', + f'@{__file__}', + __file__, + ] + ) captured = capsys.readouterr() assert 'test_main(capsys):' in captured.out @@ -72,13 +114,13 @@ class Input(io.StringIO): buffer: io.BytesIO @classmethod - def create(cls, text: str): + def create(cls, text: str) -> 'Input': instance = cls(text) instance.buffer = io.BytesIO(text.encode()) return instance -def test_main_lines_output(monkeypatch, tmp_path): +def test_main_lines_output(monkeypatch, tmp_path) -> None: text = 'my input' monkeypatch.setattr('sys.stdin', Input.create(text)) output_filename = tmp_path / 'output' @@ -87,7 +129,7 @@ def test_main_lines_output(monkeypatch, tmp_path): assert output_filename.read_text() == text -def test_main_bytes_output(monkeypatch, tmp_path): +def test_main_bytes_output(monkeypatch, tmp_path) -> None: text = 'my input' monkeypatch.setattr('sys.stdin', Input.create(text)) @@ -97,6 +139,6 @@ def test_main_bytes_output(monkeypatch, tmp_path): assert output_filename.read_text() == f'{text}' -def test_missing_input(tmp_path): +def test_missing_input(tmp_path) -> None: with pytest.raises(SystemExit): main.main([str(tmp_path / 'output')]) diff --git a/tests/test_samples.py b/tests/test_samples.py index 5ab388b..2881fac 100644 --- a/tests/test_samples.py +++ b/tests/test_samples.py @@ -1,12 +1,13 @@ import time from datetime import datetime, timedelta +from python_utils.containers import SliceableDeque + import progressbar from progressbar import widgets -from python_utils.containers import SliceableDeque -def test_numeric_samples(): +def test_numeric_samples() -> None: samples = 5 samples_widget = widgets.SamplesMixin(samples=samples) bar = progressbar.ProgressBar(widgets=[samples_widget]) @@ -42,7 +43,7 @@ def test_numeric_samples(): ) -def test_timedelta_samples(): +def test_timedelta_samples() -> None: samples = timedelta(seconds=5) samples_widget = widgets.SamplesMixin(samples=samples) bar = progressbar.ProgressBar(widgets=[samples_widget]) @@ -81,7 +82,7 @@ def test_timedelta_samples(): assert samples_widget(bar, None)[1] == [10, 20] -def test_timedelta_no_update(): +def test_timedelta_no_update() -> None: samples = timedelta(seconds=0.1) samples_widget = widgets.SamplesMixin(samples=samples) bar = progressbar.ProgressBar(widgets=[samples_widget]) diff --git a/tests/test_speed.py b/tests/test_speed.py index 0496daf..4f53639 100644 --- a/tests/test_speed.py +++ b/tests/test_speed.py @@ -1,6 +1,7 @@ -import progressbar import pytest +import progressbar + @pytest.mark.parametrize( 'total_seconds_elapsed,value,expected', @@ -21,7 +22,7 @@ (1, 2**90, '1024.0 YiB/s'), ], ) -def test_file_transfer_speed(total_seconds_elapsed, value, expected): +def test_file_transfer_speed(total_seconds_elapsed, value, expected) -> None: widget = progressbar.FileTransferSpeed() assert ( widget( diff --git a/tests/test_stream.py b/tests/test_stream.py index 1803ffd..d14845d 100644 --- a/tests/test_stream.py +++ b/tests/test_stream.py @@ -2,12 +2,13 @@ import os import sys -import progressbar import pytest + +import progressbar from progressbar import terminal -def test_nowrap(): +def test_nowrap() -> None: # Make sure we definitely unwrap for _i in range(5): progressbar.streams.unwrap(stderr=True, stdout=True) @@ -30,7 +31,7 @@ def test_nowrap(): progressbar.streams.unwrap(stderr=True, stdout=True) -def test_wrap(): +def test_wrap() -> None: # Make sure we definitely unwrap for _i in range(5): progressbar.streams.unwrap(stderr=True, stdout=True) @@ -57,7 +58,7 @@ def test_wrap(): progressbar.streams.unwrap(stderr=True, stdout=True) -def test_excepthook(): +def test_excepthook() -> None: progressbar.streams.wrap(stderr=True, stdout=True) try: @@ -69,7 +70,7 @@ def test_excepthook(): progressbar.streams.unwrap_excepthook() -def test_fd_as_io_stream(): +def test_fd_as_io_stream() -> None: stream = io.StringIO() with progressbar.ProgressBar(fd=stream) as pb: for i in range(101): @@ -77,7 +78,7 @@ def test_fd_as_io_stream(): stream.close() -def test_no_newlines(): +def test_no_newlines() -> None: kwargs = dict( redirect_stderr=True, redirect_stdout=True, @@ -100,18 +101,18 @@ def test_no_newlines(): @pytest.mark.parametrize('stream', [sys.__stdout__, sys.__stderr__]) @pytest.mark.skipif(os.name == 'nt', reason='Windows does not support this') -def test_fd_as_standard_streams(stream): +def test_fd_as_standard_streams(stream) -> None: with progressbar.ProgressBar(fd=stream) as pb: for i in range(101): pb.update(i) -def test_line_offset_stream_wrapper(): +def test_line_offset_stream_wrapper() -> None: stream = terminal.LineOffsetStreamWrapper(5, io.StringIO()) stream.write('Hello World!') -def test_last_line_stream_methods(): +def test_last_line_stream_methods() -> None: stream = terminal.LastLineStream(io.StringIO()) # Test write method @@ -130,16 +131,16 @@ def test_last_line_stream_methods(): assert stream.line == 'Hello' stream.truncate() assert stream.line == '' - + # Test seekable/readable assert not stream.seekable() assert stream.readable() - + stream.writelines(['a', 'b', 'c']) assert stream.read() == 'c' assert list(stream) == ['c'] - + with stream: stream.write('Hello World!') assert stream.read() == 'Hello World!' diff --git a/tests/test_terminal.py b/tests/test_terminal.py index ad61b7f..3980e5f 100644 --- a/tests/test_terminal.py +++ b/tests/test_terminal.py @@ -7,8 +7,8 @@ from progressbar import terminal -def test_left_justify(): - '''Left justify using the terminal width''' +def test_left_justify() -> None: + """Left justify using the terminal width""" p = progressbar.ProgressBar( widgets=[progressbar.BouncingBar(marker=progressbar.RotatingMarker())], max_value=100, @@ -21,8 +21,8 @@ def test_left_justify(): p.update(i) -def test_right_justify(): - '''Right justify using the terminal width''' +def test_right_justify() -> None: + """Right justify using the terminal width""" p = progressbar.ProgressBar( widgets=[progressbar.BouncingBar(marker=progressbar.RotatingMarker())], max_value=100, @@ -35,8 +35,8 @@ def test_right_justify(): p.update(i) -def test_auto_width(monkeypatch): - '''Right justify using the terminal width''' +def test_auto_width(monkeypatch) -> None: + """Right justify using the terminal width""" def ioctl(*args): return '\xbf\x00\xeb\x00\x00\x00\x00\x00' @@ -65,8 +65,8 @@ def fake_signal(signal, func): pass # Skip on Windows -def test_fill_right(): - '''Right justify using the terminal width''' +def test_fill_right() -> None: + """Right justify using the terminal width""" p = progressbar.ProgressBar( widgets=[progressbar.BouncingBar(fill_left=False)], max_value=100, @@ -78,8 +78,8 @@ def test_fill_right(): p.update(i) -def test_fill_left(): - '''Right justify using the terminal width''' +def test_fill_left() -> None: + """Right justify using the terminal width""" p = progressbar.ProgressBar( widgets=[progressbar.BouncingBar(fill_left=True)], max_value=100, @@ -91,8 +91,8 @@ def test_fill_left(): p.update(i) -def test_no_fill(monkeypatch): - '''Simply bounce within the terminal width''' +def test_no_fill(monkeypatch) -> None: + """Simply bounce within the terminal width""" bar = progressbar.BouncingBar() bar.INTERVAL = timedelta(seconds=1) p = progressbar.ProgressBar( @@ -108,7 +108,7 @@ def test_no_fill(monkeypatch): p.start_time = p.start_time - timedelta(seconds=i) -def test_stdout_redirection(): +def test_stdout_redirection() -> None: p = progressbar.ProgressBar( fd=sys.stdout, max_value=10, @@ -120,7 +120,7 @@ def test_stdout_redirection(): p.update(i) -def test_double_stdout_redirection(): +def test_double_stdout_redirection() -> None: p = progressbar.ProgressBar(max_value=10, redirect_stdout=True) p2 = progressbar.ProgressBar(max_value=10, redirect_stdout=True) @@ -130,7 +130,7 @@ def test_double_stdout_redirection(): p2.update(i) -def test_stderr_redirection(): +def test_stderr_redirection() -> None: p = progressbar.ProgressBar(max_value=10, redirect_stderr=True) for i in range(10): @@ -138,7 +138,7 @@ def test_stderr_redirection(): p.update(i) -def test_stdout_stderr_redirection(): +def test_stdout_stderr_redirection() -> None: p = progressbar.ProgressBar( max_value=10, redirect_stdout=True, @@ -155,7 +155,7 @@ def test_stdout_stderr_redirection(): p.finish() -def test_resize(monkeypatch): +def test_resize(monkeypatch) -> None: def ioctl(*args): return '\xbf\x00\xeb\x00\x00\x00\x00\x00' @@ -180,7 +180,7 @@ def fake_signal(signal, func): pass # Skip on Windows -def test_base(): +def test_base() -> None: assert str(terminal.CUP) assert str(terminal.CLEAR_SCREEN_ALL_AND_HISTORY) diff --git a/tests/test_timed.py b/tests/test_timed.py index 4d71ec6..ee19ab9 100644 --- a/tests/test_timed.py +++ b/tests/test_timed.py @@ -4,8 +4,8 @@ import progressbar -def test_timer(): - '''Testing (Adaptive)ETA when the value doesn't actually change''' +def test_timer() -> None: + """Testing (Adaptive)ETA when the value doesn't actually change""" widgets = [ progressbar.Timer(), ] @@ -24,8 +24,8 @@ def test_timer(): p.finish() -def test_eta(): - '''Testing (Adaptive)ETA when the value doesn't actually change''' +def test_eta() -> None: + """Testing (Adaptive)ETA when the value doesn't actually change""" widgets = [ progressbar.ETA(), ] @@ -51,8 +51,8 @@ def test_eta(): p.update(2) -def test_adaptive_eta(): - '''Testing (Adaptive)ETA when the value doesn't actually change''' +def test_adaptive_eta() -> None: + """Testing (Adaptive)ETA when the value doesn't actually change""" widgets = [ progressbar.AdaptiveETA(), ] @@ -71,8 +71,8 @@ def test_adaptive_eta(): p.finish() -def test_adaptive_transfer_speed(): - '''Testing (Adaptive)ETA when the value doesn't actually change''' +def test_adaptive_transfer_speed() -> None: + """Testing (Adaptive)ETA when the value doesn't actually change""" widgets = [ progressbar.AdaptiveTransferSpeed(), ] @@ -89,8 +89,8 @@ def test_adaptive_transfer_speed(): p.finish() -def test_etas(monkeypatch): - '''Compare file transfer speed to adaptive transfer speed''' +def test_etas(monkeypatch) -> None: + """Compare file transfer speed to adaptive transfer speed""" n = 10 interval = datetime.timedelta(seconds=1) widgets = [ @@ -102,7 +102,7 @@ def test_etas(monkeypatch): # Capture the output sent towards the `_speed` method def calculate_eta(self, value, elapsed): - '''Capture the widget output''' + """Capture the widget output""" data = dict( value=value, elapsed=int(elapsed), @@ -151,8 +151,8 @@ def calculate_eta(self, value, elapsed): # assert a['elapsed'] > b['elapsed'] -def test_non_changing_eta(): - '''Testing (Adaptive)ETA when the value doesn't actually change''' +def test_non_changing_eta() -> None: + """Testing (Adaptive)ETA when the value doesn't actually change""" widgets = [ progressbar.AdaptiveETA(), progressbar.ETA(), @@ -172,10 +172,10 @@ def test_non_changing_eta(): def test_eta_not_available(): - ''' + """ When ETA is not available (data coming from a generator), ETAs should not raise exceptions. - ''' + """ def gen(): yield from range(200) diff --git a/tests/test_timer.py b/tests/test_timer.py index b6cab79..083e1b1 100644 --- a/tests/test_timer.py +++ b/tests/test_timer.py @@ -1,8 +1,9 @@ from datetime import timedelta -import progressbar import pytest +import progressbar + @pytest.mark.parametrize( 'poll_interval,expected', @@ -20,7 +21,7 @@ 'min_poll_interval', ], ) -def test_poll_interval(parameter, poll_interval, expected): +def test_poll_interval(parameter, poll_interval, expected) -> None: # Test int, float and timedelta intervals bar = progressbar.ProgressBar(**{parameter: poll_interval}) assert getattr(bar, parameter) == expected @@ -33,7 +34,7 @@ def test_poll_interval(parameter, poll_interval, expected): timedelta(seconds=1), ], ) -def test_intervals(monkeypatch, interval): +def test_intervals(monkeypatch, interval) -> None: monkeypatch.setattr( progressbar.ProgressBar, '_MINIMUM_UPDATE_INTERVAL', diff --git a/tests/test_unicode.py b/tests/test_unicode.py index 98c740f..3babbdd 100644 --- a/tests/test_unicode.py +++ b/tests/test_unicode.py @@ -1,9 +1,12 @@ +from __future__ import annotations + import time -import progressbar import pytest from python_utils import converters +import progressbar + @pytest.mark.parametrize( 'name,markers', @@ -14,7 +17,7 @@ ], ) @pytest.mark.parametrize('as_unicode', [True, False]) -def test_markers(name, markers, as_unicode): +def test_markers(name, markers: bytes | str, as_unicode) -> None: if as_unicode: markers = converters.to_unicode(markers) else: diff --git a/tests/test_unknown_length.py b/tests/test_unknown_length.py index 77e3f84..65a5477 100644 --- a/tests/test_unknown_length.py +++ b/tests/test_unknown_length.py @@ -1,7 +1,7 @@ import progressbar -def test_unknown_length(): +def test_unknown_length() -> None: pb = progressbar.ProgressBar( widgets=[progressbar.AnimatedMarker()], max_value=progressbar.UnknownLength, @@ -9,7 +9,7 @@ def test_unknown_length(): assert pb.max_value is progressbar.UnknownLength -def test_unknown_length_default_widgets(): +def test_unknown_length_default_widgets() -> None: # The default widgets picked should work without a known max_value pb = progressbar.ProgressBar(max_value=progressbar.UnknownLength).start() for i in range(60): @@ -17,7 +17,7 @@ def test_unknown_length_default_widgets(): pb.finish() -def test_unknown_length_at_start(): +def test_unknown_length_at_start() -> None: # The default widgets should be picked after we call .start() pb = progressbar.ProgressBar().start(max_value=progressbar.UnknownLength) for i in range(60): diff --git a/tests/test_utils.py b/tests/test_utils.py index 47ab093..e347acd 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,8 +1,9 @@ import io +import pytest + import progressbar import progressbar.env -import pytest @pytest.mark.parametrize( @@ -24,7 +25,7 @@ ('False', False), ], ) -def test_env_flag(value, expected, monkeypatch): +def test_env_flag(value, expected, monkeypatch) -> None: if value is not None: monkeypatch.setenv('TEST_ENV', value) assert progressbar.env.env_flag('TEST_ENV') == expected @@ -36,7 +37,7 @@ def test_env_flag(value, expected, monkeypatch): monkeypatch.undo() -def test_is_terminal(monkeypatch): +def test_is_terminal(monkeypatch) -> None: fd = io.StringIO() monkeypatch.delenv('PROGRESSBAR_IS_TERMINAL', raising=False) @@ -63,7 +64,7 @@ def test_is_terminal(monkeypatch): assert progressbar.env.is_terminal(fd) is False -def test_is_ansi_terminal(monkeypatch): +def test_is_ansi_terminal(monkeypatch) -> None: fd = io.StringIO() monkeypatch.delenv('PROGRESSBAR_IS_TERMINAL', raising=False) @@ -108,5 +109,6 @@ def test_is_ansi_terminal(monkeypatch): def raise_error(): raise RuntimeError('test') + fd.isatty = raise_error assert not progressbar.env.is_ansi_terminal(fd) diff --git a/tests/test_widgets.py b/tests/test_widgets.py index fc1eeca..7ab3d88 100644 --- a/tests/test_widgets.py +++ b/tests/test_widgets.py @@ -1,12 +1,19 @@ +from __future__ import annotations + import time -import progressbar import pytest -max_values = [None, 10, progressbar.UnknownLength] +import progressbar + +max_values: list[None | type[progressbar.base.UnknownLength] | int] = [ + None, + 10, + progressbar.UnknownLength, +] -def test_create_wrapper(): +def test_create_wrapper() -> None: with pytest.raises(AssertionError): progressbar.widgets.create_wrapper('ab') @@ -14,7 +21,7 @@ def test_create_wrapper(): progressbar.widgets.create_wrapper(123) -def test_widgets_small_values(): +def test_widgets_small_values() -> None: widgets = [ 'Test: ', progressbar.Percentage(), @@ -36,7 +43,7 @@ def test_widgets_small_values(): @pytest.mark.parametrize('max_value', [10**6, 10**8]) -def test_widgets_large_values(max_value): +def test_widgets_large_values(max_value) -> None: widgets = [ 'Test: ', progressbar.Percentage(), @@ -56,7 +63,7 @@ def test_widgets_large_values(max_value): p.finish() -def test_format_widget(): +def test_format_widget() -> None: widgets = [ progressbar.FormatLabel(f'%({mapping})r') for mapping in progressbar.FormatLabel.mapping @@ -67,7 +74,7 @@ def test_format_widget(): @pytest.mark.parametrize('max_value', [None, 10]) -def test_all_widgets_small_values(max_value): +def test_all_widgets_small_values(max_value) -> None: widgets = [ progressbar.Timer(), progressbar.ETA(), @@ -96,7 +103,7 @@ def test_all_widgets_small_values(max_value): @pytest.mark.parametrize('max_value', [10**6, 10**7]) -def test_all_widgets_large_values(max_value): +def test_all_widgets_large_values(max_value) -> None: widgets = [ progressbar.Timer(), progressbar.ETA(), @@ -127,7 +134,7 @@ def test_all_widgets_large_values(max_value): @pytest.mark.parametrize('min_width', [None, 1, 2, 80, 120]) @pytest.mark.parametrize('term_width', [1, 2, 80, 120]) -def test_all_widgets_min_width(min_width, term_width): +def test_all_widgets_min_width(min_width, term_width) -> None: widgets = [ progressbar.Timer(min_width=min_width), progressbar.ETA(min_width=min_width), @@ -164,7 +171,7 @@ def test_all_widgets_min_width(min_width, term_width): @pytest.mark.parametrize('max_width', [None, 1, 2, 80, 120]) @pytest.mark.parametrize('term_width', [1, 2, 80, 120]) -def test_all_widgets_max_width(max_width, term_width): +def test_all_widgets_max_width(max_width, term_width) -> None: widgets = [ progressbar.Timer(max_width=max_width), progressbar.ETA(max_width=max_width), diff --git a/tests/test_windows.py b/tests/test_windows.py index be2e2a9..4c95fae 100644 --- a/tests/test_windows.py +++ b/tests/test_windows.py @@ -12,11 +12,16 @@ import progressbar pytest_plugins = 'pytester' -_WIDGETS = [progressbar.Percentage(), ' ', - progressbar.Bar(), ' ', - progressbar.FileTransferSpeed(), ' ', - progressbar.ETA()] -_MB = 1024 * 1024 +_WIDGETS = [ + progressbar.Percentage(), + ' ', + progressbar.Bar(), + ' ', + progressbar.FileTransferSpeed(), + ' ', + progressbar.ETA(), +] +_MB: int = 1024 * 1024 # --------------------------------------------------------------------------- @@ -36,7 +41,7 @@ def scrape_console(line_count): # --------------------------------------------------------------------------- -def runprogress(): +def runprogress() -> int: print('***BEGIN***') b = progressbar.ProgressBar( widgets=['example.m4v: ', *_WIDGETS], @@ -60,11 +65,12 @@ def find(lines, x): # --------------------------------------------------------------------------- def test_windows(testdir: pytest.Testdir) -> None: - testdir.run(sys.executable, '-c', - 'import progressbar; print(progressbar.__file__)') + testdir.run( + sys.executable, '-c', 'import progressbar; print(progressbar.__file__)' + ) -def main(): +def main() -> int: runprogress() scraped_lines = scrape_console(100) diff --git a/tests/test_with.py b/tests/test_with.py index a7c6023..3d2253f 100644 --- a/tests/test_with.py +++ b/tests/test_with.py @@ -1,19 +1,19 @@ import progressbar -def test_with(): +def test_with() -> None: with progressbar.ProgressBar(max_value=10) as p: for i in range(10): p.update(i) -def test_with_stdout_redirection(): +def test_with_stdout_redirection() -> None: with progressbar.ProgressBar(max_value=10, redirect_stdout=True) as p: for i in range(10): p.update(i) -def test_with_extra_start(): +def test_with_extra_start() -> None: with progressbar.ProgressBar(max_value=10) as p: p.start() p.start() diff --git a/tests/test_wrappingio.py b/tests/test_wrappingio.py index b868321..71a711b 100644 --- a/tests/test_wrappingio.py +++ b/tests/test_wrappingio.py @@ -2,10 +2,11 @@ import sys import pytest + from progressbar import utils -def test_wrappingio(): +def test_wrappingio() -> None: # Test the wrapping of our version of sys.stdout` ` q fd = utils.WrappingIO(sys.stdout) assert fd.fileno() @@ -31,7 +32,7 @@ def test_wrappingio(): next(iter(fd)) -def test_wrapping_stringio(): +def test_wrapping_stringio() -> None: # Test the wrapping of our version of sys.stdout` ` q string_io = io.StringIO() fd = utils.WrappingIO(string_io) diff --git a/tox.ini b/tox.ini index a554606..c681232 100644 --- a/tox.ini +++ b/tox.ini @@ -4,18 +4,21 @@ envlist = py39 py310 py311 + py312 docs black - ; mypy - pyright ruff - ; codespell +; mypy +; codespell skip_missing_interpreters = True [testenv] -deps = -r{toxinidir}/tests/requirements.txt -commands = py.test --basetemp="{envtmpdir}" --confcutdir=.. {posargs} -;changedir = tests +deps = + -r{toxinidir}/tests/requirements.txt + pyright +commands = + pyright + py.test --basetemp="{envtmpdir}" --confcutdir=.. {posargs} skip_install = true [testenv:mypy] @@ -24,14 +27,6 @@ basepython = python3 deps = mypy commands = mypy {toxinidir}/progressbar -[testenv:pyright] -changedir = -basepython = python3 -deps = - pyright - python_utils -commands = pyright {toxinidir}/progressbar - [testenv:black] basepython = python3 deps = black @@ -51,12 +46,14 @@ whitelist_externals = commands = rm -f docs/modules.rst mkdir -p docs/_static - sphinx-apidoc -e -o docs/ progressbar + sphinx-apidoc -e -o docs/ progressbar */os_specific/* rm -f docs/modules.rst sphinx-build -b html -d docs/_build/doctrees docs docs/_build/html {posargs} [testenv:ruff] -commands = ruff check progressbar tests +commands = + ruff check + ruff format deps = ruff skip_install = true