Source code for core_mixins.cli.progress_tracker
# -*- coding: utf-8 -*-
"""
A lightweight terminal progress bar utility for CLI pipelines, with
no external dependencies, pure stdlib.
"""
import logging
import sys
import typing
GREEN = "\033[32m"
YELLOW = "\033[33m"
CYAN = "\033[36m"
DIM = "\033[2m"
BOLD = "\033[1m"
RESET = "\033[0m"
BAR_WIDTH = 40
[docs]
class ProgressTracker:
"""Progress tracker utility for CLI pipelines."""
[docs]
def __init__(
self,
logger: typing.Optional[logging.Logger] = None,
) -> None:
self._tty = sys.stdout.isatty()
self.logger = logger
@staticmethod
def _build_bar(pct: float) -> str:
filled = int(pct / 100 * BAR_WIDTH)
return "█" * filled + "░" * (BAR_WIDTH - filled)
[docs]
def progress(self, desc: str, pct: float, note: str = "") -> None:
"""Overwrite the current terminal line."""
if not self._tty:
return
filled = self._build_bar(pct)
right = f" {note}" if note else ""
line = (
f" {CYAN}→{RESET} {desc:<26} [{CYAN}{filled}{RESET}]"
f" {pct:>5.1f}%{DIM}{right}{RESET}"
)
sys.stdout.write(f"\r{line:<120}")
sys.stdout.flush()
[docs]
def done(self, desc: str, note: str = "") -> None:
"""Clear the progress line and print a persistent ✔ line."""
if self._tty:
right = f" {DIM}{note}{RESET}" if note else ""
line = (
f" {GREEN}✔{RESET} {desc:<26} [{GREEN}{'█' * BAR_WIDTH}{RESET}]"
f" {100.0:>5.1f}%{DIM}{right}{RESET}"
)
sys.stdout.write(f"\r{line:<120}\n")
sys.stdout.flush()
elif self.logger:
right = f" {note}" if note else ""
self.logger.info(f"✔ {desc}{right}")