diff --git a/docs/source/en/package_reference/cli.md b/docs/source/en/package_reference/cli.md index 2fee74b77c..3f1fbb0340 100644 --- a/docs/source/en/package_reference/cli.md +++ b/docs/source/en/package_reference/cli.md @@ -806,7 +806,7 @@ $ hf collections delete-item [OPTIONS] COLLECTION_SLUG ITEM_OBJECT_ID ### `hf collections info` -Get info about a collection on the Hub. Output is in JSON format. +Get info about a collection on the Hub. **Usage**: @@ -820,6 +820,7 @@ $ hf collections info [OPTIONS] COLLECTION_SLUG **Options**: +* `--format [agent|auto|human|json|quiet]`: Output format. [default: auto] * `--token TEXT`: A User Access Token generated from https://huggingface.co/settings/tokens. * `--help`: Show this message and exit. @@ -847,8 +848,7 @@ $ hf collections list [OPTIONS] * `--item TEXT`: Filter collections containing a specific item (e.g., "models/gpt2", "datasets/squad", "papers/2311.12983"). * `--sort [lastModified|trending|upvotes]`: Sort results by last modified, trending, or upvotes. * `--limit INTEGER`: Limit the number of results. [default: 10] -* `--format [table|json]`: Output format (table or json). [default: table] -* `-q, --quiet`: Print only IDs (one per line). +* `--format [agent|auto|human|json|quiet]`: Output format. [default: auto] * `--token TEXT`: A User Access Token generated from https://huggingface.co/settings/tokens. * `--help`: Show this message and exit. @@ -1284,8 +1284,7 @@ $ hf discussions list [OPTIONS] REPO_ID * `--author TEXT`: Filter by author or organization. * `--limit INTEGER`: Limit the number of results. [default: 30] * `--type, --repo-type [model|dataset|space]`: The type of repository (model, dataset, or space). [default: model] -* `--format [table|json]`: Output format (table or json). [default: table] -* `-q, --quiet`: Print only IDs (one per line). +* `--format [agent|auto|human|json|quiet]`: Output format. [default: auto] * `--token TEXT`: A User Access Token generated from https://huggingface.co/settings/tokens. * `--help`: Show this message and exit. @@ -1643,8 +1642,7 @@ $ hf endpoints list [OPTIONS] **Options**: * `--namespace TEXT`: The namespace associated with the Inference Endpoint. Defaults to the current user's namespace. -* `--format [table|json]`: Output format (table or json). [default: table] -* `-q, --quiet`: Print only IDs (one per line). +* `--format [agent|auto|human|json|quiet]`: Output format. [default: auto] * `--token TEXT`: A User Access Token generated from https://huggingface.co/settings/tokens. * `--help`: Show this message and exit. @@ -1906,8 +1904,7 @@ $ hf extensions list [OPTIONS] **Options**: -* `--format [table|json]`: Output format (table or json). [default: table] -* `-q, --quiet`: Print only IDs (one per line). +* `--format [agent|auto|human|json|quiet]`: Output format. [default: auto] * `--help`: Show this message and exit. Examples @@ -1956,8 +1953,7 @@ $ hf extensions search [OPTIONS] **Options**: -* `--format [table|json]`: Output format (table or json). [default: table] -* `-q, --quiet`: Print only IDs (one per line). +* `--format [agent|auto|human|json|quiet]`: Output format. [default: auto] * `--help`: Show this message and exit. Examples @@ -3640,7 +3636,7 @@ $ hf webhooks [OPTIONS] COMMAND [ARGS]... * `delete`: Delete a webhook permanently. * `disable`: Disable an active webhook. * `enable`: Enable a disabled webhook. -* `info`: Show full details for a single webhook as... +* `info`: Show full details for a single webhook. * `list`: List all webhooks for the current user. [alias: ls] * `update`: Update an existing webhook. @@ -3761,7 +3757,7 @@ Learn more ### `hf webhooks info` -Show full details for a single webhook as JSON. +Show full details for a single webhook. **Usage**: @@ -3775,6 +3771,7 @@ $ hf webhooks info [OPTIONS] WEBHOOK_ID **Options**: +* `--format [agent|auto|human|json|quiet]`: Output format. [default: auto] * `--token TEXT`: A User Access Token generated from https://huggingface.co/settings/tokens. * `--help`: Show this message and exit. @@ -3798,15 +3795,14 @@ $ hf webhooks list [OPTIONS] **Options**: -* `--format [table|json]`: Output format (table or json). [default: table] -* `-q, --quiet`: Print only IDs (one per line). +* `--format [agent|auto|human|json|quiet]`: Output format. [default: auto] * `--token TEXT`: A User Access Token generated from https://huggingface.co/settings/tokens. * `--help`: Show this message and exit. Examples $ hf webhooks ls $ hf webhooks ls --format json - $ hf webhooks ls -q + $ hf webhooks ls --format quiet Learn more Use `hf --help` for more information about a command. diff --git a/src/huggingface_hub/cli/collections.py b/src/huggingface_hub/cli/collections.py index 020d16d76f..c2c07b05ce 100644 --- a/src/huggingface_hub/cli/collections.py +++ b/src/huggingface_hub/cli/collections.py @@ -34,7 +34,6 @@ """ import enum -import json from typing import Annotated, get_args import typer @@ -42,16 +41,14 @@ from huggingface_hub.hf_api import CollectionItemType_T, CollectionSort_T from ._cli_utils import ( - FormatOpt, + FormatWithAutoOpt, LimitOpt, - OutputFormat, - QuietOpt, TokenOpt, api_object_to_dict, get_hf_api, - print_list_output, typer_factory, ) +from ._output import OutputFormatWithAuto, out # Build enums dynamically from Literal types to avoid duplication @@ -89,8 +86,7 @@ def collections_ls( typer.Option(help="Sort results by last modified, trending, or upvotes."), ] = None, limit: LimitOpt = 10, - format: FormatOpt = OutputFormat.table, - quiet: QuietOpt = False, + format: FormatWithAutoOpt = OutputFormatWithAuto.auto, token: TokenOpt = None, ) -> None: """List collections on the Hub.""" @@ -105,7 +101,7 @@ def collections_ls( limit=limit, ) ] - print_list_output(results, format=format, quiet=quiet) + out.table(results) @collections_cli.command( @@ -116,12 +112,13 @@ def collections_ls( ) def collections_info( collection_slug: Annotated[str, typer.Argument(help="The collection slug (e.g., 'username/collection-slug').")], + format: FormatWithAutoOpt = OutputFormatWithAuto.auto, token: TokenOpt = None, ) -> None: - """Get info about a collection on the Hub. Output is in JSON format.""" + """Get info about a collection on the Hub.""" api = get_hf_api(token=token) collection = api.get_collection(collection_slug) - print(json.dumps(api_object_to_dict(collection), indent=2)) + out.dict(collection) @collections_cli.command( @@ -161,8 +158,8 @@ def collections_create( private=private, exists_ok=exists_ok, ) - print(f"Collection created: {collection.url}") - print(json.dumps(api_object_to_dict(collection), indent=2)) + out.result("Collection created", url=collection.url) + out.dict(collection) @collections_cli.command( @@ -207,8 +204,8 @@ def collections_update( private=private, theme=theme, ) - print(f"Collection updated: {collection.url}") - print(json.dumps(api_object_to_dict(collection), indent=2)) + out.result("Collection updated", url=collection.url) + out.dict(collection) @collections_cli.command( @@ -229,7 +226,7 @@ def collections_delete( """Delete a collection from the Hub.""" api = get_hf_api(token=token) api.delete_collection(collection_slug, missing_ok=missing_ok) - print(f"Collection deleted: {collection_slug}") + out.result("Collection deleted", slug=collection_slug) @collections_cli.command( @@ -268,8 +265,8 @@ def collections_add_item( note=note, exists_ok=exists_ok, ) - print(f"Item added to collection: {collection_slug}") - print(json.dumps(api_object_to_dict(collection), indent=2)) + out.result("Item added to collection", slug=collection_slug) + out.dict(collection) @collections_cli.command( @@ -303,7 +300,7 @@ def collections_update_item( note=note, position=position, ) - print(f"Item updated in collection: {collection_slug}") + out.result("Item updated in collection", slug=collection_slug) @collections_cli.command("delete-item") @@ -328,4 +325,4 @@ def collections_delete_item( item_object_id=item_object_id, missing_ok=missing_ok, ) - print(f"Item deleted from collection: {collection_slug}") + out.result("Item deleted from collection", slug=collection_slug) diff --git a/src/huggingface_hub/cli/discussions.py b/src/huggingface_hub/cli/discussions.py index 51a95ead6e..24ea497712 100644 --- a/src/huggingface_hub/cli/discussions.py +++ b/src/huggingface_hub/cli/discussions.py @@ -27,20 +27,17 @@ from ._cli_utils import ( AuthorOpt, - FormatOpt, + FormatWithAutoOpt, LimitOpt, - OutputFormat, - QuietOpt, RepoIdArg, RepoType, RepoTypeOpt, TokenOpt, - _format_cell, api_object_to_dict, get_hf_api, - print_list_output, typer_factory, ) +from ._output import OutputFormatWithAuto, out class DiscussionStatus(str, enum.Enum): @@ -181,8 +178,7 @@ def discussion_list( author: AuthorOpt = None, limit: LimitOpt = 30, repo_type: RepoTypeOpt = RepoType.model, - format: FormatOpt = OutputFormat.table, - quiet: QuietOpt = False, + format: FormatWithAutoOpt = OutputFormatWithAuto.auto, token: TokenOpt = None, ) -> None: """List discussions and pull requests on a repo.""" @@ -217,21 +213,10 @@ def discussion_list( break items = [api_object_to_dict(d) for d in discussions] - - print_list_output( + out.table( items, - format=format, - quiet=quiet, - id_key="num", headers=["num", "title", "is_pull_request", "status", "author", "created_at"], - row_fn=lambda item: [ - f"#{item['num']}", - _format_cell(item.get("title", ""), max_len=50), - "PR" if item.get("is_pull_request") else "", - _format_status(str(item.get("status", ""))), - str(item.get("author", "")), - _format_cell(item.get("created_at", "")), - ], + id_key="num", alignments={"num": "right"}, ) @@ -360,10 +345,8 @@ def discussion_create( pull_request=pull_request, ) kind = "pull request" if pull_request else "discussion" - print(f"Created {kind} {ANSI.bold(f'#{discussion.num}')} on {ANSI.bold(repo_id)}") - if pull_request: - print(f"Push changes to: {ANSI.bold(f'refs/pr/{discussion.num}')}") - print(f"View on Hub: {ANSI.blue(discussion.url)}") + ref = f"refs/pr/{discussion.num}" if pull_request else None + out.result(f"Created {kind} #{discussion.num} on {repo_id}", num=discussion.num, url=discussion.url, ref=ref) @discussions_cli.command( @@ -404,7 +387,7 @@ def discussion_comment( comment=comment, repo_type=repo_type.value, ) - print(f"Commented on #{num} in {ANSI.bold(repo_id)}") + out.result(f"Commented on #{num} on {repo_id}", num=num, repo=repo_id) @discussions_cli.command( @@ -449,7 +432,7 @@ def discussion_close( comment=comment, repo_type=repo_type.value, ) - print(f"Closed #{num} in {ANSI.bold(repo_id)}") + out.result(f"Closed #{num} on {repo_id}", num=num, repo=repo_id) @discussions_cli.command( @@ -494,7 +477,7 @@ def discussion_reopen( comment=comment, repo_type=repo_type.value, ) - print(f"Reopened #{num} in {ANSI.bold(repo_id)}") + out.result(f"Reopened #{num} on {repo_id}", num=num, repo=repo_id) @discussions_cli.command( @@ -523,7 +506,7 @@ def discussion_rename( new_title=new_title, repo_type=repo_type.value, ) - print(f"Renamed #{num} to {ANSI.bold(new_title)} in {ANSI.bold(repo_id)}") + out.result(f"Renamed #{num} on {repo_id}", num=num, repo=repo_id, title=new_title) @discussions_cli.command( @@ -567,7 +550,7 @@ def discussion_merge( comment=comment, repo_type=repo_type.value, ) - print(f"Merged #{num} in {ANSI.bold(repo_id)}") + out.result(f"Merged #{num} on {repo_id}", num=num, repo=repo_id) @discussions_cli.command( @@ -590,6 +573,6 @@ def discussion_diff( repo_type=repo_type.value, ) if details.diff: - print(details.diff) + out.text(details.diff) else: - print("No diff available.") + out.text("No diff available.") diff --git a/src/huggingface_hub/cli/extensions.py b/src/huggingface_hub/cli/extensions.py index bce15e5820..88c60dc6e7 100644 --- a/src/huggingface_hub/cli/extensions.py +++ b/src/huggingface_hub/cli/extensions.py @@ -30,7 +30,8 @@ from huggingface_hub.errors import CLIError, CLIExtensionInstallError from huggingface_hub.utils import StatusLine, get_session, logging -from ._cli_utils import FormatOpt, OutputFormat, QuietOpt, print_list_output, typer_factory +from ._cli_utils import FormatWithAutoOpt, typer_factory +from ._output import OutputFormatWithAuto, out DEFAULT_EXTENSION_OWNER = "huggingface" @@ -159,7 +160,7 @@ def extension_exec( @extensions_cli.command("list | ls", examples=["hf extensions list"]) -def extension_list(format: FormatOpt = OutputFormat.table, quiet: QuietOpt = False) -> None: +def extension_list(format: FormatWithAutoOpt = OutputFormatWithAuto.auto) -> None: """List installed extension commands.""" rows = [ { @@ -171,11 +172,11 @@ def extension_list(format: FormatOpt = OutputFormat.table, quiet: QuietOpt = Fal } for manifest in _list_installed_extensions() ] - print_list_output(rows, format=format, quiet=quiet, id_key="command") + out.table(rows, id_key="command") @extensions_cli.command("search", examples=["hf extensions search"]) -def extension_search(format: FormatOpt = OutputFormat.table, quiet: QuietOpt = False) -> None: +def extension_search(format: FormatWithAutoOpt = OutputFormatWithAuto.auto) -> None: """Search extensions available on GitHub (tagged with 'hf-extension' topic).""" response = get_session().get( "https://api.github.com/search/repositories", @@ -202,7 +203,7 @@ def extension_search(format: FormatOpt = OutputFormat.table, quiet: QuietOpt = F } ) - print_list_output(rows, format=format, quiet=quiet, id_key="repo", alignments={"stars": "right"}) + out.table(rows, id_key="repo", alignments={"stars": "right"}) @extensions_cli.command("remove | rm", examples=["hf extensions remove claude"]) diff --git a/src/huggingface_hub/cli/inference_endpoints.py b/src/huggingface_hub/cli/inference_endpoints.py index da1db4f43f..57bc97dca7 100644 --- a/src/huggingface_hub/cli/inference_endpoints.py +++ b/src/huggingface_hub/cli/inference_endpoints.py @@ -1,22 +1,14 @@ """CLI commands for Hugging Face Inference Endpoints.""" -import json -from typing import Annotated, Any +from typing import Annotated import typer -from huggingface_hub._inference_endpoints import InferenceEndpoint, InferenceEndpointScalingMetric +from huggingface_hub._inference_endpoints import InferenceEndpointScalingMetric from huggingface_hub.errors import HfHubHTTPError -from ._cli_utils import ( - FormatOpt, - OutputFormat, - QuietOpt, - TokenOpt, - get_hf_api, - print_list_output, - typer_factory, -) +from ._cli_utils import FormatWithAutoOpt, TokenOpt, get_hf_api, typer_factory +from ._output import OutputFormatWithAuto, out ie_cli = typer_factory(help="Manage Hugging Face Inference Endpoints.") @@ -41,15 +33,10 @@ ] -def _print_endpoint(endpoint: InferenceEndpoint) -> None: - typer.echo(json.dumps(endpoint.raw, indent=2, sort_keys=True)) - - @ie_cli.command("list | ls", examples=["hf endpoints ls", "hf endpoints ls --namespace my-org"]) def ls( namespace: NamespaceOpt = None, - format: FormatOpt = OutputFormat.table, - quiet: QuietOpt = False, + format: FormatWithAutoOpt = OutputFormatWithAuto.auto, token: TokenOpt = None, ) -> None: """Lists all Inference Endpoints for the given namespace.""" @@ -60,32 +47,26 @@ def ls( typer.echo(f"Listing failed: {error}") raise typer.Exit(code=error.response.status_code) from error - results = [endpoint.raw for endpoint in endpoints] - - def row_fn(item: dict[str, Any]) -> list[str]: - status = item.get("status", {}) - model = item.get("model", {}) - compute = item.get("compute", {}) - provider = item.get("provider", {}) - return [ - str(item.get("name", "")), - str(model.get("repository", "") if isinstance(model, dict) else ""), - str(status.get("state", "") if isinstance(status, dict) else ""), - str(model.get("task", "") if isinstance(model, dict) else ""), - str(model.get("framework", "") if isinstance(model, dict) else ""), - str(compute.get("instanceType", "") if isinstance(compute, dict) else ""), - str(provider.get("vendor", "") if isinstance(provider, dict) else ""), - str(provider.get("region", "") if isinstance(provider, dict) else ""), - ] - - print_list_output( - items=results, - format=format, - quiet=quiet, - id_key="name", - headers=["NAME", "MODEL", "STATUS", "TASK", "FRAMEWORK", "INSTANCE", "VENDOR", "REGION"], - row_fn=row_fn, - ) + results = [] + for endpoint in endpoints: + raw = endpoint.raw + status = raw.get("status", {}) + model = raw.get("model", {}) + compute = raw.get("compute", {}) + provider = raw.get("provider", {}) + results.append( + { + "name": raw.get("name", ""), + "model": model.get("repository", "") if isinstance(model, dict) else "", + "status": status.get("state", "") if isinstance(status, dict) else "", + "task": model.get("task", "") if isinstance(model, dict) else "", + "framework": model.get("framework", "") if isinstance(model, dict) else "", + "instance": compute.get("instanceType", "") if isinstance(compute, dict) else "", + "vendor": provider.get("vendor", "") if isinstance(provider, dict) else "", + "region": provider.get("region", "") if isinstance(provider, dict) else "", + } + ) + out.table(results, id_key="name") @ie_cli.command(name="deploy", examples=["hf endpoints deploy my-endpoint --repo gpt2 --framework pytorch ..."]) @@ -193,8 +174,7 @@ def deploy( scaling_threshold=scaling_threshold, scale_to_zero_timeout=scale_to_zero_timeout, ) - - _print_endpoint(endpoint) + out.dict(endpoint.raw) @catalog_app.command(name="deploy", examples=["hf endpoints catalog deploy --repo meta-llama/Llama-3.2-1B-Instruct"]) @@ -229,7 +209,7 @@ def deploy_from_catalog( typer.echo(f"Deployment failed: {error}") raise typer.Exit(code=error.response.status_code) from error - _print_endpoint(endpoint) + out.dict(endpoint.raw) def list_catalog( @@ -243,7 +223,7 @@ def list_catalog( typer.echo(f"Catalog fetch failed: {error}") raise typer.Exit(code=error.response.status_code) from error - typer.echo(json.dumps({"models": models}, indent=2, sort_keys=True)) + out.dict({"models": models}) catalog_app.command(name="list | ls", examples=["hf endpoints catalog ls"])(list_catalog) @@ -267,7 +247,7 @@ def describe( typer.echo(f"Fetch failed: {error}") raise typer.Exit(code=error.response.status_code) from error - _print_endpoint(endpoint) + out.dict(endpoint.raw) @ie_cli.command(examples=["hf endpoints update my-endpoint --min-replica 2"]) @@ -371,7 +351,7 @@ def update( except HfHubHTTPError as error: typer.echo(f"Update failed: {error}") raise typer.Exit(code=error.response.status_code) from error - _print_endpoint(endpoint) + out.dict(endpoint.raw) @ie_cli.command(examples=["hf endpoints delete my-endpoint"]) @@ -415,7 +395,7 @@ def pause( typer.echo(f"Pause failed: {error}") raise typer.Exit(code=error.response.status_code) from error - _print_endpoint(endpoint) + out.dict(endpoint.raw) @ie_cli.command(examples=["hf endpoints resume my-endpoint"]) @@ -443,7 +423,7 @@ def resume( except HfHubHTTPError as error: typer.echo(f"Resume failed: {error}") raise typer.Exit(code=error.response.status_code) from error - _print_endpoint(endpoint) + out.dict(endpoint.raw) @ie_cli.command(examples=["hf endpoints scale-to-zero my-endpoint"]) @@ -460,4 +440,4 @@ def scale_to_zero( typer.echo(f"Scale To Zero failed: {error}") raise typer.Exit(code=error.response.status_code) from error - _print_endpoint(endpoint) + out.dict(endpoint.raw) diff --git a/src/huggingface_hub/cli/webhooks.py b/src/huggingface_hub/cli/webhooks.py index 8d00761d8f..2c2045407c 100644 --- a/src/huggingface_hub/cli/webhooks.py +++ b/src/huggingface_hub/cli/webhooks.py @@ -38,7 +38,6 @@ """ import enum -import json from typing import Annotated, get_args, get_type_hints import typer @@ -47,15 +46,12 @@ from huggingface_hub.hf_api import WebhookWatchedItem from ._cli_utils import ( - FormatOpt, - OutputFormat, - QuietOpt, + FormatWithAutoOpt, TokenOpt, - api_object_to_dict, get_hf_api, - print_list_output, typer_factory, ) +from ._output import OutputFormatWithAuto, out # Build enums dynamically from Literal types to avoid duplication @@ -102,32 +98,26 @@ def _parse_watch(values: list[str]) -> list[WebhookWatchedItem]: examples=[ "hf webhooks ls", "hf webhooks ls --format json", - "hf webhooks ls -q", + "hf webhooks ls --format quiet", ], ) def webhooks_ls( - format: FormatOpt = OutputFormat.table, - quiet: QuietOpt = False, + format: FormatWithAutoOpt = OutputFormatWithAuto.auto, token: TokenOpt = None, ) -> None: """List all webhooks for the current user.""" api = get_hf_api(token=token) - results = [api_object_to_dict(w) for w in api.list_webhooks()] - print_list_output( - results, - format=format, - quiet=quiet, - headers=["id", "url", "disabled", "domains", "watched"], - row_fn=lambda item: [ - item.get("id", ""), - item.get("url") or "(job)", - str(item.get("disabled", False)), - ", ".join(item.get("domains") or []), - ", ".join( - f"{w['type']}:{w['name']}" if isinstance(w, dict) else str(w) for w in (item.get("watched") or []) - ), - ], - ) + results = [ + { + "id": w.id, + "url": w.url or "(job)", + "disabled": w.disabled, + "domains": ", ".join(w.domains or []), + "watched": ", ".join(f"{wi.type}:{wi.name}" for wi in (w.watched or [])), + } + for w in api.list_webhooks() + ] + out.table(results) @webhooks_cli.command( @@ -138,12 +128,13 @@ def webhooks_ls( ) def webhooks_info( webhook_id: Annotated[str, typer.Argument(help="The ID of the webhook.")], + format: FormatWithAutoOpt = OutputFormatWithAuto.auto, token: TokenOpt = None, ) -> None: - """Show full details for a single webhook as JSON.""" + """Show full details for a single webhook.""" api = get_hf_api(token=token) webhook = api.get_webhook(webhook_id) - print(json.dumps(api_object_to_dict(webhook), indent=2)) + out.dict(webhook) @webhooks_cli.command( @@ -198,8 +189,8 @@ def webhooks_create( watched_items = _parse_watch(watch) domains = [d.value for d in domain] if domain else None webhook = api.create_webhook(url=url, job_id=job_id, watched=watched_items, domains=domains, secret=secret) # type: ignore - print(f"Webhook created: {webhook.id}") - print(json.dumps(api_object_to_dict(webhook), indent=2)) + out.result("Webhook created", id=webhook.id) + out.dict(webhook) @webhooks_cli.command( @@ -244,8 +235,8 @@ def webhooks_update( watched_items = _parse_watch(watch) if watch else None domains = [d.value for d in domain] if domain else None webhook = api.update_webhook(webhook_id, url=url, watched=watched_items, domains=domains, secret=secret) # type: ignore - print(f"Webhook updated: {webhook.id}") - print(json.dumps(api_object_to_dict(webhook), indent=2)) + out.result("Webhook updated", id=webhook.id) + out.dict(webhook) @webhooks_cli.command( @@ -261,7 +252,7 @@ def webhooks_enable( """Enable a disabled webhook.""" api = get_hf_api(token=token) webhook = api.enable_webhook(webhook_id) - print(f"Webhook enabled: {webhook.id}") + out.result("Webhook enabled", id=webhook.id) @webhooks_cli.command( @@ -277,7 +268,7 @@ def webhooks_disable( """Disable an active webhook.""" api = get_hf_api(token=token) webhook = api.disable_webhook(webhook_id) - print(f"Webhook disabled: {webhook.id}") + out.result("Webhook disabled", id=webhook.id) @webhooks_cli.command( @@ -307,4 +298,4 @@ def webhooks_delete( raise typer.Abort() api = get_hf_api(token=token) api.delete_webhook(webhook_id) - print(f"Webhook deleted: {webhook_id}") + out.result("Webhook deleted", id=webhook_id) diff --git a/tests/test_cli.py b/tests/test_cli.py index 7fc6549be5..4b1eb47994 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -2061,8 +2061,7 @@ def test_list_with_format_and_quiet(self, runner: CliRunner) -> None: with patch("huggingface_hub.cli.inference_endpoints.get_hf_api") as api_cls: api = api_cls.return_value api.list_inference_endpoints.return_value = [endpoint] - # Test table format - result = runner.invoke(app, ["endpoints", "ls", "--format", "table"]) + result = runner.invoke(app, ["endpoints", "ls", "--format", "human"]) assert result.exit_code == 0 assert "NAME" in result.stdout assert "demo" in result.stdout @@ -2070,8 +2069,7 @@ def test_list_with_format_and_quiet(self, runner: CliRunner) -> None: with patch("huggingface_hub.cli.inference_endpoints.get_hf_api") as api_cls: api = api_cls.return_value api.list_inference_endpoints.return_value = [endpoint] - # Test quiet mode - result = runner.invoke(app, ["endpoints", "ls", "--quiet"]) + result = runner.invoke(app, ["endpoints", "ls", "--format", "quiet"]) assert result.exit_code == 0 assert result.stdout.strip() == "demo" @@ -3111,7 +3109,7 @@ def test_ls_quiet(self, runner: CliRunner) -> None: webhook = self._make_webhook() with patch("huggingface_hub.cli.webhooks.get_hf_api") as api_cls: api_cls.return_value.list_webhooks.return_value = [webhook] - result = runner.invoke(app, ["webhooks", "ls", "-q"]) + result = runner.invoke(app, ["webhooks", "ls", "--format", "quiet"]) assert result.exit_code == 0, result.output assert result.output.strip() == "wh-abc123" @@ -3141,7 +3139,7 @@ def test_create(self, runner: CliRunner) -> None: ["webhooks", "create", "--url", "https://example.com/hook", "--watch", "model:bert-base-uncased"], ) assert result.exit_code == 0, result.output - assert "Webhook created: wh-abc123" in result.output + assert "wh-abc123" in result.output from huggingface_hub.hf_api import WebhookWatchedItem api_cls.return_value.create_webhook.assert_called_once_with( @@ -3191,7 +3189,7 @@ def test_create_with_job_id(self, runner: CliRunner) -> None: ["webhooks", "create", "--job-id", "687f911eaea852de79c4a50a", "--watch", "user:julien-c"], ) assert result.exit_code == 0, result.output - assert "Webhook created" in result.output + assert "wh-abc123" in result.output from huggingface_hub.hf_api import WebhookWatchedItem api_cls.return_value.create_webhook.assert_called_once_with( @@ -3239,7 +3237,7 @@ def test_update(self, runner: CliRunner) -> None: ["webhooks", "update", "wh-abc123", "--url", "https://new.example.com/hook"], ) assert result.exit_code == 0, result.output - assert "Webhook updated: wh-abc123" in result.output + assert "wh-abc123" in result.output api_cls.return_value.update_webhook.assert_called_once_with( "wh-abc123", url="https://new.example.com/hook", @@ -3254,7 +3252,7 @@ def test_enable(self, runner: CliRunner) -> None: api_cls.return_value.enable_webhook.return_value = webhook result = runner.invoke(app, ["webhooks", "enable", "wh-abc123"]) assert result.exit_code == 0, result.output - assert "Webhook enabled: wh-abc123" in result.output + assert "wh-abc123" in result.output api_cls.return_value.enable_webhook.assert_called_once_with("wh-abc123") def test_disable(self, runner: CliRunner) -> None: @@ -3263,7 +3261,7 @@ def test_disable(self, runner: CliRunner) -> None: api_cls.return_value.disable_webhook.return_value = webhook result = runner.invoke(app, ["webhooks", "disable", "wh-abc123"]) assert result.exit_code == 0, result.output - assert "Webhook disabled: wh-abc123" in result.output + assert "wh-abc123" in result.output api_cls.return_value.disable_webhook.assert_called_once_with("wh-abc123") def test_delete_with_yes(self, runner: CliRunner) -> None: @@ -3271,7 +3269,7 @@ def test_delete_with_yes(self, runner: CliRunner) -> None: api_cls.return_value.delete_webhook.return_value = None result = runner.invoke(app, ["webhooks", "delete", "wh-abc123", "--yes"]) assert result.exit_code == 0, result.output - assert "Webhook deleted: wh-abc123" in result.output + assert "wh-abc123" in result.output api_cls.return_value.delete_webhook.assert_called_once_with("wh-abc123") def test_delete_confirm_yes(self, runner: CliRunner) -> None: @@ -3279,7 +3277,7 @@ def test_delete_confirm_yes(self, runner: CliRunner) -> None: api_cls.return_value.delete_webhook.return_value = None result = runner.invoke(app, ["webhooks", "delete", "wh-abc123"], input="y\n") assert result.exit_code == 0, result.output - assert "Webhook deleted: wh-abc123" in result.output + assert "wh-abc123" in result.output def test_delete_confirm_no(self, runner: CliRunner) -> None: with patch("huggingface_hub.cli.webhooks.get_hf_api") as api_cls: diff --git a/tests/test_cli_discussions.py b/tests/test_cli_discussions.py index 7c88a96c85..5d163be7ed 100644 --- a/tests/test_cli_discussions.py +++ b/tests/test_cli_discussions.py @@ -82,12 +82,12 @@ def test_list_discussions(repo_with_discussion: tuple): repo_id, _, _ = repo_with_discussion result = cli(f"hf discussions list {repo_id}") assert result.exit_code == 0, result.output - assert "#" in result.output + assert "Test discussion" in result.output def test_list_discussions_quiet(repo_with_discussion: tuple): repo_id, disc_num, pr_num = repo_with_discussion - result = cli(f"hf discussions list {repo_id} --status all --quiet") + result = cli(f"hf discussions list {repo_id} --status all --format quiet") assert result.exit_code == 0, result.output nums = result.output.strip().splitlines() assert str(disc_num) in nums @@ -107,7 +107,7 @@ def test_list_discussions_json(repo_with_discussion: tuple): def test_list_filter_kind_discussion(repo_with_discussion: tuple): repo_id, disc_num, pr_num = repo_with_discussion - result = cli(f"hf discussions list {repo_id} --status all --kind discussion --quiet") + result = cli(f"hf discussions list {repo_id} --status all --kind discussion --format quiet") assert result.exit_code == 0, result.output nums = result.output.strip().splitlines() assert str(disc_num) in nums @@ -116,7 +116,7 @@ def test_list_filter_kind_discussion(repo_with_discussion: tuple): def test_list_filter_kind_pull_request(repo_with_discussion: tuple): repo_id, disc_num, pr_num = repo_with_discussion - result = cli(f"hf discussions list {repo_id} --status all --kind pull_request --quiet") + result = cli(f"hf discussions list {repo_id} --status all --kind pull_request --format quiet") assert result.exit_code == 0, result.output nums = result.output.strip().splitlines() assert str(pr_num) in nums @@ -125,7 +125,7 @@ def test_list_filter_kind_pull_request(repo_with_discussion: tuple): def test_list_filter_status_closed(repo_with_discussion: tuple): repo_id, _, _ = repo_with_discussion - result = cli(f"hf discussions list {repo_id} --status closed --quiet") + result = cli(f"hf discussions list {repo_id} --status closed --format quiet") assert result.exit_code == 0 # All our test discussions are open, so closed should return nothing assert result.output.strip() == ""