From d9b31e5a9b43fa8cbf88bfaad1e7149f01a59850 Mon Sep 17 00:00:00 2001 From: Hagen Wierstorf Date: Mon, 1 Jul 2024 09:58:02 +0200 Subject: [PATCH 1/2] Add maximum_refresh_time to progress_bar() --- audeer/core/tqdm.py | 35 ++++++++++++++++++++++++++++++++--- tests/test_tqdm.py | 12 ++++++++++++ 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/audeer/core/tqdm.py b/audeer/core/tqdm.py index dd3eff0..879c3de 100644 --- a/audeer/core/tqdm.py +++ b/audeer/core/tqdm.py @@ -1,4 +1,6 @@ -from typing import Sequence +import threading +import time +import typing from tqdm import tqdm @@ -42,11 +44,12 @@ def format_display_message(text: str, pbar: bool = False) -> str: def progress_bar( - iterable: Sequence = None, + iterable: typing.Sequence = None, *, total: int = None, desc: str = None, disable: bool = False, + maximum_refresh_time: typing.Optional = 1, ) -> tqdm: r"""Progress bar with optional text on the right. @@ -80,6 +83,11 @@ def progress_bar( total: total number of iterations desc: text shown on the right of the progress bar disable: don't show the display bar + maximum_refresh_time: refresh the progress bar + at least every ``maximum_refresh_time`` seconds, + using another thread. + If ``None``, + no refreshing is enforced Returns: progress bar object @@ -87,8 +95,9 @@ def progress_bar( """ if desc is None: desc = "" - return tqdm( + return tqdm_wrapper( iterable=iterable, + maximum_refresh_time=maximum_refresh_time, ncols=config.TQDM_COLUMNS, bar_format=config.TQDM_FORMAT, total=total, @@ -96,3 +105,23 @@ def progress_bar( desc=format_display_message(desc, pbar=True), leave=config.TQDM_LEAVE, ) + + +def tqdm_wrapper(iterable, maximum_refresh_time, *args, **kwargs): + r"""Progress bar wrapper to enforce update once a second. + + See https://github.com/tqdm/tqdm/issues/861#issuecomment-2197893883. + + """ + pbar = tqdm(iterable, *args, **kwargs) + + def refresh(): + while not pbar.disable: + time.sleep(maximum_refresh_time) + pbar.refresh() + + if maximum_refresh_time is not None: + thread = threading.Thread(target=refresh, daemon=True) + thread.start() + + return pbar diff --git a/tests/test_tqdm.py b/tests/test_tqdm.py index 9d992a0..cbcd68e 100644 --- a/tests/test_tqdm.py +++ b/tests/test_tqdm.py @@ -37,3 +37,15 @@ def test_progress_bar(): pbar = audeer.progress_bar([0.1]) for step in pbar: time.sleep(step) + + +def test_progress_bar_update(): + r"""Ensure progress bar is refreshed. + + If the progress bar has to wait for a long time + until it would get updated, + we enforce an update by a given time. + + """ + for _ in audeer.progress_bar(range(2), maximum_refresh_time=0.01): + time.sleep(0.05) From ff1906122b0697e6ccf769a2ebd974f3cd377708 Mon Sep 17 00:00:00 2001 From: Hagen Wierstorf Date: Mon, 1 Jul 2024 11:12:32 +0200 Subject: [PATCH 2/2] None as default --- audeer/core/tqdm.py | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/audeer/core/tqdm.py b/audeer/core/tqdm.py index 879c3de..c32dca2 100644 --- a/audeer/core/tqdm.py +++ b/audeer/core/tqdm.py @@ -49,7 +49,7 @@ def progress_bar( total: int = None, desc: str = None, disable: bool = False, - maximum_refresh_time: typing.Optional = 1, + maximum_refresh_time: float = None, ) -> tqdm: r"""Progress bar with optional text on the right. @@ -107,10 +107,32 @@ def progress_bar( ) -def tqdm_wrapper(iterable, maximum_refresh_time, *args, **kwargs): - r"""Progress bar wrapper to enforce update once a second. +def tqdm_wrapper( + iterable: typing.Sequence, + maximum_refresh_time: float, + *args, + **kwargs, +) -> tqdm: + r"""Tqdm progress bar wrapper to enforce update once a second. + + When using tqdm with large time durations + between single steps of the iteration, + it will not automatically update the elapsed time, + but needs to be forced, + see https://github.com/tqdm/tqdm/issues/861#issuecomment-2197893883. + + Args: + iterable: sequence to iterate through + maximum_refresh_time: refresh the progress bar + at least every ``maximum_refresh_time`` seconds, + using another thread. + If ``None``, + no refreshing is enforced + args: arguments passed on to ``tqdm`` + kwargs: keyword arguments passed on to ``tqdm`` - See https://github.com/tqdm/tqdm/issues/861#issuecomment-2197893883. + Returns: + progress bar object """ pbar = tqdm(iterable, *args, **kwargs)