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

1from datetime import timedelta 

2from typing import Callable, Iterable, Optional, Sequence, TypeVar 

3 

4import rich.progress 

5from noiftimer import Timer 

6from rich.console import Console 

7 

8from .gradient import Gradient 

9 

10 

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 ) 

17 

18 

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 ) 

23 

24 

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) 

29 

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 

41 

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 

51 

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}" 

58 

59 

60ProgressType = TypeVar("ProgressType") 

61Style = TypeVar("Style") 

62StyleType = str | Style 

63 

64 

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 ) 

74 

75 

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. 

94 

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. 

112 

113 """ 

114 

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 ) 

141 

142 with progress: 

143 yield from progress.track( 

144 sequence, total=total, description=description, update_period=update_period 

145 )