2021-04-29 11:25:00 +02:00
|
|
|
import asyncio
|
2021-04-29 13:11:58 +02:00
|
|
|
from contextlib import asynccontextmanager, contextmanager
|
2021-04-29 14:23:28 +02:00
|
|
|
from types import TracebackType
|
|
|
|
from typing import AsyncIterator, Iterator, List, Optional, Type
|
2021-04-29 11:25:00 +02:00
|
|
|
|
2021-05-13 19:47:44 +02:00
|
|
|
from rich.console import Console
|
2021-04-29 11:25:00 +02:00
|
|
|
from rich.progress import Progress, TaskID
|
|
|
|
|
|
|
|
|
|
|
|
class ProgressBar:
|
|
|
|
def __init__(self, progress: Progress, taskid: TaskID):
|
|
|
|
self._progress = progress
|
|
|
|
self._taskid = taskid
|
|
|
|
|
|
|
|
def advance(self, amount: float = 1) -> None:
|
|
|
|
self._progress.advance(self._taskid, advance=amount)
|
|
|
|
|
2021-05-10 23:50:16 +02:00
|
|
|
def set_total(self, total: float) -> None:
|
2021-05-06 01:02:40 +02:00
|
|
|
self._progress.update(self._taskid, total=total)
|
2021-05-14 21:41:24 +02:00
|
|
|
self._progress.start_task(self._taskid)
|
2021-05-06 01:02:40 +02:00
|
|
|
|
2021-04-29 11:25:00 +02:00
|
|
|
|
|
|
|
class TerminalConductor:
|
|
|
|
def __init__(self) -> None:
|
|
|
|
self._stopped = False
|
|
|
|
self._lock = asyncio.Lock()
|
|
|
|
self._lines: List[str] = []
|
|
|
|
|
2021-05-13 19:47:44 +02:00
|
|
|
self._console = Console(highlight=False)
|
|
|
|
self._progress = Progress(console=self._console)
|
|
|
|
|
2021-04-29 14:23:28 +02:00
|
|
|
async def _start(self) -> None:
|
2021-04-29 15:26:10 +02:00
|
|
|
for task in self._progress.tasks:
|
|
|
|
task.visible = True
|
|
|
|
self._progress.start()
|
|
|
|
|
|
|
|
self._stopped = False
|
2021-04-29 11:25:00 +02:00
|
|
|
|
2021-04-29 15:26:10 +02:00
|
|
|
for line in self._lines:
|
|
|
|
self.print(line)
|
|
|
|
self._lines = []
|
2021-04-29 11:25:00 +02:00
|
|
|
|
2021-04-29 14:23:28 +02:00
|
|
|
async def _stop(self) -> None:
|
2021-04-29 15:26:10 +02:00
|
|
|
self._stopped = True
|
|
|
|
|
|
|
|
for task in self._progress.tasks:
|
|
|
|
task.visible = False
|
|
|
|
self._progress.stop()
|
2021-04-29 11:25:00 +02:00
|
|
|
|
2021-04-29 14:23:28 +02:00
|
|
|
async def __aenter__(self) -> None:
|
2021-04-29 15:26:10 +02:00
|
|
|
async with self._lock:
|
|
|
|
await self._start()
|
2021-04-29 14:23:28 +02:00
|
|
|
|
|
|
|
async def __aexit__(
|
|
|
|
self,
|
|
|
|
exc_type: Optional[Type[BaseException]],
|
|
|
|
exc_value: Optional[BaseException],
|
|
|
|
traceback: Optional[TracebackType],
|
|
|
|
) -> Optional[bool]:
|
2021-04-29 15:26:10 +02:00
|
|
|
async with self._lock:
|
|
|
|
await self._stop()
|
2021-04-29 14:23:28 +02:00
|
|
|
return None
|
2021-04-29 11:25:00 +02:00
|
|
|
|
|
|
|
def print(self, line: str) -> None:
|
|
|
|
if self._stopped:
|
|
|
|
self._lines.append(line)
|
|
|
|
else:
|
2021-05-13 19:47:44 +02:00
|
|
|
self._console.print(line)
|
2021-04-29 11:25:00 +02:00
|
|
|
|
|
|
|
@asynccontextmanager
|
2021-04-29 13:43:50 +02:00
|
|
|
async def exclusive_output(self) -> AsyncIterator[None]:
|
2021-04-29 11:25:00 +02:00
|
|
|
async with self._lock:
|
2021-04-29 15:26:10 +02:00
|
|
|
await self._stop()
|
2021-04-29 11:25:00 +02:00
|
|
|
try:
|
|
|
|
yield
|
|
|
|
finally:
|
2021-04-29 15:26:10 +02:00
|
|
|
await self._start()
|
2021-04-29 11:25:00 +02:00
|
|
|
|
|
|
|
@contextmanager
|
2021-04-29 13:43:50 +02:00
|
|
|
def progress_bar(
|
2021-04-29 11:25:00 +02:00
|
|
|
self,
|
|
|
|
description: str,
|
2021-04-29 13:43:50 +02:00
|
|
|
total: Optional[float] = None,
|
2021-04-29 11:25:00 +02:00
|
|
|
) -> Iterator[ProgressBar]:
|
2021-04-29 13:43:50 +02:00
|
|
|
if total is None:
|
|
|
|
# Indeterminate progress bar
|
|
|
|
taskid = self._progress.add_task(description, start=False)
|
|
|
|
else:
|
|
|
|
taskid = self._progress.add_task(description, total=total)
|
|
|
|
|
2021-04-29 11:25:00 +02:00
|
|
|
bar = ProgressBar(self._progress, taskid)
|
|
|
|
try:
|
|
|
|
yield bar
|
|
|
|
finally:
|
|
|
|
self._progress.remove_task(taskid)
|