Spaces:
Paused
Paused
| from __future__ import annotations | |
| import shutil | |
| from pathlib import Path | |
| from typing import Annotated, Optional | |
| import typer | |
| from rich import print | |
| from rich.panel import Panel | |
| from rich.prompt import Confirm, Prompt | |
| from tomlkit import dump, parse | |
| from gradio.analytics import custom_component_analytics | |
| from ..display import LivePanelDisplay | |
| from . import _create_utils | |
| from .install_component import _get_npm, _install_command | |
| def _create( | |
| name: Annotated[ | |
| str, | |
| typer.Argument( | |
| help="Name of the component. Preferably in camel case, i.e. MyTextBox." | |
| ), | |
| ], | |
| directory: Annotated[ | |
| Optional[Path], | |
| typer.Option( | |
| help="Directory to create the component in. Default is None. If None, will be created in <component-name> directory in the current directory." | |
| ), | |
| ] = None, | |
| package_name: Annotated[ | |
| Optional[str], | |
| typer.Option(help="Name of the package. Default is gradio_{name.lower()}"), | |
| ] = None, | |
| template: Annotated[ | |
| str, | |
| typer.Option( | |
| help="Component to use as a template. Should use exact name of python class." | |
| ), | |
| ] = "", | |
| install: Annotated[ | |
| bool, | |
| typer.Option( | |
| help="Whether to install the component in your current environment as a development install. Recommended for development." | |
| ), | |
| ] = True, | |
| npm_install: Annotated[ | |
| str, | |
| typer.Option(help="NPM install command to use. Default is 'npm install'."), | |
| ] = "npm install", | |
| pip_path: Annotated[ | |
| Optional[str], | |
| typer.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, | |
| overwrite: Annotated[ | |
| bool, | |
| typer.Option(help="Whether to overwrite the existing component if it exists."), | |
| ] = False, | |
| configure_metadata: Annotated[ | |
| bool, | |
| typer.Option( | |
| help="Whether to interactively configure project metadata based on user input" | |
| ), | |
| ] = True, | |
| ): | |
| custom_component_analytics( | |
| "create", | |
| template, | |
| None, | |
| None, | |
| None, | |
| npm_install=npm_install, | |
| ) | |
| if not directory: | |
| directory = Path(name.lower()) | |
| if not package_name: | |
| package_name = f"gradio_{name.lower()}" | |
| if directory.exists() and not overwrite: | |
| raise ValueError( | |
| f"The directory {directory.resolve()} already exists. " | |
| "Please set --overwrite flag or pass in the name " | |
| "of a directory that does not already exist via the --directory option." | |
| ) | |
| elif directory.exists() and overwrite: | |
| _create_utils.delete_contents(directory) | |
| directory.mkdir(exist_ok=overwrite) | |
| if _create_utils._in_test_dir(): | |
| npm_install = f"{shutil.which('pnpm')} i --ignore-scripts" | |
| else: | |
| npm_install = _get_npm(npm_install) | |
| with LivePanelDisplay() as live: | |
| live.update( | |
| f":building_construction: Creating component [orange3]{name}[/] in directory [orange3]{directory}[/]", | |
| add_sleep=0.2, | |
| ) | |
| if template: | |
| live.update(f":fax: Starting from template [orange3]{template}[/]") | |
| else: | |
| live.update(":page_facing_up: Creating a new component from scratch.") | |
| component = _create_utils._get_component_code(template) | |
| _create_utils._create_backend(name, component, directory, package_name) | |
| live.update(":snake: Created backend code", add_sleep=0.2) | |
| _create_utils._create_frontend( | |
| name.lower(), component, directory=directory, package_name=package_name | |
| ) | |
| live.update(":art: Created frontend code", add_sleep=0.2) | |
| if install: | |
| _install_command(directory, live, npm_install, pip_path) | |
| live._panel.stop() | |
| description = "A gradio custom component" | |
| keywords = [] | |
| if configure_metadata: | |
| print( | |
| Panel( | |
| "It is recommended to answer the following [bold][magenta]4 questions[/][/] to finish configuring your custom component's metadata." | |
| "\nYou can also answer them later by editing the [bold][magenta]pyproject.toml[/][/] file in your component directory." | |
| ) | |
| ) | |
| answer_qs = Confirm.ask("\nDo you want to answer them now?") | |
| pyproject_toml = parse((directory / "pyproject.toml").read_text()) | |
| if answer_qs: | |
| name = pyproject_toml["project"]["name"] # type: ignore | |
| description = Prompt.ask( | |
| "\n:pencil: Please enter a one sentence [bold][magenta]description[/][/] for your component" | |
| ) | |
| if description: | |
| pyproject_toml["project"]["description"] = description # type: ignore | |
| license_ = ( | |
| Prompt.ask( | |
| "\n:bookmark_tabs: Please enter a [bold][magenta]software license[/][/] for your component. Leave blank for 'apache-2.0'" | |
| ) | |
| or "apache-2.0" | |
| ) | |
| print(f":bookmark_tabs: Using license [bold][magenta]{license_}[/][/]") | |
| pyproject_toml["project"]["license"] = license_ # type: ignore | |
| requires_python = Prompt.ask( | |
| "\n:snake: Please enter the [bold][magenta]allowed python[/][/] versions for your component. Leave blank for '>=3.10'" | |
| ) | |
| requires_python = requires_python or ">=3.10" | |
| print( | |
| f":snake: Using requires-python of [bold][magenta]{requires_python}[/][/]" | |
| ) | |
| pyproject_toml["project"]["requires-python"] = ( # type: ignore | |
| requires_python or ">=3.10" | |
| ) | |
| print( | |
| "\n:label: Please add some keywords to help others discover your component." | |
| ) | |
| while True: | |
| keyword = Prompt.ask(":label: Leave blank to stop adding keywords") | |
| if keyword: | |
| keywords.append(keyword) | |
| else: | |
| break | |
| current_keywords = pyproject_toml["project"].get("keywords", []) # type: ignore | |
| pyproject_toml["project"]["keywords"] = current_keywords + keywords # type: ignore | |
| with open(directory / "pyproject.toml", "w", encoding="utf-8") as f: | |
| dump(pyproject_toml, f) | |
| (directory / "demo" / "requirements.txt").write_text(package_name) | |
| readme_path = Path(__file__).parent / "files" / "README.md" | |
| readme_contents = readme_path.read_text() | |
| tags = f", {', '.join(keywords)}" if keywords else "" | |
| template = f", {template}" | |
| readme_contents = ( | |
| readme_contents.replace("<<title>>", package_name) | |
| .replace("<<short-description>>", description) | |
| .replace("<<tags>>", tags) | |
| .replace("<<template>>", template) | |
| ) | |
| (directory / "README.md").write_text(readme_contents) | |
| print("\nComponent creation [bold][magenta]complete[/][/]!") | |