Files
MoFin/venv/lib/python3.12/site-packages/huggingface_hub/cli/repos.py
T
知微 fa45d8aa5f fix: 小果地址统一node122(兼容LAN+EasyTier)
- health_checklist.json: 192.168.1.122→node122
- ocr_client.py: docstring IP→node122
- docs/market-data-requirements.md: IP→node122
- 所有API调用通过ProxyHandler({})绕过系统代理
  Privoxy对node122:18003返回500,直连正常
2026-06-30 02:56:35 +08:00

629 lines
20 KiB
Python

# Copyright 2025 The HuggingFace Team. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Contains commands to interact with repositories on the Hugging Face Hub.
Usage:
# create a new dataset repo on the Hub
hf repos create my-cool-dataset --repo-type=dataset
# create a private model repo on the Hub
hf repos create my-cool-model --private
# delete files from a repo on the Hub
hf repos delete-files my-model file.txt
"""
import enum
from typing import Annotated
import typer
from huggingface_hub import SpaceHardware, SpaceStorage
from huggingface_hub.cli._cli_utils import SoftChoice
from huggingface_hub.errors import CLIError, HfHubHTTPError, RepositoryNotFoundError, RevisionNotFoundError
from huggingface_hub.hf_api import REPO_REGIONS
from ._city_game import run_city_game
from ._cli_utils import (
REPO_LIST_DEFAULT_LIMIT,
EnvFileOpt,
EnvOpt,
LimitOpt,
PrivateOpt,
RepoIdArg,
RepoType,
RepoTypeOpt,
RevisionOpt,
SearchOpt,
SecretsFileOpt,
SecretsOpt,
TokenOpt,
VolumesOpt,
env_map_to_key_value_list,
get_hf_api,
parse_env_map,
parse_volumes,
typer_factory,
)
from ._cp import make_cp
from ._file_listing import format_size
from ._output import OutputFormat, out
repos_cli = typer_factory(help="Manage repos on the Hub.")
@repos_cli.callback(invoke_without_command=True)
def _repos_callback(ctx: typer.Context) -> None:
if ctx.info_name == "repo":
out.warning("`hf repo` is deprecated in favor of `hf repos`.")
class RepoTypeAll(str, enum.Enum):
model = "model"
dataset = "dataset"
space = "space"
bucket = "bucket"
class GatedChoices(str, enum.Enum):
auto = "auto"
manual = "manual"
false = "false"
PublicOpt = Annotated[
bool | None,
typer.Option(
"--public",
help="Whether to make the repo public. Ignored if the repo already exists.",
),
]
ProtectedOpt = Annotated[
bool | None,
typer.Option(
"--protected",
help="Whether to make the Space protected (Spaces only). Ignored if the repo already exists.",
),
]
SpaceHardwareOpt = Annotated[
str | None,
typer.Option(
"--flavor",
help="Space hardware flavor (e.g. 'cpu-basic', 't4-medium', 'l4x4'). Only for Spaces.",
click_type=SoftChoice(SpaceHardware),
),
]
SpaceStorageOpt = Annotated[
SpaceStorage | None,
typer.Option(
"--storage",
help="(Deprecated, use volumes instead) Space persistent storage tier ('small', 'medium', or 'large'). Only for Spaces.",
),
]
SpaceSleepTimeOpt = Annotated[
int | None,
typer.Option(
"--sleep-time",
help="Seconds of inactivity before the Space is put to sleep. Use -1 to disable. Only for Spaces.",
),
]
tag_cli = typer_factory(help="Manage tags for a repo on the Hub.")
branch_cli = typer_factory(help="Manage branches for a repo on the Hub.")
repos_cli.add_typer(tag_cli, name="tag")
repos_cli.add_typer(branch_cli, name="branch")
@repos_cli.command(
"list | ls",
examples=[
"hf repos ls",
"hf repos ls --explore",
"hf repos ls --namespace my-org --search bert",
],
)
def repo_list(
namespace: Annotated[
str | None,
typer.Option(
help="Organization name. If not provided, lists repos for the authenticated user.",
),
] = None,
repo_type: Annotated[
RepoTypeAll | None,
typer.Option(
"--type",
"--repo-type",
help="Filter by repository type (model, dataset, space, or bucket).",
),
] = None,
search: SearchOpt = None,
limit: LimitOpt = REPO_LIST_DEFAULT_LIMIT,
explore: Annotated[
bool,
typer.Option("--explore", help="Explore your repos as an interactive 3D city."),
] = False,
token: TokenOpt = None,
) -> None:
"""List all repos (models, datasets, spaces, buckets) with storage info."""
api = get_hf_api(token=token)
repos = list(api.list_user_repos(namespace=namespace))
if repo_type is not None:
repos = [r for r in repos if r.type == repo_type.value]
if search is not None:
search_lower = search.lower()
repos = [r for r in repos if search_lower in r.id.lower()]
total = len(repos)
if explore:
if out.mode == OutputFormat.human:
run_city_game(repos)
return
raise CLIError("Repository exploration is only available in terminal.")
if limit > 0:
repos = repos[:limit]
items = [
{
"id": r.id,
"type": r.type,
"updated": r.updated_at.strftime("%Y-%m-%d"),
"visibility": r.visibility,
"storage": format_size(r.storage, human_readable=True),
"%_of_total": f"{r.storage_percent:.1f}%",
}
for r in repos
]
out.table(items, id_key="id", alignments={"storage": "right", "%_of_total": "right"})
if limit > 0 and total > limit:
out.hint(f"Showing {limit} of {total} repos. Use `--limit 0` to list all.")
@repos_cli.command(
"create",
examples=[
"hf repos create my-model",
"hf repos create my-dataset --repo-type dataset --private",
"hf repos create my-space --type space --space-sdk gradio --flavor t4-medium --secrets HF_TOKEN -e THEME=dark --protected",
"hf repos create my-space --type space --space-sdk gradio -v hf://org/my-model:/models -v hf://buckets/org/b:/data",
"hf repos create my-model --region us",
],
)
def repo_create(
repo_id: RepoIdArg,
repo_type: RepoTypeOpt = RepoType.model,
space_sdk: Annotated[
str | None,
typer.Option(
help="Hugging Face Spaces SDK type. Required when --type is set to 'space'.",
),
] = None,
private: PrivateOpt = None,
public: PublicOpt = None,
protected: ProtectedOpt = None,
token: TokenOpt = None,
exist_ok: Annotated[
bool,
typer.Option(
help="Do not raise an error if repo already exists.",
),
] = False,
resource_group_id: Annotated[
str | None,
typer.Option(
help="Resource group in which to create the repo. Resource groups is only available for Enterprise Hub organizations.",
),
] = None,
region: Annotated[
REPO_REGIONS | None,
typer.Option(
"--region",
help="Cloud region in which to create the repo. Can be one of 'us' or 'eu'. Requires Team plan or above.",
),
] = None,
hardware: SpaceHardwareOpt = None,
storage: SpaceStorageOpt = None,
sleep_time: SpaceSleepTimeOpt = None,
secrets: SecretsOpt = None,
secrets_file: SecretsFileOpt = None,
env: EnvOpt = None,
env_file: EnvFileOpt = None,
volume: VolumesOpt = None,
) -> None:
"""Create a new repo on the Hub."""
api = get_hf_api(token=token)
repo_url = api.create_repo(
repo_id=repo_id,
repo_type=repo_type.value,
visibility="private" if private else "public" if public else "protected" if protected else None, # type: ignore [arg-type]
token=token,
exist_ok=exist_ok,
resource_group_id=resource_group_id,
region=region,
space_sdk=space_sdk,
space_hardware=hardware,
space_storage=storage,
space_sleep_time=sleep_time,
space_secrets=env_map_to_key_value_list(parse_env_map(secrets, secrets_file)),
space_variables=env_map_to_key_value_list(parse_env_map(env, env_file)),
space_volumes=parse_volumes(volume),
)
out.result("Repo created", repo_id=repo_url.repo_id, url=str(repo_url))
@repos_cli.command(
"duplicate",
examples=[
"hf repos duplicate openai/gdpval --type dataset",
"hf repos duplicate multimodalart/dreambooth-training my-dreambooth --type space --flavor l4x4 --secrets HF_TOKEN --private",
"hf repos duplicate org/my-space my-space --type space -v hf://org/my-model:/models -v hf://buckets/org/b:/data",
],
)
def repo_duplicate(
from_id: RepoIdArg,
to_id: Annotated[
str | None,
typer.Argument(
help="Destination repo ID (e.g. `myorg/my-copy`). Defaults to your namespace with the same repo name.",
),
] = None,
repo_type: RepoTypeOpt = RepoType.model,
private: PrivateOpt = None,
public: PublicOpt = None,
protected: ProtectedOpt = None,
token: TokenOpt = None,
exist_ok: Annotated[
bool,
typer.Option(
help="Do not raise an error if repo already exists.",
),
] = False,
hardware: SpaceHardwareOpt = None,
storage: SpaceStorageOpt = None,
sleep_time: SpaceSleepTimeOpt = None,
secrets: SecretsOpt = None,
secrets_file: SecretsFileOpt = None,
env: EnvOpt = None,
env_file: EnvFileOpt = None,
volume: VolumesOpt = None,
) -> None:
"""Duplicate a repo on the Hub (model, dataset, or Space)."""
api = get_hf_api(token=token)
repo_url = api.duplicate_repo(
from_id=from_id,
to_id=to_id,
repo_type=repo_type.value,
visibility="private" if private else "public" if public else "protected" if protected else None, # type: ignore [arg-type]
token=token,
exist_ok=exist_ok,
space_hardware=hardware,
space_storage=storage,
space_sleep_time=sleep_time,
space_secrets=env_map_to_key_value_list(parse_env_map(secrets, secrets_file)),
space_variables=env_map_to_key_value_list(parse_env_map(env, env_file)),
space_volumes=parse_volumes(volume),
)
out.result("Repo duplicated", from_id=from_id, to_id=repo_url.repo_id, url=str(repo_url))
@repos_cli.command("delete", examples=["hf repos delete my-model"])
def repo_delete(
repo_id: RepoIdArg,
repo_type: RepoTypeOpt = RepoType.model,
token: TokenOpt = None,
missing_ok: Annotated[
bool,
typer.Option(
help="If set to True, do not raise an error if repo does not exist.",
),
] = False,
yes: Annotated[
bool,
typer.Option(
"-y",
"--yes",
help="Answer Yes to prompt automatically.",
),
] = False,
) -> None:
"""Delete a repo from the Hub. This is an irreversible operation."""
out.confirm(f"You are about to permanently delete {repo_type.value} '{repo_id}'. Proceed?", yes=yes)
api = get_hf_api(token=token)
api.delete_repo(
repo_id=repo_id,
repo_type=repo_type.value,
missing_ok=missing_ok,
)
out.result("Repo deleted", repo_id=repo_id)
@repos_cli.command("move", examples=["hf repos move old-namespace/my-model new-namespace/my-model"])
def repo_move(
from_id: RepoIdArg,
to_id: RepoIdArg,
token: TokenOpt = None,
repo_type: RepoTypeOpt = RepoType.model,
) -> None:
"""Move a repository from a namespace to another namespace."""
api = get_hf_api(token=token)
api.move_repo(
from_id=from_id,
to_id=to_id,
repo_type=repo_type.value,
)
out.result("Repo moved", from_id=from_id, to_id=to_id)
@repos_cli.command(
"settings",
examples=[
"hf repos settings my-model --private",
"hf repos settings my-model --gated auto",
"hf repos settings my-space --repo-type space --protected",
],
)
def repo_settings(
repo_id: RepoIdArg,
gated: Annotated[
GatedChoices | None,
typer.Option(
help="The gated status for the repository.",
),
] = None,
private: PrivateOpt = None,
public: PublicOpt = None,
protected: ProtectedOpt = None,
token: TokenOpt = None,
repo_type: RepoTypeOpt = RepoType.model,
) -> None:
"""Update the settings of a repository."""
api = get_hf_api(token=token)
api.update_repo_settings(
repo_id=repo_id,
gated=(None if gated is None else False if gated is GatedChoices.false else gated.value),
visibility="private" if private else "public" if public else "protected" if protected else None, # type: ignore [arg-type]
repo_type=repo_type.value,
)
out.result("Repo settings updated", repo_id=repo_id)
@repos_cli.command(
"delete-files",
examples=[
"hf repos delete-files my-model file.txt",
'hf repos delete-files my-model "*.json"',
"hf repos delete-files my-model folder/",
],
)
def repo_delete_files(
repo_id: RepoIdArg,
patterns: Annotated[
list[str],
typer.Argument(
help="Glob patterns to match files to delete. Based on fnmatch, '*' matches files recursively.",
),
],
repo_type: RepoTypeOpt = RepoType.model,
revision: RevisionOpt = None,
commit_message: Annotated[
str | None,
typer.Option(
help="The summary / title / first line of the generated commit.",
),
] = None,
commit_description: Annotated[
str | None,
typer.Option(
help="The description of the generated commit.",
),
] = None,
create_pr: Annotated[
bool,
typer.Option(
help="Whether to create a new Pull Request for these changes.",
),
] = False,
token: TokenOpt = None,
) -> None:
"""Delete files from a repo on the Hub."""
api = get_hf_api(token=token)
url = api.delete_files(
delete_patterns=patterns,
repo_id=repo_id,
repo_type=repo_type.value,
revision=revision,
commit_message=commit_message,
commit_description=commit_description,
create_pr=create_pr,
)
out.result("Files deleted", repo_id=repo_id, commit_url=url)
# `hf repos cp` is an alias for the top-level `hf cp` command (see `cli/_cp.py`).
repos_cli.command(
name="cp",
examples=[
# Download (repo or bucket -> local / stdout)
"hf repos cp hf://username/my-model/config.json config.json",
"hf repos cp hf://datasets/username/my-dataset/data.csv data/",
"hf repos cp hf://username/my-model/config.json -",
# Upload (local / stdin -> repo)
"hf repos cp model.safetensors hf://username/my-model/model.safetensors",
"hf repos cp config.json hf://username/my-model/logs/",
"hf repos cp - hf://username/my-model/config.json",
# Remote to remote (repo -> repo)
"hf repos cp hf://username/source-model/config.json hf://username/dest-model/config.json",
"hf repos cp hf://datasets/username/my-dataset/processed/ hf://datasets/username/dest-dataset/processed/",
"hf repos cp hf://username/my-model/logs/ hf://username/archive-model/logs/",
],
)(make_cp("repos"))
@branch_cli.command(
"create",
examples=[
"hf repos branch create my-model dev",
"hf repos branch create my-model dev --revision abc123",
],
)
def branch_create(
repo_id: RepoIdArg,
branch: Annotated[
str,
typer.Argument(
help="The name of the branch to create.",
),
],
revision: RevisionOpt = None,
token: TokenOpt = None,
repo_type: RepoTypeOpt = RepoType.model,
exist_ok: Annotated[
bool,
typer.Option(
help="If set to True, do not raise an error if branch already exists.",
),
] = False,
) -> None:
"""Create a new branch for a repo on the Hub."""
api = get_hf_api(token=token)
api.create_branch(
repo_id=repo_id,
branch=branch,
revision=revision,
repo_type=repo_type.value,
exist_ok=exist_ok,
)
out.result("Branch created", branch=branch, repo_type=repo_type.value, repo_id=repo_id)
@branch_cli.command("delete", examples=["hf repos branch delete my-model dev"])
def branch_delete(
repo_id: RepoIdArg,
branch: Annotated[
str,
typer.Argument(
help="The name of the branch to delete.",
),
],
token: TokenOpt = None,
repo_type: RepoTypeOpt = RepoType.model,
) -> None:
"""Delete a branch from a repo on the Hub."""
api = get_hf_api(token=token)
api.delete_branch(
repo_id=repo_id,
branch=branch,
repo_type=repo_type.value,
)
out.result("Branch deleted", branch=branch, repo_type=repo_type.value, repo_id=repo_id)
@tag_cli.command(
"create",
examples=[
"hf repos tag create my-model v1.0",
'hf repos tag create my-model v1.0 -m "First release"',
],
)
def tag_create(
repo_id: RepoIdArg,
tag: Annotated[
str,
typer.Argument(
help="The name of the tag to create.",
),
],
message: Annotated[
str | None,
typer.Option(
"-m",
"--message",
help="The description of the tag to create.",
),
] = None,
revision: RevisionOpt = None,
token: TokenOpt = None,
repo_type: RepoTypeOpt = RepoType.model,
) -> None:
"""Create a tag for a repo."""
repo_type_str = repo_type.value
api = get_hf_api(token=token)
try:
api.create_tag(repo_id=repo_id, tag=tag, tag_message=message, revision=revision, repo_type=repo_type_str)
except RepositoryNotFoundError as e:
raise CLIError(f"{repo_type_str.capitalize()} '{repo_id}' not found.") from e
except RevisionNotFoundError as e:
raise CLIError(f"Revision '{revision}' not found.") from e
except HfHubHTTPError as e:
if e.response.status_code == 409:
raise CLIError(f"Tag '{tag}' already exists on '{repo_id}'.") from e
raise
out.result("Tag created", tag=tag, repo_type=repo_type_str, repo_id=repo_id)
@tag_cli.command("list | ls", examples=["hf repos tag list my-model"])
def tag_list(
repo_id: RepoIdArg,
token: TokenOpt = None,
repo_type: RepoTypeOpt = RepoType.model,
) -> None:
"""List tags for a repo."""
repo_type_str = repo_type.value
api = get_hf_api(token=token)
try:
refs = api.list_repo_refs(repo_id=repo_id, repo_type=repo_type_str)
except RepositoryNotFoundError as e:
raise CLIError(f"{repo_type_str.capitalize()} '{repo_id}' not found.") from e
items = [{"name": t.name, "target_commit": t.target_commit, "ref": t.ref} for t in refs.tags]
out.table(items)
@tag_cli.command("delete", examples=["hf repos tag delete my-model v1.0"])
def tag_delete(
repo_id: RepoIdArg,
tag: Annotated[
str,
typer.Argument(
help="The name of the tag to delete.",
),
],
yes: Annotated[
bool,
typer.Option(
"-y",
"--yes",
help="Answer Yes to prompt automatically",
),
] = False,
token: TokenOpt = None,
repo_type: RepoTypeOpt = RepoType.model,
) -> None:
"""Delete a tag for a repo."""
repo_type_str = repo_type.value
out.text(f"You are about to delete tag {tag} on {repo_type_str} {repo_id}")
out.confirm("Proceed?", yes=yes)
api = get_hf_api(token=token)
try:
api.delete_tag(repo_id=repo_id, tag=tag, repo_type=repo_type_str)
except RepositoryNotFoundError as e:
raise CLIError(f"{repo_type_str.capitalize()} '{repo_id}' not found.") from e
except RevisionNotFoundError as e:
raise CLIError(f"Tag '{tag}' not found on '{repo_id}'.") from e
out.result("Tag deleted", tag=tag, repo_type=repo_type_str, repo_id=repo_id)