You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Entry point: `hf.py` (Typer app). Subcommands split into modules: `auth.py`, `repos.py`, `spaces.py`, `models.py`, `datasets.py`, `buckets.py`, `jobs.py`, `collections.py`, `webhooks.py`, etc.
72
71
73
-
### CLI (`src/huggingface_hub/cli/`)
72
+
#### Adding CLI commands
73
+
74
+
**Structure & naming conventions:**
75
+
76
+
- Commands are organized as **Typer groups** registered in `hf.py` (e.g. `app.add_typer(spaces_cli, name="spaces")`).
77
+
- Each module creates its app with `typer_factory(help="...")` and defines commands with `@app.command("name")`.
78
+
- Use **pipe-separated aliases**: `@app.command("list | ls")` registers both `list` and `ls`.
79
+
- Use standard **verb names**: `ls`/`list`, `info`, `create`, `set`, `delete`, `update`.
80
+
- For **sub-resources** with multiple operations, create a nested subgroup: `volumes_cli = typer_factory(...)` then `spaces_cli.add_typer(volumes_cli, name="volumes")` → gives `hf spaces volumes ls/set/delete`. See `repos.py` for examples (`tag_cli`, `branch_cli`).
81
+
82
+
**Reusable option types** (from `_cli_utils.py` — import and use these, don't reinvent):
83
+
84
+
-`TokenOpt`, `RepoIdArg`, `RepoTypeOpt`, `RevisionOpt` — standard auth/repo options.
-`FormatWithAutoOpt` / `FormatOpt` — output format (`auto|json|human|quiet|agent`).
87
+
-`VolumesOpt` + `parse_volumes()` — `-v`/`--volume` flag with `hf://[TYPE/]SOURCE:/MOUNT_PATH[:ro]` syntax.
88
+
-`get_hf_api(token=token)` — creates an `HfApi` instance with token.
89
+
-`api_object_to_dict(obj)` — converts dataclass API objects to dicts for output.
90
+
91
+
**Output** (from `_output.py` — use the `out` singleton):
74
92
75
-
Entry point: `hf.py` (Typer app). Subcommands split into modules: `auth.py`, `repo.py`, `repo_files.py`, etc.
93
+
Convention: stderr for prompts/warnings, stdout for data. Warnings go to stderr even in quiet/json modes.
76
94
77
-
### Tests (`tests/`)
95
+
-`out.table(items)` — list results (auto-formats as padded table / TSV / JSON depending on `--format`).
96
+
-`out.dict(data)` — single-item detail view.
97
+
-`out.result("Message", key=value, ...)` — success summary with green checkmark.
98
+
-`out.confirm("Prompt?", yes=yes)` — confirmation for destructive operations. Pair with a `-y`/`--yes` flag.
99
+
-`out.hint("...")` — actionable follow-up suggestion. Try to add hints when adding new commands or refactoring a command. Hints should preferably reuse the input args to be specific to the current use case. Example: `out.hint(f"Use 'hf buckets ls {bucket_id}' to list files from the bucket.")` after a bucket creation.
-`testing_utils.py` / `testing_constants.py` — Shared test helpers and staging-repo constants.
82
-
-`cassettes/` — Recorded HTTP responses for offline tests (`@pytest.mark.vcr`). **Do not add new cassettes.**
83
-
-`fixtures/` — Static test data.
102
+
**Destructive operations** should use `out.confirm()` with a `yes: Annotated[bool, typer.Option("-y", "--yes", help="Answer Yes to prompt automatically.")]` parameter.
84
103
85
-
### Dev scripts (`utils/`)
104
+
**Errors**: raise `CLIError("message")` for user-facing errors. Never wrap API calls with try/except for `RepositoryNotFoundError`, `RevisionNotFoundError`, etc. (already done globally)
86
105
87
-
-`check_static_imports.py` — Ensures `__init__.py` static imports match the lazy loader.
**Generated docs**: `make style` auto-regenerates `docs/source/en/package_reference/cli.md` via `utils/generate_cli_reference.py`. Don't edit that file by hand.
92
107
93
-
## Style
108
+
**Guides**: update the CLI guide `docs/sources/en/guides/cli.md` when adding / updating CLI commands. If the command is specific to a topic which has its own guide in `docs/sources/en/guides`, add a mention in the guide as well, using the same tone as the existing guide.
109
+
110
+
**CLI tests**: add tests in `tests/test_cli.py`. Try to group tests into classes when relevant. Do not add a test for each specific use case / parameter set. Usually testing the 1-2 main use cases is enough.
94
111
95
-
- Max line length: 119 chars.
96
-
- Linter/formatter: `ruff`.
97
-
- Imports sorted by `ruff` (isort-compatible).
98
112
99
113
### Type checking: local vs CI
100
114
@@ -105,8 +119,26 @@ Locally, `make quality` runs `ty check src` using whatever version of `ty` is in
105
119
106
120
If CI fails on type checks that pass locally, the likely cause is a newer `ty` version or a `mypy`-only diagnostic. Fix the reported errors rather than downgrading the checker.
107
121
108
-
## Testing notes
122
+
## Commits & PRs
123
+
124
+
-**Commit message prefix**: use `[Area]` prefix matching the scope, e.g. `[CLI] Add ...`, `[CLI] Fix ...`, `[Inference] ...`.
125
+
-**PR title**: short (under 70 chars), same `[Area]` prefix convention.
126
+
-**PR description**: keep it casual. Include a `## Summary` with a few bullet points and real CLI/code **examples** from manual testing (copy-paste terminal output). No need for a formal "Test plan" section. Call out breaking changes explicitly if any. It is important to document any decision taken in the PR or instructions provided in the prompt while working on the PR, ideally with the rationale behind it.
127
+
128
+
## Code conventions
129
+
130
+
### Simplicity is the #1 priority
131
+
132
+
- No premature abstractions. No unnecessary generalization.
133
+
- Don't implement features until they're actually needed.
134
+
- Don't accept parameters without a use case.
135
+
- No redundant `try`/`except` - don't catch errors already handled up the call stack.
136
+
- Prefer strictness now, relax later.
137
+
138
+
### Follow Python 3.10+ idioms
109
139
110
-
- Tests use `pytest` with `pytest-env` setting `HUGGINGFACE_CO_STAGING=1` (tests hit staging Hub by default).
111
-
- Most integration tests require `HF_TOKEN` to be set. Unit tests don't.
112
-
- do not register or commit new HTTP cassettes.
140
+
-`match`/`case` over `if`/`elif` chains when dispatching on a value.
141
+
-`str | None` not `Optional[str]`.
142
+
- f-strings over `.format()` or `%`.
143
+
- Walrus operator (`:=`) when it improves readability.
144
+
- Comprehensions over `map`/`filter` with lambdas.
0 commit comments