diff --git a/src/cli/cpl/cli/command/project/build.py b/src/cli/cpl/cli/command/project/build.py index 91225ec7..6a37c2fc 100644 --- a/src/cli/cpl/cli/command/project/build.py +++ b/src/cli/cpl/cli/command/project/build.py @@ -5,6 +5,7 @@ from pathlib import Path import click from cpl.cli.cli import cli +from cpl.cli.model.project import Project from cpl.cli.utils.structure import get_project_by_name_or_path from cpl.cli.utils.venv import ensure_venv, get_venv_python from cpl.core.configuration import Configuration @@ -42,23 +43,33 @@ def build(project: str, dist: str = None, skip_py_build: bool = None, verbose: b create_pyproject_toml(project, dist_path / project.name) python = str(get_venv_python(venv)) - Console.spinner( + result = Console.spinner( "Building python package...", - lambda: ( - subprocess.run( - [ - python, - "-m", - "build", - "--outdir", - str(dist_path / project.name), - str(dist_path / project.name), - ], - check=True, - stdin=subprocess.DEVNULL if not verbose else None, - stdout=subprocess.DEVNULL if not verbose else None, - stderr=subprocess.DEVNULL if not verbose else None, - ) + lambda: subprocess.run( + [ + python, + "-m", + "build", + "--outdir", + str(dist_path / project.name), + str(dist_path / project.name), + ], + check=True, + stdin=subprocess.DEVNULL if not verbose else None, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, ), ) + + if result is None: + raise RuntimeError("Build process did not run") + + if verbose and result.stdout is not None: + Console.write_line(result.stdout.decode()) + + if result.returncode != 0 and result.stderr is not None: + if result.stderr is not None: + Console.error(str(result.stderr.decode())) + raise RuntimeError(f"Build process failed with exit code {result.returncode}") + Console.write_line(" Done!") diff --git a/src/core/cpl/core/console/_spinner.py b/src/core/cpl/core/console/_spinner.py index a9f28ab2..1dcdf301 100644 --- a/src/core/cpl/core/console/_spinner.py +++ b/src/core/cpl/core/console/_spinner.py @@ -1,7 +1,5 @@ -import os import shutil import sys -import multiprocessing import time from multiprocessing import Process @@ -21,18 +19,28 @@ class Spinner(Process): Foreground color of the spinner background_color: :class:`cpl.core.console.background_color.BackgroundColorEnum` Background color of the spinner + done_char: :class:`str` """ - def __init__(self, msg_len: int, foreground_color: ForegroundColorEnum, background_color: BackgroundColorEnum): + def __init__( + self, + foreground_color: ForegroundColorEnum, + background_color: BackgroundColorEnum, + done_char: str = None, + msg_len: int = None, + ): Process.__init__(self) - self._msg_len = msg_len self._foreground_color = foreground_color self._background_color = background_color self._is_spinning = True self._exit = False + assert done_char is None or len(done_char) == 1, "done_char must be a single character" + self._done_char = done_char or "✓" + self._msg_len = msg_len + @staticmethod def _spinner(): r"""Selects active spinner char""" @@ -53,43 +61,33 @@ class Spinner(Process): def run(self) -> None: r"""Entry point of process, shows the spinner""" - columns = 0 - if sys.platform == "win32": - columns = os.get_terminal_size().columns - else: - size = shutil.get_terminal_size(fallback=(80, 24)) - columns = max(1, size.columns) - - end_msg = "done" - - padding = columns - self._msg_len - len(end_msg) - if padding > 0: - print(f'{"" : >{padding}}', end="") - else: - print("", end="") + size = shutil.get_terminal_size(fallback=(80, 24)) + columns = max(1, size.columns) spinner = self._spinner() + color_args = self._get_color_args() + + if self._msg_len is not None: + columns = min(columns, self._msg_len + 2) + while self._is_spinning: - print(colored(f"{next(spinner): >{len(end_msg)}}", *self._get_color_args()), end="") + frame = next(spinner) + sys.stdout.write(f"\033[{columns}G") + print(colored(frame, *color_args), end="", flush=True) time.sleep(0.1) - back = "" - for i in range(0, len(end_msg)): - back += "\b" - - print(back, end="") + sys.stdout.write(f"\033[{columns}G") sys.stdout.flush() - if not self._exit: - print(colored(end_msg, *self._get_color_args()), end="") - def stop(self): r"""Stops the spinner""" self._is_spinning = False - super().terminate() time.sleep(0.1) + print("\b" + colored(self._done_char, *self._get_color_args()), end="", flush=True) + super().terminate() def exit(self): r"""Stops the spinner""" self._is_spinning = False self._exit = True time.sleep(0.1) + super().terminate() diff --git a/src/core/cpl/core/console/console.py b/src/core/cpl/core/console/console.py index 86e6fa9c..d45e3158 100644 --- a/src/core/cpl/core/console/console.py +++ b/src/core/cpl/core/console/console.py @@ -435,6 +435,8 @@ class Console: message: str, call: Callable, *args, + done_char: str = None, + full_width: bool = False, text_foreground_color: Union[str, ForegroundColorEnum] = None, spinner_foreground_color: Union[str, ForegroundColorEnum] = None, text_background_color: Union[str, BackgroundColorEnum] = None, @@ -484,7 +486,8 @@ class Console: cls.set_hold_back(True) spinner = None if not cls._disabled: - spinner = Spinner(len(message), spinner_foreground_color, spinner_background_color) + msg_len = None if full_width else len(message) + 1 + spinner = Spinner(spinner_foreground_color, spinner_background_color, done_char=done_char, msg_len=msg_len) spinner.start() return_value = None