-
Notifications
You must be signed in to change notification settings - Fork 2.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Gradio custom component publish #6098
Changes from 11 commits
87c3bb1
8764a6a
407b0ac
8724407
6b1ded2
1b459e4
73fed5f
b83e637
40ed470
a65b197
4d9c8ef
a334285
74fedfc
c8a6ee1
fb61447
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"gradio": minor | ||
--- | ||
|
||
feat:Gradio custom component publish |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
--- | ||
tags: [gradio-custom-component, gradio-custom-component-template-{{ template }}] | ||
title: {theme_name} | ||
colorFrom: orange | ||
colorTo: purple | ||
sdk: gradio | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Confused about this -- why is the sdk gradio if this is a Docker Space? I'm probably missing something There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this file not the readme in the uploaded Space? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yea sorry I need to delete this file! It's not used right now. |
||
sdk_version: {gradio_version} | ||
app_file: app.py | ||
pinned: false | ||
license: apache-2.0 | ||
--- |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,232 @@ | ||
import random | ||
import re | ||
import shutil | ||
import tempfile | ||
from pathlib import Path | ||
from typing import Optional | ||
|
||
from huggingface_hub import HfApi | ||
from rich import print | ||
from rich.console import Console | ||
from rich.panel import Panel | ||
from rich.prompt import Confirm, Prompt | ||
from typer import Argument, Option | ||
from typing_extensions import Annotated | ||
|
||
from gradio.cli.commands.components._create_utils import PATTERN_RE | ||
|
||
colors = ["red", "yellow", "green", "blue", "indigo", "purple", "pink", "gray"] | ||
|
||
PYPI_REGISTER_URL = "https://pypi.org/account/register/" | ||
|
||
README_CONTENTS = """ | ||
--- | ||
tags: [gradio-custom-component{template}] | ||
freddyaboulton marked this conversation as resolved.
Show resolved
Hide resolved
|
||
title: {package_name} V{version} | ||
colorFrom: {color_from} | ||
colorTo: {color_to} | ||
sdk: docker | ||
pinned: false | ||
license: apache-2.0 | ||
--- | ||
""" | ||
|
||
DOCKERFILE = """ | ||
FROM python:3.9 | ||
|
||
WORKDIR /code | ||
|
||
COPY . . | ||
|
||
RUN pip install --no-cache-dir -r requirements.txt | ||
|
||
ENV PYTHONUNBUFFERED=1 \ | ||
GRADIO_ALLOW_FLAGGING=never \ | ||
GRADIO_NUM_PORTS=1 \ | ||
GRADIO_SERVER_NAME=0.0.0.0 \ | ||
GRADIO_SERVER_PORT=7860 \ | ||
SYSTEM=spaces | ||
|
||
CMD ["python", "app.py"] | ||
""" | ||
|
||
|
||
def _ignore(s, names): | ||
ignored = [] | ||
for n in names: | ||
if "__pycache__" in n or n.startswith("dist") or n.startswith("node_modules"): | ||
ignored.append(n) | ||
return ignored | ||
|
||
|
||
def _publish( | ||
wheel_file: Annotated[Path, Argument(help="Path to the wheel directory.")], | ||
upload_pypi: Annotated[bool, Option(help="Whether to upload to PyPI.")] = True, | ||
pypi_username: Annotated[str, Option(help="The username for PyPI.")] = "", | ||
pypi_password: Annotated[str, Option(help="The password for PyPI.")] = "", | ||
upload_demo: Annotated[ | ||
bool, Option(help="Whether to upload demo to HuggingFace.") | ||
] = True, | ||
demo_dir: Annotated[ | ||
Optional[Path], Option(help="Path to the demo directory.") | ||
] = None, | ||
source_dir: Annotated[ | ||
Optional[Path], | ||
Option( | ||
help="Path to the source directory of the custom component. To share with community." | ||
), | ||
] = None, | ||
hf_token: Annotated[ | ||
Optional[str], | ||
Option( | ||
help="HuggingFace token for uploading demo. Can be omitted if already logged in via huggingface cli." | ||
), | ||
] = None, | ||
): | ||
upload_source = source_dir is not None | ||
console = Console() | ||
wheel_file = wheel_file.resolve() | ||
if not wheel_file.suffix == ".whl": | ||
raise ValueError( | ||
"Please provide a wheel file. It must end with .whl. Run `gradio cc build` to create a wheel file." | ||
) | ||
if not wheel_file.exists(): | ||
raise ValueError( | ||
f"{wheel_file} does not exist. Run `gradio cc build` to create a wheel file." | ||
) | ||
|
||
if upload_pypi and (not pypi_username or not pypi_password): | ||
panel = Panel( | ||
"It is recommended to upload your component to pypi so that [bold][magenta]anyone[/][/] " | ||
"can install it with [bold][magenta]pip install[/][/].\n\n" | ||
f"A PyPi account is needed. If you do not have an account, register account here: [blue]{PYPI_REGISTER_URL}[/]", | ||
) | ||
print(panel) | ||
upload_pypi = Confirm.ask(":snake: Upload to pypi?") | ||
if upload_pypi: | ||
pypi_username = Prompt.ask(":laptop_computer: Enter your pypi username") | ||
pypi_password = Prompt.ask( | ||
":closed_lock_with_key: Enter your pypi password", password=True | ||
) | ||
if upload_pypi: | ||
try: | ||
from twine.commands.upload import upload as twine_upload # type: ignore | ||
from twine.settings import Settings # type: ignore | ||
except (ImportError, ModuleNotFoundError) as e: | ||
raise ValueError( | ||
"The twine library must be installed to publish to pypi." | ||
"Install it with pip, pip install twine." | ||
) from e | ||
|
||
twine_settings = Settings(username=pypi_username, password=pypi_password) | ||
try: | ||
twine_upload(twine_settings, [str(wheel_file)]) | ||
except Exception: | ||
console.print_exception() | ||
if upload_demo and not demo_dir: | ||
panel = Panel( | ||
"It is recommended you upload a demo of your component to [blue]https://huggingface.co/spaces[/] " | ||
"so that anyone can try it from their browser." | ||
) | ||
print(panel) | ||
upload_demo = Confirm.ask(":hugging_face: Upload demo?") | ||
if upload_demo: | ||
panel = Panel( | ||
"Please provide the path to the [magenta]demo directory[/] for your custom component.\n\n" | ||
"This directory should contain [magenta]all the files[/] it needs to run successfully.\n\n" | ||
"Please make sure the gradio app is in an [magenta]app.py[/] file.\n\n" | ||
"If you need additional python requirements, add a [magenta]requirements.txt[/] file to this directory." | ||
) | ||
print(panel) | ||
demo_dir = Path( | ||
Prompt.ask(":roller_coaster: Please enter demo directory") | ||
).resolve() | ||
if upload_demo and not source_dir: | ||
panel = Panel( | ||
"It is recommended that you share your [magenta]source code[/] so that others can learn from and improve your component." | ||
) | ||
print(panel) | ||
upload_source = Confirm.ask(":books: Would you like to share your source code?") | ||
if upload_source: | ||
source_dir = Path( | ||
Prompt.ask( | ||
":page_with_curl: Enter the path to the source code [magenta]directory[/] here" | ||
) | ||
).resolve() | ||
if upload_demo: | ||
assert demo_dir | ||
if not (demo_dir / "app.py").exists(): | ||
raise FileNotFoundError("app.py not found in demo directory.") | ||
additional_reqs = [ | ||
"https://gradio-builds.s3.amazonaws.com/4.0/attempt-05/gradio-4.0.0-py3-none-any.whl", | ||
"https://gradio-builds.s3.amazonaws.com/4.0/attempt-05/gradio_client-0.7.0b0-py3-none-any.whl", | ||
wheel_file.name, | ||
] | ||
if (demo_dir / "requirements.txt").exists(): | ||
reqs = (demo_dir / "requirements.txt").read_text().splitlines() | ||
reqs += additional_reqs | ||
else: | ||
reqs = additional_reqs | ||
|
||
color_from, color_to = random.choice(colors), random.choice(colors) | ||
package_name, version = wheel_file.name.split("-")[:2] | ||
with tempfile.TemporaryDirectory() as tempdir: | ||
shutil.copytree( | ||
str(demo_dir), | ||
str(tempdir), | ||
dirs_exist_ok=True, | ||
) | ||
if source_dir: | ||
shutil.copytree( | ||
str(source_dir), | ||
str(Path(tempdir) / "src"), | ||
dirs_exist_ok=True, | ||
ignore=_ignore, | ||
) | ||
reqs_txt = Path(tempdir) / "requirements.txt" | ||
reqs_txt.write_text("\n".join(reqs)) | ||
readme = Path(tempdir) / "README.md" | ||
template = "" | ||
if upload_source and source_dir: | ||
match = re.search( | ||
PATTERN_RE, (source_dir / "pyproject.toml").read_text() | ||
) | ||
if match: | ||
template = f", {match.group(0)}" | ||
|
||
readme.write_text( | ||
README_CONTENTS.format( | ||
package_name=package_name, | ||
version=version, | ||
color_from=color_from, | ||
color_to=color_to, | ||
template=template, | ||
) | ||
) | ||
dockerfile = Path(tempdir) / "Dockerfile" | ||
dockerfile.write_text(DOCKERFILE) | ||
|
||
api = HfApi() | ||
new_space = api.create_repo( | ||
repo_id=f"{package_name}", | ||
repo_type="space", | ||
exist_ok=True, | ||
private=False, | ||
space_sdk="docker", | ||
token=hf_token, | ||
) | ||
api.upload_folder( | ||
repo_id=new_space.repo_id, | ||
folder_path=tempdir, | ||
token=hf_token, | ||
repo_type="space", | ||
) | ||
api.upload_file( | ||
repo_id=new_space.repo_id, | ||
path_or_fileobj=str(wheel_file), | ||
path_in_repo=wheel_file.name, | ||
token=hf_token, | ||
repo_type="space", | ||
) | ||
print("\n") | ||
print(f"Demo uploaded to {new_space}!") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo,
{theme_name}
should be presumably the name of the component