From 1ab2fa650fec439647a54cb0c709305d95af39e5 Mon Sep 17 00:00:00 2001 From: Brian Rutledge Date: Tue, 22 Feb 2022 06:59:01 -0500 Subject: [PATCH 1/3] Add condensed time column --- rich/progress.py | 24 ++++++++++++++++++++++++ tests/test_progress.py | 23 +++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/rich/progress.py b/rich/progress.py index bbbdf70ef..ddd865c64 100644 --- a/rich/progress.py +++ b/rich/progress.py @@ -357,6 +357,30 @@ def render(self, task: "Task") -> Text: return Text(str(remaining_delta), style="progress.remaining") +class CondensedTimeColumn(ProgressColumn): + """Renders estimated time remaining, or elapsed time when the task is finished.""" + + # Only refresh twice a second to prevent jitter + max_refresh = 0.5 + + def render(self, task: "Task") -> Text: + """Show time.""" + style = "progress.elapsed" if task.finished else "progress.remaining" + task_time = task.finished_time if task.finished else task.time_remaining + if task_time is None: + return Text("--:--", style=style) + + # Based on https://github.com/tqdm/tqdm/blob/master/tqdm/std.py + minutes, seconds = divmod(int(task_time), 60) + hours, minutes = divmod(minutes, 60) + if hours: + formatted = f"{hours:d}:{minutes:02d}:{seconds:02d}" + else: + formatted = f"{minutes:02d}:{seconds:02d}" + + return Text(formatted, style=style) + + class FileSizeColumn(ProgressColumn): """Renders completed filesize.""" diff --git a/tests/test_progress.py b/tests/test_progress.py index 2dd53ccd2..fb3674cf9 100644 --- a/tests/test_progress.py +++ b/tests/test_progress.py @@ -2,6 +2,7 @@ import io from time import sleep +from types import SimpleNamespace import pytest @@ -22,6 +23,7 @@ TextColumn, TimeElapsedColumn, TimeRemainingColumn, + CondensedTimeColumn, track, _TrackThread, TaskID, @@ -89,6 +91,27 @@ class FakeTask(Task): assert str(text) == "0:01:00" +@pytest.mark.parametrize("finished", [False, True]) +@pytest.mark.parametrize( + "task_time, formatted", + [ + (None, "--:--"), + (0, "00:00"), + (59, "00:59"), + (71, "01:11"), + (4210, "1:10:10"), + ], +) +def test_condensed_time_column(finished, task_time, formatted): + if finished: + task = SimpleNamespace(finished=finished, finished_time=task_time) + else: + task = SimpleNamespace(finished=finished, time_remaining=task_time) + + column = CondensedTimeColumn() + assert str(column.render(task)) == formatted + + def test_renderable_column(): column = RenderableColumn("foo") task = Task(1, "test", 100, 20, _get_time=lambda: 1.0) From 71b1ac60e3fb27cc948bfca3bcdb22fd6fc8b793 Mon Sep 17 00:00:00 2001 From: Brian Rutledge Date: Sat, 26 Feb 2022 15:39:55 -0500 Subject: [PATCH 2/3] Move new behaviors to TimeRemainingColumn --- rich/progress.py | 48 ++++++++++++++++++++++++------------------ tests/test_progress.py | 21 +++++++++++------- 2 files changed, 40 insertions(+), 29 deletions(-) diff --git a/rich/progress.py b/rich/progress.py index ddd865c64..a80a20d2e 100644 --- a/rich/progress.py +++ b/rich/progress.py @@ -343,40 +343,46 @@ def render(self, task: "Task") -> Text: class TimeRemainingColumn(ProgressColumn): - """Renders estimated time remaining.""" + """Renders estimated time remaining. + + Args: + compact (bool, optional): Render MM:SS when time remaining is less than an hour. Defaults to False. + elapsed_when_finished (bool, optional): Render time elapsed when the task is finished. Defaults to False. + """ # Only refresh twice a second to prevent jitter max_refresh = 0.5 + def __init__( + self, + compact: bool = False, + elapsed_when_finished: bool = False, + table_column: Optional[Column] = None, + ): + self.compact = compact + self.elapsed_when_finished = elapsed_when_finished + super().__init__(table_column=table_column) + def render(self, task: "Task") -> Text: """Show time remaining.""" - remaining = task.time_remaining - if remaining is None: - return Text("-:--:--", style="progress.remaining") - remaining_delta = timedelta(seconds=int(remaining)) - return Text(str(remaining_delta), style="progress.remaining") - - -class CondensedTimeColumn(ProgressColumn): - """Renders estimated time remaining, or elapsed time when the task is finished.""" - - # Only refresh twice a second to prevent jitter - max_refresh = 0.5 + if self.elapsed_when_finished and task.finished: + task_time = task.finished_time + style = "progress.elapsed" + else: + task_time = task.time_remaining + style = "progress.remaining" - def render(self, task: "Task") -> Text: - """Show time.""" - style = "progress.elapsed" if task.finished else "progress.remaining" - task_time = task.finished_time if task.finished else task.time_remaining if task_time is None: - return Text("--:--", style=style) + return Text("--:--" if self.compact else "-:--:--", style=style) # Based on https://github.com/tqdm/tqdm/blob/master/tqdm/std.py minutes, seconds = divmod(int(task_time), 60) hours, minutes = divmod(minutes, 60) - if hours: - formatted = f"{hours:d}:{minutes:02d}:{seconds:02d}" - else: + + if self.compact and not hours: formatted = f"{minutes:02d}:{seconds:02d}" + else: + formatted = f"{hours:d}:{minutes:02d}:{seconds:02d}" return Text(formatted, style=style) diff --git a/tests/test_progress.py b/tests/test_progress.py index fb3674cf9..8bbf29b64 100644 --- a/tests/test_progress.py +++ b/tests/test_progress.py @@ -23,7 +23,6 @@ TextColumn, TimeElapsedColumn, TimeRemainingColumn, - CondensedTimeColumn, track, _TrackThread, TaskID, @@ -91,7 +90,6 @@ class FakeTask(Task): assert str(text) == "0:01:00" -@pytest.mark.parametrize("finished", [False, True]) @pytest.mark.parametrize( "task_time, formatted", [ @@ -102,13 +100,20 @@ class FakeTask(Task): (4210, "1:10:10"), ], ) -def test_condensed_time_column(finished, task_time, formatted): - if finished: - task = SimpleNamespace(finished=finished, finished_time=task_time) - else: - task = SimpleNamespace(finished=finished, time_remaining=task_time) +def test_compact_time_remaining_column(task_time, formatted): + task = SimpleNamespace(finished=False, time_remaining=task_time) + column = TimeRemainingColumn(compact=True) + + assert str(column.render(task)) == formatted + + +def test_time_remaining_column_elapsed_when_finished(): + task_time = 71 + formatted = "0:01:11" + + task = SimpleNamespace(finished=True, finished_time=task_time) + column = TimeRemainingColumn(elapsed_when_finished=True) - column = CondensedTimeColumn() assert str(column.render(task)) == formatted From e2ccd5b70dbc134f80c0cf2cd68eaf5a4457d50b Mon Sep 17 00:00:00 2001 From: Brian Rutledge Date: Sat, 26 Feb 2022 16:00:55 -0500 Subject: [PATCH 3/3] Update CHANGELOG and CONTRIBUTORS --- CHANGELOG.md | 4 +++- CONTRIBUTORS.md | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a93d06391..f11ad82e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,8 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Added options to TimeRemainingColumn to render a compact time format and render elapsed time when a task is + finished. https://github.com/Textualize/rich/pull/1992 - Added ProgressColumn `MofNCompleteColumn` to display raw `completed/total` column (similar to DownloadColumn, - but displays values as ints, does not convert to floats or add bit/bytes units). + but displays values as ints, does not convert to floats or add bit/bytes units). https://github.com/Textualize/rich/pull/1941 ### Fixed diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index c58f4703b..9baddef6c 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -31,4 +31,4 @@ The following people have contributed to the development of Rich: - [Dennis Brakhane](https://github.com/brakhane) - [Michał Górny](https://github.com/mgorny) - [Arian Mollik Wasi](https://github.com/wasi-master) - +- [Brian Rutledge](https://github.com/bhrutledge)