Coverage for E:\1vsCode\python\printbuddies\src\printbuddies\progress.py: 38%
52 statements
« prev ^ index » next coverage.py v7.2.2, created at 2024-02-15 19:42 -0600
« prev ^ index » next coverage.py v7.2.2, created at 2024-02-15 19:42 -0600
1from datetime import timedelta
2from typing import Callable, Iterable, Optional, Sequence, TypeVar
4import rich.progress
5from noiftimer import Timer
6from rich.console import Console
8from .gradient import Gradient
11def get_bar_column() -> rich.progress.BarColumn:
12 return rich.progress.BarColumn(
13 style="sea_green1",
14 complete_style="deep_pink1",
15 finished_style="cornflower_blue",
16 )
19def get_task_progress_column(*args, **kwargs) -> rich.progress.TaskProgressColumn:
20 return rich.progress.TaskProgressColumn(
21 "{task.percentage:>3.0f}%", style="light_coral", markup=False, *args, **kwargs
22 )
25class TimerColumn(rich.progress.TimeRemainingColumn):
26 def __init__(self, elapsed_only: bool = False, *args, **kwargs):
27 self.elapsed_only = elapsed_only
28 super().__init__(*args, **kwargs)
30 def get_time_remaining(self, task: rich.progress.Task) -> str:
31 if self.elapsed_when_finished and task.finished:
32 task_time = task.finished_time
33 else:
34 task_time = task.time_remaining
35 if not task.total or not task_time:
36 return ""
37 time_remaining = Timer.format_time(task_time)
38 if time_remaining == "<1s":
39 time_remaining = "0s"
40 return time_remaining
42 def get_time_elapsed(self, task: rich.progress.Task) -> str:
43 elapsed = task.finished_time if task.finished else task.elapsed
44 if not elapsed:
45 return ""
46 delta = timedelta(seconds=max(0, int(elapsed)))
47 time_elapsed = Timer.format_time(delta.total_seconds())
48 if time_elapsed == "<1s":
49 time_elapsed = "0s"
50 return time_elapsed
52 def render(self, task: rich.progress.Task) -> str:
53 timing = self.get_time_elapsed(task)
54 if not self.elapsed_only and (time_remaining := self.get_time_remaining(task)):
55 timing += f" <-> {time_remaining}"
56 return Gradient().apply(timing)
57 return f"[pink1]{timing}"
60ProgressType = TypeVar("ProgressType")
61Style = TypeVar("Style")
62StyleType = str | Style
65class Progress(rich.progress.Progress):
66 @classmethod
67 def get_default_columns(cls) -> tuple[rich.progress.ProgressColumn, ...]:
68 return (
69 rich.progress.TextColumn("[progress.description]{task.description}"),
70 get_bar_column(),
71 get_task_progress_column(),
72 TimerColumn(),
73 )
76def track(
77 sequence: Sequence[ProgressType] | Iterable[ProgressType],
78 description: str = "[hot_pink]Yeehaw...",
79 total: Optional[float] = None,
80 auto_refresh: bool = True,
81 console: Optional[Console] = None,
82 transient: bool = False,
83 get_time: Optional[Callable[[], float]] = None,
84 refresh_per_second: float = 10,
85 style: StyleType = "sea_green1",
86 complete_style: StyleType = "deep_pink1",
87 finished_style: StyleType = "cornflower_blue",
88 pulse_style: StyleType = "bar.pulse",
89 update_period: float = 0.1,
90 disable: bool = False,
91 show_speed: bool = True,
92) -> Iterable[ProgressType]:
93 """Track progress by iterating over a sequence.
95 Args:
96 sequence (Iterable[ProgressType]): A sequence (must support "len") you wish to iterate over.
97 description (str, optional): Description of task show next to progress bar. Defaults to "Working".
98 total: (float, optional): Total number of steps. Default is len(sequence).
99 auto_refresh (bool, optional): Automatic refresh, disable to force a refresh after each iteration. Default is True.
100 transient: (bool, optional): Clear the progress on exit. Defaults to False.
101 console (Console, optional): Console to write to. Default creates internal Console instance.
102 refresh_per_second (float): Number of times per second to refresh the progress information. Defaults to 10.
103 style (StyleType, optional): Style for the bar background. Defaults to "bar.back".
104 complete_style (StyleType, optional): Style for the completed bar. Defaults to "bar.complete".
105 finished_style (StyleType, optional): Style for a finished bar. Defaults to "bar.finished".
106 pulse_style (StyleType, optional): Style for pulsing bars. Defaults to "bar.pulse".
107 update_period (float, optional): Minimum time (in seconds) between calls to update(). Defaults to 0.1.
108 disable (bool, optional): Disable display of progress.
109 show_speed (bool, optional): Show speed if total isn't known. Defaults to True.
110 Returns:
111 Iterable[ProgressType]: An iterable of the values in the sequence.
113 """
115 columns: list[rich.progress.ProgressColumn] = (
116 [rich.progress.TextColumn("[progress.description]{task.description}")]
117 if description
118 else []
119 )
120 columns.extend(
121 (
122 rich.progress.BarColumn(
123 style=style,
124 complete_style=complete_style,
125 finished_style=finished_style,
126 pulse_style=pulse_style,
127 ),
128 get_task_progress_column(show_speed=show_speed),
129 TimerColumn(elapsed_when_finished=True, elapsed_only=True),
130 )
131 )
132 progress = Progress(
133 *columns,
134 auto_refresh=auto_refresh,
135 console=console,
136 transient=transient,
137 get_time=get_time,
138 refresh_per_second=refresh_per_second or 10,
139 disable=disable,
140 )
142 with progress:
143 yield from progress.track(
144 sequence, total=total, description=description, update_period=update_period
145 )