Spaces:
Sleeping
Sleeping
| """Generate documentation for Cyclopts applications.""" | |
| from pathlib import Path | |
| from typing import Annotated | |
| from cyclopts.cli import app | |
| from cyclopts.docs.types import ( | |
| FORMAT_ALIASES, | |
| DocFormat, | |
| normalize_format, | |
| ) | |
| from cyclopts.group import Group | |
| from cyclopts.loader import load_app_from_script | |
| from cyclopts.parameter import Parameter | |
| from cyclopts.utils import UNSET | |
| def _format_group_validator(argument_collection): | |
| format_arg = argument_collection.get("--format") | |
| output_arg = argument_collection.get("--output") | |
| if format_arg.value is UNSET: | |
| if output_arg.value is UNSET: | |
| raise ValueError('"--format" must be specified when output path is not provided.') | |
| if not output_arg.value or not hasattr(output_arg.value, "suffix"): | |
| raise ValueError('"--output" must be a valid file path when format is not specified.') | |
| suffix = output_arg.value.suffix.lower() | |
| if not suffix: | |
| raise ValueError( | |
| "Output file must have an extension to infer format (e.g., .md, .html, .rst). " | |
| 'Please specify "--format" explicitly or add an extension to the output file.' | |
| ) | |
| # Strip the leading period from suffix to look up in FORMAT_ALIASES | |
| suffix_key = suffix.lstrip(".") | |
| if not suffix_key: | |
| raise ValueError( | |
| "Invalid file extension. Output file must have a valid extension after the period. " | |
| 'Please specify "--format" explicitly.' | |
| ) | |
| inferred_format = FORMAT_ALIASES.get(suffix_key) | |
| if inferred_format is None: | |
| supported_extensions = [f".{ext}" for ext in FORMAT_ALIASES.keys() if len(ext) <= 4] | |
| raise ValueError( | |
| f'Cannot infer format from output extension "{suffix}". ' | |
| f"Supported extensions: {', '.join(sorted(set(supported_extensions)))}. " | |
| f'Please specify "--format" explicitly.' | |
| ) | |
| format_arg.value = inferred_format | |
| format_group = Group(validator=_format_group_validator) | |
| def generate_docs( | |
| script: str, | |
| output: Annotated[Path | None, Parameter(alias="-o", group=format_group)] = None, | |
| *, | |
| format: Annotated[ | |
| DocFormat | None, | |
| Parameter(alias="-f", group=format_group), | |
| ] = None, | |
| include_hidden: bool = False, | |
| heading_level: int = 1, | |
| usage_name: str | None = None, | |
| ): | |
| """Generate documentation for a Cyclopts application. | |
| Parameters | |
| ---------- | |
| script : str | |
| Python script path, optionally with ``':app_object'`` notation to specify | |
| the App object. If not specified, will search for App objects in the | |
| script's global namespace. | |
| output : Optional[Path] | |
| Output file path. If not specified, prints to stdout. | |
| format : Optional[DocFormat] | |
| Output format for documentation. If not specified, inferred from output | |
| file extension. | |
| include_hidden : bool | |
| Include hidden commands in documentation. | |
| heading_level : int | |
| Starting heading level for markdown format. | |
| usage_name : Optional[str] | |
| Replace the app name shown in ``Usage:`` lines with this string. For | |
| example, ``"uv run cli"`` for an app whose runtime name is ``"cli"``. | |
| Headings and anchors are unaffected. Default is None. | |
| """ | |
| if format is None: # Handled by _format_group_validator | |
| raise ValueError("Must specify format.") | |
| format = normalize_format(format) | |
| app_obj, _ = load_app_from_script(script) | |
| docs_content = app_obj.generate_docs( | |
| output_format=format, | |
| include_hidden=include_hidden, | |
| heading_level=heading_level, | |
| usage_name=usage_name, | |
| ) | |
| if output: | |
| output.write_text(docs_content) | |
| else: | |
| print(docs_content) | |