Skip to content

[CLI] Migrate buckets commands to out singleton#4111

Merged
Wauplin merged 8 commits intomainfrom
cli-migrate-buckets-out-singleton
Apr 23, 2026
Merged

[CLI] Migrate buckets commands to out singleton#4111
Wauplin merged 8 commits intomainfrom
cli-migrate-buckets-out-singleton

Conversation

@hanouticelina
Copy link
Copy Markdown
Contributor

@hanouticelina hanouticelina commented Apr 15, 2026

Part of #3979. Migrates buckets commands (create, list, info, delete, remove, move, cp) to the unified --format [auto|human|agent|json|quiet] flag and out.* methods.

  • Replaces --quiet with --format on all migrated commands
  • Uses out.result() for success messages, out.dict() for info, out.table() for bucket listing, out.confirm() for confirmations, out.hint() for hints
  • Adds id_key param to out.dict() for quiet mode (consistent with out.table())
  • sync left as-is (passes quiet/verbose to library)

_list_files and _list_buckets keep their existing output format — the ls-like flat table, -h for human-readable sizes, --tree, and the json/quiet branches are preserved as-is. Only the flag changes (--quiet--format quiet, --format table--format auto). The existing output worked well and we didn't want to change it unnecessarily by routing everything through out.table().


Note

Medium Risk
Moderate risk because it changes CLI flags and stdout/stderr output formatting for multiple hf buckets commands, which may break user scripts relying on previous messages or --quiet behavior.

Overview
Migrates hf buckets commands (create, list, info, delete, rm, move, cp) from ad-hoc printing and --quiet/--format table|json to the unified out output framework with --format [auto|human|agent|json|quiet].

Adds id_key support to out.dict() to make --format quiet print a single identifier (used by buckets info), routes bucket listing through out.table() (including standardized empty-results messaging), and adjusts hints/confirmations/results accordingly; tests and generated CLI docs are updated to match the new outputs and flags.

Reviewed by Cursor Bugbot for commit 387dcfe. Bugbot is set up for automated code reviews on this repo. Configure here.

Consistent with `out.table()` and `out.result()`, `out.dict()` now
accepts an `id_key` param. When set and mode is quiet, prints just
that value instead of the full JSON.
Part of #3979. Migrates create, list, info, delete, remove, move,
and cp to the unified `--format` flag and `out.*` methods. Preserves
the ls-like flat table format for file listing.
@bot-ci-comment
Copy link
Copy Markdown

The docs for this PR live here. All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update.

Copy link
Copy Markdown
Contributor

@Wauplin Wauplin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made a first pass, especially on the tests. Might be the worst CLI to migrate, sorry about that^^

sync left as-is (passes quiet/verbose to library)

Agree with this.
Just as a nit I think in the --plan mode we should output the --apply method just after as a hint.

"""
if dataclasses.is_dataclass(data) and not isinstance(data, type):
data = _dataclass_to_dict(data)
if self.mode == OutputFormatWithAuto.quiet and id_key is not None:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if --quiet but no id_key provided, what should we do? Currently we default to printing the full JSON (not a problem from this PR but this addition highlights it^^)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know, for now full json is a reasonable fallback imo, quiet without id_key means the caller didn't specify what to extract, so printing everything is safer maybe ? 😄

Comment thread tests/test_buckets_cli.py Outdated
Comment thread tests/test_buckets_cli.py Outdated
Comment thread tests/test_buckets_cli.py Outdated
Comment thread tests/test_buckets_cli.py Outdated
Comment thread src/huggingface_hub/cli/buckets.py Outdated
Comment thread src/huggingface_hub/cli/buckets.py Outdated

if recursive:
status = StatusLine(enabled=not quiet)
status = StatusLine(enabled=out.mode == OutputFormatWithAuto.human)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we should make the StatusLine __init__ check the out.mode singleton by default instead of passing enabled=out.mode == OutputFormatWithAuto.human like here (i.e. remove the enabled parameter entirely)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed, but it lives in utils/_terminal.py which doesn't currently import from cli/_output.py. doing it here would create a dependency from utils -> cli. i'd rather tackle this in a followup when we implement out.status()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fine for me to do it in a later PR 👍

…ep tree --quiet behavior

- Tests use --quiet instead of --format quiet (alias handled by TyperGroup)
- _check_list_output uses result.stdout instead of filtering stderr lines
- Restore quiet param on _build_tree so --tree --quiet shows structure only
- Tree prints directly instead of mutating out.mode
- Revert :>19 formatting back to explicit spaces in test expectations
@hanouticelina hanouticelina marked this pull request as ready for review April 15, 2026 15:16
@hanouticelina hanouticelina requested a review from Wauplin April 15, 2026 15:21
@hanouticelina
Copy link
Copy Markdown
Contributor Author

@Wauplin ready for a second review!

Just as a nit I think in the --plan mode we should output the --apply method just after as a hint.

added a hint in 7421ca2

Copy link
Copy Markdown
Contributor

@Wauplin Wauplin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me! I've resolved the merge conflicts so we should be good to merge

(and let's not forget about #4111 (comment))

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 387dcfe. Configure here.

}
for bucket in api.list_buckets(namespace=namespace, search=search)
]
out.table(items, alignments={"size": "right", "total_files": "right"})
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Human-readable size formatting contaminates JSON output

Medium Severity

In _list_buckets, the size field is pre-formatted at dict construction time via _format_size(bucket.size, human_readable) if human_readable else bucket.size. When -h is passed, this converts the integer size to a human-readable string (e.g., "1.5 MB") in the items dict before passing it to out.table(). This means JSON output gets "size": "1.5 MB" instead of "size": 1500000, changing the field type from number to string. In the old code, _format_size was called only inside row_fn for table display, so JSON output always received raw integer values regardless of -h.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 387dcfe. Configure here.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's fine

api = get_hf_api(token=token)

if recursive:
status = StatusLine(enabled=not quiet)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dry-run produces no output in json/quiet modes

Low Severity

The remove command's --dry-run path uses out.text() to display what would be deleted. Since out.text() is a no-op in json and quiet modes, running hf buckets rm ... --dry-run --format json (or --format quiet) produces zero output and returns silently. The old code always printed dry-run details regardless of the --quiet flag, so this is a behavioral regression where users get no indication of what would be deleted.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 387dcfe. Configure here.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fine

@Wauplin Wauplin merged commit f1cd149 into main Apr 23, 2026
17 of 21 checks passed
@Wauplin Wauplin deleted the cli-migrate-buckets-out-singleton branch April 23, 2026 09:38
@huggingface-hub-bot
Copy link
Copy Markdown
Contributor

This PR has been shipped as part of the v1.12.0 release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants