1from types import TracebackType 2from typing import Optional, Type 3 4from .console import Console, RenderableType 5from .jupyter import JupyterMixin 6from .live import Live 7from .spinner import Spinner 8from .style import StyleType 9 10 11class Status(JupyterMixin): 12 """Displays a status indicator with a 'spinner' animation. 13 14 Args: 15 status (RenderableType): A status renderable (str or Text typically). 16 console (Console, optional): Console instance to use, or None for global console. Defaults to None. 17 spinner (str, optional): Name of spinner animation (see python -m rich.spinner). Defaults to "dots". 18 spinner_style (StyleType, optional): Style of spinner. Defaults to "status.spinner". 19 speed (float, optional): Speed factor for spinner animation. Defaults to 1.0. 20 refresh_per_second (float, optional): Number of refreshes per second. Defaults to 12.5. 21 """ 22 23 def __init__( 24 self, 25 status: RenderableType, 26 *, 27 console: Optional[Console] = None, 28 spinner: str = "dots", 29 spinner_style: StyleType = "status.spinner", 30 speed: float = 1.0, 31 refresh_per_second: float = 12.5, 32 ): 33 self.status = status 34 self.spinner_style = spinner_style 35 self.speed = speed 36 self._spinner = Spinner(spinner, text=status, style=spinner_style, speed=speed) 37 self._live = Live( 38 self.renderable, 39 console=console, 40 refresh_per_second=refresh_per_second, 41 transient=True, 42 ) 43 44 @property 45 def renderable(self) -> Spinner: 46 return self._spinner 47 48 @property 49 def console(self) -> "Console": 50 """Get the Console used by the Status objects.""" 51 return self._live.console 52 53 def update( 54 self, 55 status: Optional[RenderableType] = None, 56 *, 57 spinner: Optional[str] = None, 58 spinner_style: Optional[StyleType] = None, 59 speed: Optional[float] = None, 60 ) -> None: 61 """Update status. 62 63 Args: 64 status (Optional[RenderableType], optional): New status renderable or None for no change. Defaults to None. 65 spinner (Optional[str], optional): New spinner or None for no change. Defaults to None. 66 spinner_style (Optional[StyleType], optional): New spinner style or None for no change. Defaults to None. 67 speed (Optional[float], optional): Speed factor for spinner animation or None for no change. Defaults to None. 68 """ 69 if status is not None: 70 self.status = status 71 if spinner_style is not None: 72 self.spinner_style = spinner_style 73 if speed is not None: 74 self.speed = speed 75 if spinner is not None: 76 self._spinner = Spinner( 77 spinner, text=self.status, style=self.spinner_style, speed=self.speed 78 ) 79 self._live.update(self.renderable, refresh=True) 80 else: 81 self._spinner.update( 82 text=self.status, style=self.spinner_style, speed=self.speed 83 ) 84 85 def start(self) -> None: 86 """Start the status animation.""" 87 self._live.start() 88 89 def stop(self) -> None: 90 """Stop the spinner animation.""" 91 self._live.stop() 92 93 def __rich__(self) -> RenderableType: 94 return self.renderable 95 96 def __enter__(self) -> "Status": 97 self.start() 98 return self 99 100 def __exit__( 101 self, 102 exc_type: Optional[Type[BaseException]], 103 exc_val: Optional[BaseException], 104 exc_tb: Optional[TracebackType], 105 ) -> None: 106 self.stop() 107 108 109if __name__ == "__main__": # pragma: no cover 110 111 from time import sleep 112 113 from .console import Console 114 115 console = Console() 116 with console.status("[magenta]Covid detector booting up") as status: 117 sleep(3) 118 console.log("Importing advanced AI") 119 sleep(3) 120 console.log("Advanced Covid AI Ready") 121 sleep(3) 122 status.update(status="[bold blue] Scanning for Covid", spinner="earth") 123 sleep(3) 124 console.log("Found 10,000,000,000 copies of Covid32.exe") 125 sleep(3) 126 status.update( 127 status="[bold red]Moving Covid32.exe to Trash", 128 spinner="bouncingBall", 129 spinner_style="yellow", 130 ) 131 sleep(5) 132 console.print("[bold green]Covid deleted successfully") 133