1*bb4ee6a4SAndroid Build Coastguard Worker#!/usr/bin/env python3 2*bb4ee6a4SAndroid Build Coastguard Worker# Copyright 2023 The ChromiumOS Authors 3*bb4ee6a4SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be 4*bb4ee6a4SAndroid Build Coastguard Worker# found in the LICENSE file. 5*bb4ee6a4SAndroid Build Coastguard Worker 6*bb4ee6a4SAndroid Build Coastguard Worker""" 7*bb4ee6a4SAndroid Build Coastguard WorkerImplements styles for `Command.fg(style=)` that use `rich` terminal UI features. 8*bb4ee6a4SAndroid Build Coastguard Worker""" 9*bb4ee6a4SAndroid Build Coastguard Worker 10*bb4ee6a4SAndroid Build Coastguard Workerimport subprocess 11*bb4ee6a4SAndroid Build Coastguard Workerfrom typing import List 12*bb4ee6a4SAndroid Build Coastguard Worker 13*bb4ee6a4SAndroid Build Coastguard Workerfrom .util import ensure_packages_exist 14*bb4ee6a4SAndroid Build Coastguard Worker 15*bb4ee6a4SAndroid Build Coastguard Workerensure_packages_exist("rich") 16*bb4ee6a4SAndroid Build Coastguard Workerimport rich 17*bb4ee6a4SAndroid Build Coastguard Workerimport rich.console 18*bb4ee6a4SAndroid Build Coastguard Workerimport rich.live 19*bb4ee6a4SAndroid Build Coastguard Workerimport rich.spinner 20*bb4ee6a4SAndroid Build Coastguard Workerimport rich.text 21*bb4ee6a4SAndroid Build Coastguard Worker 22*bb4ee6a4SAndroid Build Coastguard Worker 23*bb4ee6a4SAndroid Build Coastguard Workerclass Styles(object): 24*bb4ee6a4SAndroid Build Coastguard Worker "A collection of methods that can be passed to `Command.fg(style=)`" 25*bb4ee6a4SAndroid Build Coastguard Worker 26*bb4ee6a4SAndroid Build Coastguard Worker @staticmethod 27*bb4ee6a4SAndroid Build Coastguard Worker def live_truncated(num_lines: int = 8): 28*bb4ee6a4SAndroid Build Coastguard Worker "Prints only the last `num_lines` of output while the program is running and successful." 29*bb4ee6a4SAndroid Build Coastguard Worker 30*bb4ee6a4SAndroid Build Coastguard Worker def output(process: "subprocess.Popen[str]"): 31*bb4ee6a4SAndroid Build Coastguard Worker assert process.stdout 32*bb4ee6a4SAndroid Build Coastguard Worker spinner = rich.spinner.Spinner("dots") 33*bb4ee6a4SAndroid Build Coastguard Worker lines: List[rich.text.Text] = [] 34*bb4ee6a4SAndroid Build Coastguard Worker stdout: List[str] = [] 35*bb4ee6a4SAndroid Build Coastguard Worker with rich.live.Live(refresh_per_second=30, transient=True) as live: 36*bb4ee6a4SAndroid Build Coastguard Worker for line in iter(process.stdout.readline, ""): 37*bb4ee6a4SAndroid Build Coastguard Worker stdout.append(line.strip()) 38*bb4ee6a4SAndroid Build Coastguard Worker lines.append(rich.text.Text.from_ansi(line.strip(), no_wrap=True)) 39*bb4ee6a4SAndroid Build Coastguard Worker while len(lines) > num_lines: 40*bb4ee6a4SAndroid Build Coastguard Worker lines.pop(0) 41*bb4ee6a4SAndroid Build Coastguard Worker live.update(rich.console.Group(rich.text.Text("…"), *lines, spinner)) 42*bb4ee6a4SAndroid Build Coastguard Worker if process.wait() == 0: 43*bb4ee6a4SAndroid Build Coastguard Worker console.print(rich.console.Group(rich.text.Text("…"), *lines)) 44*bb4ee6a4SAndroid Build Coastguard Worker else: 45*bb4ee6a4SAndroid Build Coastguard Worker for line in stdout: 46*bb4ee6a4SAndroid Build Coastguard Worker print(line) 47*bb4ee6a4SAndroid Build Coastguard Worker 48*bb4ee6a4SAndroid Build Coastguard Worker return output 49*bb4ee6a4SAndroid Build Coastguard Worker 50*bb4ee6a4SAndroid Build Coastguard Worker @staticmethod 51*bb4ee6a4SAndroid Build Coastguard Worker def quiet_with_progress(title: str): 52*bb4ee6a4SAndroid Build Coastguard Worker "Prints only the last `num_lines` of output while the program is running and successful." 53*bb4ee6a4SAndroid Build Coastguard Worker 54*bb4ee6a4SAndroid Build Coastguard Worker def output(process: "subprocess.Popen[str]"): 55*bb4ee6a4SAndroid Build Coastguard Worker assert process.stdout 56*bb4ee6a4SAndroid Build Coastguard Worker with rich.live.Live( 57*bb4ee6a4SAndroid Build Coastguard Worker rich.spinner.Spinner("dots", title), refresh_per_second=30, transient=True 58*bb4ee6a4SAndroid Build Coastguard Worker ): 59*bb4ee6a4SAndroid Build Coastguard Worker stdout = process.stdout.read() 60*bb4ee6a4SAndroid Build Coastguard Worker 61*bb4ee6a4SAndroid Build Coastguard Worker if process.wait() == 0: 62*bb4ee6a4SAndroid Build Coastguard Worker console.print(f"[green]OK[/green] {title}") 63*bb4ee6a4SAndroid Build Coastguard Worker else: 64*bb4ee6a4SAndroid Build Coastguard Worker print(stdout) 65*bb4ee6a4SAndroid Build Coastguard Worker console.print(f"[red]ERR[/red] {title}") 66*bb4ee6a4SAndroid Build Coastguard Worker 67*bb4ee6a4SAndroid Build Coastguard Worker return output 68*bb4ee6a4SAndroid Build Coastguard Worker 69*bb4ee6a4SAndroid Build Coastguard Worker 70*bb4ee6a4SAndroid Build Coastguard Workerconsole = rich.console.Console() 71