File size: 6,714 Bytes
0ad74ed |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
from __future__ import annotations
import importlib
import shutil
import subprocess
from pathlib import Path
from typing import Annotated, Optional
import semantic_version
import typer
from tomlkit import dump, parse
import gradio
from gradio.analytics import custom_component_analytics
from gradio.cli.commands.components._docs_utils import (
get_deep,
)
from gradio.cli.commands.components.docs import run_command
from gradio.cli.commands.components.install_component import _get_executable_path
from gradio.cli.commands.display import LivePanelDisplay
gradio_template_path = Path(gradio.__file__).parent / "templates" / "frontend"
def _build(
path: Annotated[
Path, typer.Argument(help="The directory of the custom component.")
] = Path("."),
build_frontend: Annotated[
bool, typer.Option(help="Whether to build the frontend as well.")
] = True,
bump_version: Annotated[
bool, typer.Option(help="Whether to bump the version number automatically.")
] = False,
generate_docs: Annotated[
bool, typer.Option(help="Whether to generate the documentation as well.")
] = True,
python_path: Annotated[
Optional[str],
typer.Option(
help="Path to python executable. If None, will use the default path found by `which python3`. If python3 is not found, `which python` will be tried. If both fail an error will be raised."
),
] = None,
):
custom_component_analytics(
"build",
None,
None,
None,
None,
generate_docs=generate_docs,
bump_version=bump_version,
)
name = Path(path).resolve()
if not (name / "pyproject.toml").exists():
raise ValueError(f"Cannot find pyproject.toml file in {name}")
with LivePanelDisplay() as live:
live.update(
f":package: Building package in [orange3]{str(name.name)}[/]", add_sleep=0.2
)
pyproject_toml = parse((path / "pyproject.toml").read_text())
package_name = get_deep(pyproject_toml, ["project", "name"])
python_path = _get_executable_path(
"python", python_path, "--python-path", check_3=True
)
if not isinstance(package_name, str):
raise ValueError(
"Your pyproject.toml file does not have a [project] name field!"
)
try:
importlib.import_module(package_name) # type: ignore
except ModuleNotFoundError as e:
raise ValueError(
f"Your custom component package ({package_name}) is not installed! "
"Please install it with the gradio cc install command before building it."
) from e
if bump_version:
pyproject_toml = parse((path / "pyproject.toml").read_text())
version = semantic_version.Version(
pyproject_toml["project"]["version"] # type: ignore
).next_patch()
live.update(
f":1234: Using version [bold][magenta]{version}[/][/]. "
"Set [bold][magenta]--no-bump-version[/][/] to use the version in pyproject.toml file."
)
pyproject_toml["project"]["version"] = str(version) # type: ignore
with open(path / "pyproject.toml", "w", encoding="utf-8") as f:
dump(pyproject_toml, f)
else:
version = pyproject_toml["project"]["version"] # type: ignore
live.update(
f":1234: Package will use version [bold][magenta]{version}[/][/] defined in pyproject.toml file. "
"Set [bold][magenta]--bump-version[/][/] to automatically bump the version number."
)
if generate_docs:
_demo_dir = Path("demo").resolve()
_demo_name = "app.py"
_demo_path = _demo_dir / _demo_name
_readme_path = name / "README.md"
run_command(
live=live,
name=package_name,
suppress_demo_check=False,
pyproject_toml=pyproject_toml,
generate_space=True,
generate_readme=True,
type_mode="simple",
_demo_path=_demo_path,
_demo_dir=_demo_dir,
_readme_path=_readme_path,
space_url=None,
_component_dir=name,
simple=True,
)
if build_frontend:
live.update(":art: Building frontend")
component_directory = path.resolve()
node = shutil.which("node")
if not node:
raise ValueError(
"node must be installed in order to run build command."
)
gradio_node_path = subprocess.run(
[node, "-e", "console.log(require.resolve('@gradio/preview'))"],
cwd=Path(component_directory / "frontend"),
check=False,
capture_output=True,
)
if gradio_node_path.returncode != 0:
raise ValueError(
"Could not find `@gradio/preview`. Run `npm i -D @gradio/preview` in your frontend folder."
)
gradio_node_path = gradio_node_path.stdout.decode("utf-8").strip()
node_cmds = [
node,
gradio_node_path,
"--component-directory",
component_directory,
"--root",
gradio_template_path,
"--mode",
"build",
"--python-path",
python_path,
]
pipe = subprocess.run(
node_cmds, capture_output=True, text=True, check=False
)
if pipe.returncode != 0:
live.update(":red_square: Build failed!")
live.update(pipe.stderr)
live.update(pipe.stdout)
raise SystemExit("Frontend build failed")
else:
live.update(":white_check_mark: Build succeeded!")
cmds = [python_path, "-m", "build", str(name)]
live.update(f":construction_worker: Building... [grey37]({' '.join(cmds)})[/]")
pipe = subprocess.run(cmds, capture_output=True, text=True, check=False)
if pipe.returncode != 0:
live.update(":red_square: Build failed!")
live.update(pipe.stderr)
raise SystemExit("Python build failed")
else:
live.update(":white_check_mark: Build succeeded!")
live.update(
f":ferris_wheel: Wheel located in [orange3]{str(name / 'dist')}[/]"
)
|