dntrplytch's picture
Upload folder using huggingface_hub
7d134e4 verified
from __future__ import annotations
import importlib
import re
from pathlib import Path
from typing import Any, Optional
import requests
import tomlkit as toml
from typer import Argument, Option
from typing_extensions import Annotated
from gradio.analytics import custom_component_analytics
from gradio.cli.commands.display import LivePanelDisplay
from ._docs_assets import css
from ._docs_utils import extract_docstrings, get_deep, make_markdown, make_space
def _docs(
path: Annotated[
Path, Argument(help="The directory of the custom component.")
] = Path("."),
demo_dir: Annotated[
Optional[Path], Option(help="Path to the demo directory.")
] = None,
demo_name: Annotated[Optional[str], Option(help="Name of the demo file.")] = None,
readme_path: Annotated[
Optional[Path], Option(help="Path to the README.md file.")
] = None,
space_url: Annotated[
Optional[str], Option(help="URL of the Space to use for the demo.")
] = None,
generate_space: Annotated[
bool,
Option(
help="Create a documentation space for the custom compone.", is_flag=True
),
] = True,
generate_readme: Annotated[
bool,
Option(help="Create a README.md file for the custom component.", is_flag=True),
] = True,
suppress_demo_check: Annotated[
bool,
Option(
help="Suppress demo warnings and errors.",
is_flag=True,
),
] = False,
):
"""Runs the documentation generator."""
custom_component_analytics(
"docs",
None,
None,
None,
None,
)
_component_dir = Path(path).resolve()
_demo_dir = Path(demo_dir).resolve() if demo_dir else Path("demo").resolve()
_demo_name = demo_name if demo_name else "app.py"
_demo_path = _demo_dir / _demo_name
_readme_path = (
Path(readme_path).resolve() if readme_path else _component_dir / "README.md"
)
if not generate_space and not generate_readme:
raise ValueError("Must generate at least one of space or readme")
with LivePanelDisplay() as live:
live.update(
f":page_facing_up: Generating documentation for [orange3]{str(_component_dir.name)}[/]",
add_sleep=0.2,
)
live.update(
f":eyes: Reading project metadata from [orange3]{_component_dir}/pyproject.toml[/]\n"
)
if not (_component_dir / "pyproject.toml").exists():
raise ValueError(
f"Cannot find pyproject.toml file in [orange3]{_component_dir}[/]"
)
with open(_component_dir / "pyproject.toml", encoding="utf-8") as f:
data = toml.loads(f.read())
name = get_deep(data, ["project", "name"])
if not isinstance(name, str):
raise ValueError("Name not found in pyproject.toml")
run_command(
live=live,
name=name,
suppress_demo_check=suppress_demo_check,
pyproject_toml=data,
generate_space=generate_space,
generate_readme=generate_readme,
type_mode="simple",
_demo_path=_demo_path,
_demo_dir=_demo_dir,
_readme_path=_readme_path,
space_url=space_url,
_component_dir=_component_dir,
)
def run_command(
live: LivePanelDisplay,
name: str,
pyproject_toml: dict[str, Any],
suppress_demo_check: bool,
generate_space: bool,
generate_readme: bool,
type_mode: str,
_demo_path: Path,
_demo_dir: Path,
_readme_path: Path,
space_url: str | None,
_component_dir: Path,
simple: bool = False,
):
with open(_demo_path, encoding="utf-8") as f:
demo = f.read()
pypi_exists = requests.get(f"https://pypi.org/pypi/{name}/json").status_code
pypi_exists = pypi_exists == 200 or False
local_version = get_deep(pyproject_toml, ["project", "version"])
description = str(get_deep(pyproject_toml, ["project", "description"]) or "")
repo = get_deep(pyproject_toml, ["project", "urls", "repository"])
space = (
space_url
if space_url
else get_deep(pyproject_toml, ["project", "urls", "space"])
)
if not local_version and not pypi_exists:
raise ValueError(
f"Cannot find version in pyproject.toml or on PyPI for [orange3]{name}[/].\nIf you have just published to PyPI, please wait a few minutes and try again."
)
module = importlib.import_module(name)
(docs, type_mode) = extract_docstrings(module)
if generate_space:
if not simple:
live.update(":computer: [blue]Generating space.[/]")
source = make_space(
docs=docs,
name=name,
description=description,
local_version=local_version
if local_version is None
else str(local_version),
demo=demo,
space=space if space is None else str(space),
repo=repo if repo is None else str(repo),
pypi_exists=pypi_exists,
suppress_demo_check=suppress_demo_check,
)
with open(_demo_dir / "space.py", "w", encoding="utf-8") as f:
f.write(source)
if not simple:
live.update(
f":white_check_mark: Space created in [orange3]{_demo_dir}/space.py[/]\n"
)
with open(_demo_dir / "css.css", "w", encoding="utf-8") as f:
f.write(css)
if generate_readme:
if not simple:
live.update(":pencil: [blue]Generating README.[/]")
readme = make_markdown(
docs, name, description, local_version, demo, space, repo, pypi_exists
)
readme_content = Path(_readme_path).read_text()
with open(_readme_path, "w", encoding="utf-8") as f:
yaml_regex = re.search(
"(?:^|[\r\n])---[\n\r]+([\\S\\s]*?)[\n\r]+---([\n\r]|$)", readme_content
)
if yaml_regex is not None:
readme = readme_content[: yaml_regex.span()[-1]] + readme
f.write(readme)
if not simple:
live.update(
f":white_check_mark: README generated in [orange3]{_readme_path}[/]"
)
if simple:
short_readme_path = Path(_readme_path).relative_to(_component_dir)
short_demo_path = Path(_demo_dir / "space.py").relative_to(_component_dir)
live.update(
f":white_check_mark: Documentation generated in [orange3]{short_demo_path}[/] and [orange3]{short_readme_path}[/]. Pass --no-generate-docs to disable auto documentation."
)
if type_mode == "simple":
live.update(
"\n:orange_circle: [red]The docs were generated in simple mode. Updating python to a version greater than 3.9 will result in richer documentation.[/]"
)