From 11d67ae7529e0838565e4131b185c413489c5aa6 Mon Sep 17 00:00:00 2001 From: Freddy Boulton Date: Wed, 25 Oct 2023 22:12:30 -0400 Subject: [PATCH] Add a stand-alone install command and tidy-up the fallback template (#6092) * Add code * add changeset * Add test * Make install default * Better error message --------- Co-authored-by: gradio-pr-bot --- .changeset/mighty-coats-do.md | 6 ++ gradio/cli/cli.py | 8 +++ gradio/cli/commands/components/app.py | 4 ++ gradio/cli/commands/components/create.py | 45 ++----------- .../commands/components/install_component.py | 63 +++++++++++++++++++ js/fallback/Index.svelte | 3 - test/test_gradio_component_cli.py | 27 +++++++- 7 files changed, 110 insertions(+), 46 deletions(-) create mode 100644 .changeset/mighty-coats-do.md create mode 100644 gradio/cli/commands/components/install_component.py diff --git a/.changeset/mighty-coats-do.md b/.changeset/mighty-coats-do.md new file mode 100644 index 0000000000..cc1ea6d414 --- /dev/null +++ b/.changeset/mighty-coats-do.md @@ -0,0 +1,6 @@ +--- +"@gradio/fallback": minor +"gradio": minor +--- + +feat:Add a stand-alone install command and tidy-up the fallback template diff --git a/gradio/cli/cli.py b/gradio/cli/cli.py index d20fd573c3..d87cf4d4b9 100644 --- a/gradio/cli/cli.py +++ b/gradio/cli/cli.py @@ -2,6 +2,7 @@ import sys import typer from gradio_client.cli import deploy_discord # type: ignore +from rich.console import Console from .commands import custom_component, deploy, print_environment_info, reload @@ -27,5 +28,12 @@ def cli(): elif args[0] in {"cc", "component"}: sys.argv = sys.argv[1:] custom_component() + elif args[0] in {"build", "dev", "create", "show", "publish"}: + try: + error = f"gradio {args[0]} is not a valid command. Did you mean `gradio cc {args[0]}` or `gradio component {args[0]}`?." + raise ValueError(error) + except ValueError: + console = Console() + console.print_exception() else: typer.run(reload) diff --git a/gradio/cli/commands/components/app.py b/gradio/cli/commands/components/app.py index 8aeda37fab..ce8fc79188 100644 --- a/gradio/cli/commands/components/app.py +++ b/gradio/cli/commands/components/app.py @@ -3,6 +3,7 @@ from typer import Typer from .build import _build from .create import _create from .dev import _dev +from .install_component import _install from .show import _show app = Typer(help="Create and publish a new Gradio component") @@ -14,3 +15,6 @@ app.command( )(_build) app.command("dev", help="Launch the custom component demo in development mode.")(_dev) app.command("show", help="Show the list of available templates")(_show) +app.command("install", help="Install the custom component in the current environment")( + _install +) diff --git a/gradio/cli/commands/components/create.py b/gradio/cli/commands/components/create.py index a99dc7aa82..10f8a3bd1c 100644 --- a/gradio/cli/commands/components/create.py +++ b/gradio/cli/commands/components/create.py @@ -1,14 +1,12 @@ import shutil -import subprocess from pathlib import Path from typing import Optional import typer -from rich.markup import escape from typing_extensions import Annotated +from gradio.cli.commands.components.install_component import _get_npm, _install_command from gradio.cli.commands.display import LivePanelDisplay -from gradio.utils import set_directory from . import _create_utils @@ -41,7 +39,7 @@ def _create( typer.Option( help="Whether to install the component in your current environment as a development install. Recommended for development." ), - ] = False, + ] = True, npm_install: Annotated[ str, typer.Option(help="NPM install command to use. Default is 'npm install'."), @@ -69,17 +67,8 @@ def _create( if _create_utils._in_test_dir(): npm_install = f"{shutil.which('pnpm')} i --ignore-scripts" - - 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" + else: + npm_install = _get_npm(npm_install) with LivePanelDisplay() as live: live.update( @@ -102,28 +91,4 @@ def _create( live.update(":art: Created frontend code", add_sleep=0.2) if install: - cmds = [shutil.which("pip"), "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) - - if pipe.returncode != 0: - live.update(":red_square: Python installation [bold][red]failed[/][/]") - live.update(pipe.stderr) - 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 - ) - if pipe.returncode != 0: - live.update(":red_square: NPM install [bold][red]failed[/][/]") - live.update(pipe.stdout) - live.update(pipe.stderr) - else: - live.update(":white_check_mark: NPM install succeeded!") + _install_command(directory, live, npm_install) diff --git a/gradio/cli/commands/components/install_component.py b/gradio/cli/commands/components/install_component.py new file mode 100644 index 0000000000..f10636d31d --- /dev/null +++ b/gradio/cli/commands/components/install_component.py @@ -0,0 +1,63 @@ +import shutil +import subprocess +from pathlib import Path + +from rich.markup import escape +from typer import Argument, Option +from typing_extensions import Annotated + +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 _install_command(directory: Path, live: LivePanelDisplay, npm_install: str): + cmds = [shutil.which("pip"), "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) + + if pipe.returncode != 0: + live.update(":red_square: Python installation [bold][red]failed[/][/]") + live.update(pipe.stderr) + 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) + if pipe.returncode != 0: + live.update(":red_square: NPM install [bold][red]failed[/][/]") + live.update(pipe.stdout) + live.update(pipe.stderr) + 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", +): + npm_install = _get_npm(npm_install) + with LivePanelDisplay() as live: + _install_command(directory, live, npm_install) diff --git a/js/fallback/Index.svelte b/js/fallback/Index.svelte index 604245af31..df41ecb0ab 100644 --- a/js/fallback/Index.svelte +++ b/js/fallback/Index.svelte @@ -11,9 +11,6 @@ export let elem_classes: string[] = []; export let visible = true; export let value = false; - // export let value_is_output = false; - // export let label = "Checkbox"; - // export let info: string | undefined = undefined; export let container = true; export let scale: number | null = null; export let min_width: number | undefined = undefined; diff --git a/test/test_gradio_component_cli.py b/test/test_gradio_component_cli.py index 6d641bd700..1512aa7427 100644 --- a/test/test_gradio_component_cli.py +++ b/test/test_gradio_component_cli.py @@ -6,6 +6,7 @@ import pytest from gradio.cli.commands.components._create_utils import OVERRIDES from gradio.cli.commands.components.build import _build from gradio.cli.commands.components.create import _create +from gradio.cli.commands.components.install_component import _install from gradio.cli.commands.components.show import _show @@ -33,7 +34,7 @@ from gradio.cli.commands.components.show import _show ], ) def test_template_override_component(template, tmp_path): - _create("MyComponent", tmp_path, template=template, overwrite=True) + _create("MyComponent", tmp_path, template=template, overwrite=True, install=False) app = (tmp_path / "demo" / "app.py").read_text() answer = textwrap.dedent( f""" @@ -55,12 +56,18 @@ def test_raise_error_component_template_does_not_exist(tmp_path): match="Cannot find NonExistentComponent in gradio.components or gradio.layouts", ): _create( - "MyComponent", tmp_path, template="NonExistentComponent", overwrite=True + "MyComponent", + tmp_path, + template="NonExistentComponent", + overwrite=True, + install=False, ) def test_do_not_replace_class_name_in_import_statement(tmp_path): - _create("MyImage", template="Image", directory=tmp_path, overwrite=True) + _create( + "MyImage", template="Image", directory=tmp_path, overwrite=True, install=False + ) code = (tmp_path / "backend" / "gradio_myimage" / "myimage.py").read_text() assert "from PIL import Image as _Image" in code assert "class MyImage" in code @@ -100,3 +107,17 @@ def test_build(tmp_path): assert template_dir.exists() and template_dir.is_dir() assert list(template_dir.glob("**/index.js")) assert (tmp_path / "dist").exists() and list((tmp_path / "dist").glob("*.whl")) + + +def test_install(tmp_path): + _create( + "TestTextbox", + template="Textbox", + directory=tmp_path, + overwrite=True, + install=False, + ) + + assert not (tmp_path / "frontend" / "node_modules").exists() + _install(tmp_path) + assert (tmp_path / "frontend" / "node_modules").exists()