|
from __future__ import annotations |
|
|
|
import shutil |
|
import subprocess |
|
from pathlib import Path |
|
from typing import Annotated, Optional |
|
|
|
from rich.markup import escape |
|
from typer import Argument, Option |
|
|
|
from gradio.cli.commands.display import LivePanelDisplay |
|
from gradio.utils import set_directory |
|
|
|
|
|
def _get_npm(npm_install: str): |
|
npm_install = npm_install.strip() |
|
if npm_install == "npm install": |
|
npm = shutil.which("npm") |
|
if not npm: |
|
raise ValueError( |
|
"By default, the install command uses npm to install " |
|
"the frontend dependencies. Please install npm or pass your own install command " |
|
"via the --npm-install option." |
|
) |
|
npm_install = f"{npm} install" |
|
return npm_install |
|
|
|
|
|
def _get_executable_path( |
|
executable: str, |
|
executable_path: str | None, |
|
cli_arg_name: str, |
|
check_3: bool = False, |
|
) -> str: |
|
"""Get the path to an executable, either from the provided path or from the PATH environment variable. |
|
|
|
The value of executable_path takes precedence in case the value in PATH is incorrect. |
|
This should give more control to the developer in case their envrinment is not set up correctly. |
|
|
|
If check_3 is True, we append 3 to the executable name to give python3 priority over python (same for pip). |
|
""" |
|
if executable_path: |
|
if not Path(executable_path).exists() or not Path(executable_path).is_file(): |
|
raise ValueError( |
|
f"The provided {executable} path ({executable_path}) does not exist or is not a file." |
|
) |
|
return executable_path |
|
path = shutil.which(executable) |
|
if check_3: |
|
path = shutil.which(f"{executable}3") or path |
|
if not path: |
|
raise ValueError( |
|
f"Could not find {executable}. Please ensure it is installed and in your PATH or pass the {cli_arg_name} parameter." |
|
) |
|
return path |
|
|
|
|
|
def _install_command( |
|
directory: Path, live: LivePanelDisplay, npm_install: str, pip_path: str | None |
|
): |
|
pip_executable_path = _get_executable_path( |
|
"pip", executable_path=pip_path, cli_arg_name="--pip-path", check_3=True |
|
) |
|
cmds = [pip_executable_path, "install", "-e", f"{str(directory)}[dev]"] |
|
live.update( |
|
f":construction_worker: Installing python... [grey37]({escape(' '.join(cmds))})[/]" |
|
) |
|
pipe = subprocess.run(cmds, capture_output=True, text=True, check=False) |
|
|
|
if pipe.returncode != 0: |
|
live.update(":red_square: Python installation [bold][red]failed[/][/]") |
|
live.update(pipe.stderr) |
|
raise SystemExit("Python installation failed") |
|
|
|
else: |
|
live.update(":white_check_mark: Python install succeeded!") |
|
|
|
live.update( |
|
f":construction_worker: Installing javascript... [grey37]({npm_install})[/]" |
|
) |
|
with set_directory(directory / "frontend"): |
|
pipe = subprocess.run( |
|
npm_install.split(), capture_output=True, text=True, check=False |
|
) |
|
if pipe.returncode != 0: |
|
live.update(":red_square: NPM install [bold][red]failed[/][/]") |
|
live.update(pipe.stdout) |
|
live.update(pipe.stderr) |
|
raise SystemExit("NPM install failed") |
|
else: |
|
live.update(":white_check_mark: NPM install succeeded!") |
|
|
|
|
|
def _install( |
|
directory: Annotated[ |
|
Path, Argument(help="The directory containing the custom components.") |
|
] = Path("."), |
|
npm_install: Annotated[ |
|
str, Option(help="NPM install command to use. Default is 'npm install'.") |
|
] = "npm install", |
|
pip_path: Annotated[ |
|
Optional[str], |
|
Option( |
|
help="Path to pip executable. If None, will use the default path found by `which pip3`. If pip3 is not found, `which pip` will be tried. If both fail an error will be raised." |
|
), |
|
] = None, |
|
): |
|
npm_install = _get_npm(npm_install) |
|
with LivePanelDisplay() as live: |
|
_install_command(directory, live, npm_install, pip_path) |
|
|