xref: /aosp_15_r20/external/crosvm/tools/impl/tui.py (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
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