Skip to content

Commit 0ed9309

Browse files
authored
Merge pull request #2210 from Textualize/progress-speed
progress speed
2 parents c979a1b + f27a512 commit 0ed9309

File tree

3 files changed

+56
-14
lines changed

3 files changed

+56
-14
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
### Added
1111

1212
- Ability to change terminal window title https://github.com/Textualize/rich/pull/2200
13+
- Added show_speed parameter to progress.track which will show the speed when the total is not known
1314

1415
### Fixed
1516

rich/progress.py

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ def track(
115115
pulse_style: StyleType = "bar.pulse",
116116
update_period: float = 0.1,
117117
disable: bool = False,
118+
show_speed: bool = True,
118119
) -> Iterable[ProgressType]:
119120
"""Track progress by iterating over a sequence.
120121
@@ -132,6 +133,7 @@ def track(
132133
pulse_style (StyleType, optional): Style for pulsing bars. Defaults to "bar.pulse".
133134
update_period (float, optional): Minimum time (in seconds) between calls to update(). Defaults to 0.1.
134135
disable (bool, optional): Disable display of progress.
136+
show_speed (bool, optional): Show speed if total isn't known. Defaults to True.
135137
Returns:
136138
Iterable[ProgressType]: An iterable of the values in the sequence.
137139
@@ -148,7 +150,7 @@ def track(
148150
finished_style=finished_style,
149151
pulse_style=pulse_style,
150152
),
151-
TaskProgressColumn(),
153+
TaskProgressColumn(show_speed=show_speed),
152154
TimeRemainingColumn(),
153155
)
154156
)
@@ -676,7 +678,18 @@ def render(self, task: "Task") -> Text:
676678

677679

678680
class TaskProgressColumn(TextColumn):
679-
"""A column displaying the progress of a task."""
681+
"""Show task progress as a percentage.
682+
683+
Args:
684+
text_format (str, optional): Format for percentage display. Defaults to "[progress.percentage]{task.percentage:>3.0f}%".
685+
text_format_no_percentage (str, optional): Format if percentage is unknown. Defaults to "".
686+
style (StyleType, optional): Style of output. Defaults to "none".
687+
justify (JustifyMethod, optional): Text justification. Defaults to "left".
688+
markup (bool, optional): Enable markup. Defaults to True.
689+
highlighter (Optional[Highlighter], optional): Highlighter to apply to output. Defaults to None.
690+
table_column (Optional[Column], optional): Table Column to use. Defaults to None.
691+
show_speed (bool, optional): Show speed if total is unknown. Defaults to False.
692+
"""
680693

681694
def __init__(
682695
self,
@@ -687,8 +700,11 @@ def __init__(
687700
markup: bool = True,
688701
highlighter: Optional[Highlighter] = None,
689702
table_column: Optional[Column] = None,
703+
show_speed: bool = False,
690704
) -> None:
705+
691706
self.text_format_no_percentage = text_format_no_percentage
707+
self.show_speed = show_speed
692708
super().__init__(
693709
text_format=text_format,
694710
style=style,
@@ -698,7 +714,29 @@ def __init__(
698714
table_column=table_column,
699715
)
700716

717+
@classmethod
718+
def render_speed(cls, speed: Optional[float]) -> Text:
719+
"""Render the speed in iterations per second.
720+
721+
Args:
722+
task (Task): A Task object.
723+
724+
Returns:
725+
Text: Text object containing the task speed.
726+
"""
727+
if speed is None:
728+
return Text("", style="progress.percentage")
729+
unit, suffix = filesize.pick_unit_and_suffix(
730+
int(speed),
731+
["", "×10³", "×10⁶", "×10⁹", "×10¹²"],
732+
1000,
733+
)
734+
data_speed = speed / unit
735+
return Text(f"{data_speed:.1f}{suffix} it/s", style="progress.percentage")
736+
701737
def render(self, task: "Task") -> Text:
738+
if task.total is None and self.show_speed:
739+
return self.render_speed(task.finished_speed or task.speed)
702740
text_format = (
703741
self.text_format_no_percentage if task.total is None else self.text_format
704742
)
@@ -1152,13 +1190,10 @@ def track(
11521190
Iterable[ProgressType]: An iterable of values taken from the provided sequence.
11531191
"""
11541192

1193+
task_total: Optional[float] = None
11551194
if total is None:
11561195
if isinstance(sequence, Sized):
11571196
task_total = float(len(sequence))
1158-
else:
1159-
raise ValueError(
1160-
f"unable to get size of {sequence!r}, please specify 'total'"
1161-
)
11621197
else:
11631198
task_total = total
11641199

tests/test_progress.py

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -308,10 +308,6 @@ def test_track() -> None:
308308

309309
assert result == expected
310310

311-
with pytest.raises(ValueError):
312-
for n in track(5):
313-
pass
314-
315311

316312
def test_progress_track() -> None:
317313
console = Console(
@@ -341,10 +337,6 @@ def test_progress_track() -> None:
341337

342338
assert result == expected
343339

344-
with pytest.raises(ValueError):
345-
for n in progress.track(5):
346-
pass
347-
348340

349341
def test_columns() -> None:
350342

@@ -655,6 +647,20 @@ def test_wrap_file_task_total() -> None:
655647
os.remove(filename)
656648

657649

650+
def test_task_progress_column_speed():
651+
speed_text = TaskProgressColumn.render_speed(None)
652+
assert speed_text.plain == ""
653+
654+
speed_text = TaskProgressColumn.render_speed(5)
655+
assert speed_text.plain == "5.0 it/s"
656+
657+
speed_text = TaskProgressColumn.render_speed(5000)
658+
assert speed_text.plain == "5.0×10³ it/s"
659+
660+
speed_text = TaskProgressColumn.render_speed(8888888)
661+
assert speed_text.plain == "8.9×10⁶ it/s"
662+
663+
658664
if __name__ == "__main__":
659665
_render = render_progress()
660666
print(_render)

0 commit comments

Comments
 (0)