From 96794d0f12e27dd7fcbcbcac1b47425cdea38e58 Mon Sep 17 00:00:00 2001 From: Lucain Pouget Date: Thu, 17 Jul 2025 18:57:31 +0200 Subject: [PATCH 1/7] Rename CLI to 'hf' + reorganize them --- setup.py | 1 + src/huggingface_hub/cli/__init__.py | 27 ++ src/huggingface_hub/cli/_cli_utils.py | 69 +++ src/huggingface_hub/cli/auth.py | 210 +++++++++ src/huggingface_hub/cli/cache.py | 405 ++++++++++++++++++ src/huggingface_hub/cli/download.py | 200 +++++++++ src/huggingface_hub/cli/hf.py | 64 +++ src/huggingface_hub/cli/lfs.py | 198 +++++++++ src/huggingface_hub/cli/repo.py | 285 ++++++++++++ src/huggingface_hub/cli/repo_files.py | 128 ++++++ src/huggingface_hub/cli/system.py | 52 +++ src/huggingface_hub/cli/upload.py | 316 ++++++++++++++ .../cli/upload_large_folder.py | 132 ++++++ src/huggingface_hub/commands/_cli_utils.py | 5 + src/huggingface_hub/commands/delete_cache.py | 4 +- src/huggingface_hub/commands/download.py | 4 + src/huggingface_hub/commands/env.py | 3 + .../commands/huggingface_cli.py | 2 + src/huggingface_hub/commands/repo.py | 4 + src/huggingface_hub/commands/repo_files.py | 4 + src/huggingface_hub/commands/scan_cache.py | 4 +- src/huggingface_hub/commands/tag.py | 4 +- src/huggingface_hub/commands/upload.py | 4 + .../commands/upload_large_folder.py | 4 +- src/huggingface_hub/commands/user.py | 12 +- src/huggingface_hub/commands/version.py | 3 + 26 files changed, 2139 insertions(+), 5 deletions(-) create mode 100644 src/huggingface_hub/cli/__init__.py create mode 100644 src/huggingface_hub/cli/_cli_utils.py create mode 100644 src/huggingface_hub/cli/auth.py create mode 100644 src/huggingface_hub/cli/cache.py create mode 100644 src/huggingface_hub/cli/download.py create mode 100644 src/huggingface_hub/cli/hf.py create mode 100644 src/huggingface_hub/cli/lfs.py create mode 100644 src/huggingface_hub/cli/repo.py create mode 100644 src/huggingface_hub/cli/repo_files.py create mode 100644 src/huggingface_hub/cli/system.py create mode 100644 src/huggingface_hub/cli/upload.py create mode 100644 src/huggingface_hub/cli/upload_large_folder.py diff --git a/setup.py b/setup.py index 7de6594de1..d30610563e 100644 --- a/setup.py +++ b/setup.py @@ -134,6 +134,7 @@ def get_version() -> str: entry_points={ "console_scripts": [ "huggingface-cli=huggingface_hub.commands.huggingface_cli:main", + "hf=huggingface_hub.cli.hf:main", "tiny-agents=huggingface_hub.inference._mcp.cli:app", ], "fsspec.specs": "hf=huggingface_hub.HfFileSystem", diff --git a/src/huggingface_hub/cli/__init__.py b/src/huggingface_hub/cli/__init__.py new file mode 100644 index 0000000000..53927cbfb1 --- /dev/null +++ b/src/huggingface_hub/cli/__init__.py @@ -0,0 +1,27 @@ +# Copyright 202 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. + +from abc import ABC, abstractmethod +from argparse import _SubParsersAction + + +class BaseHuggingfaceCLICommand(ABC): + @staticmethod + @abstractmethod + def register_subcommand(parser: _SubParsersAction): + raise NotImplementedError() + + @abstractmethod + def run(self): + raise NotImplementedError() diff --git a/src/huggingface_hub/cli/_cli_utils.py b/src/huggingface_hub/cli/_cli_utils.py new file mode 100644 index 0000000000..bd56ad6896 --- /dev/null +++ b/src/huggingface_hub/cli/_cli_utils.py @@ -0,0 +1,69 @@ +# Copyright 2022 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 a utility for good-looking prints.""" + +import os +from typing import List, Union + + +class ANSI: + """ + Helper for en.wikipedia.org/wiki/ANSI_escape_code + """ + + _bold = "\u001b[1m" + _gray = "\u001b[90m" + _red = "\u001b[31m" + _reset = "\u001b[0m" + _yellow = "\u001b[33m" + + @classmethod + def bold(cls, s: str) -> str: + return cls._format(s, cls._bold) + + @classmethod + def gray(cls, s: str) -> str: + return cls._format(s, cls._gray) + + @classmethod + def red(cls, s: str) -> str: + return cls._format(s, cls._bold + cls._red) + + @classmethod + def yellow(cls, s: str) -> str: + return cls._format(s, cls._yellow) + + @classmethod + def _format(cls, s: str, code: str) -> str: + if os.environ.get("NO_COLOR"): + # See https://no-color.org/ + return s + return f"{code}{s}{cls._reset}" + + +def tabulate(rows: List[List[Union[str, int]]], headers: List[str]) -> str: + """ + Inspired by: + + - stackoverflow.com/a/8356620/593036 + - stackoverflow.com/questions/9535954/printing-lists-as-tabular-data + """ + col_widths = [max(len(str(x)) for x in col) for col in zip(*rows, headers)] + row_format = ("{{:{}}} " * len(headers)).format(*col_widths) + lines = [] + lines.append(row_format.format(*headers)) + lines.append(row_format.format(*["-" * w for w in col_widths])) + for row in rows: + lines.append(row_format.format(*row)) + return "\n".join(lines) diff --git a/src/huggingface_hub/cli/auth.py b/src/huggingface_hub/cli/auth.py new file mode 100644 index 0000000000..afcf48da82 --- /dev/null +++ b/src/huggingface_hub/cli/auth.py @@ -0,0 +1,210 @@ +# Copyright 2020 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 authenticate to the Hugging Face Hub and interact with your repositories. + +Usage: + # login and save token locally. + hf auth login --token=hf_*** --add-to-git-credential + + # switch between tokens + hf auth switch + + # list all tokens + hf auth list + + # logout from all tokens + hf auth logout + + # check which account you are logged in as + hf auth whoami +""" + +from argparse import _SubParsersAction +from typing import List, Optional + +from requests.exceptions import HTTPError + +from huggingface_hub.commands import BaseHuggingfaceCLICommand +from huggingface_hub.constants import ENDPOINT +from huggingface_hub.hf_api import HfApi + +from .._login import auth_list, auth_switch, login, logout +from ..utils import get_stored_tokens, get_token, logging +from ._cli_utils import ANSI + + +logger = logging.get_logger(__name__) + +try: + from InquirerPy import inquirer + from InquirerPy.base.control import Choice + + _inquirer_py_available = True +except ImportError: + _inquirer_py_available = False + + +class AuthCommands(BaseHuggingfaceCLICommand): + @staticmethod + def register_subcommand(parser: _SubParsersAction): + # Create the main 'auth' command + auth_parser = parser.add_parser("auth", help="Manage authentication (login, logout, etc.).") + auth_subparsers = auth_parser.add_subparsers(help="Authentication subcommands") + + # Add 'login' as a subcommand of 'auth' + login_parser = auth_subparsers.add_parser( + "login", help="Log in using a token from huggingface.co/settings/tokens" + ) + login_parser.add_argument( + "--token", + type=str, + help="Token generated from https://huggingface.co/settings/tokens", + ) + login_parser.add_argument( + "--add-to-git-credential", + action="store_true", + help="Optional: Save token to git credential helper.", + ) + login_parser.set_defaults(func=lambda args: AuthLogin(args)) + + # Add 'logout' as a subcommand of 'auth' + logout_parser = auth_subparsers.add_parser("logout", help="Log out") + logout_parser.add_argument( + "--token-name", + type=str, + help="Optional: Name of the access token to log out from.", + ) + logout_parser.set_defaults(func=lambda args: AuthLogout(args)) + + # Add 'whoami' as a subcommand of 'auth' + whoami_parser = auth_subparsers.add_parser( + "whoami", help="Find out which huggingface.co account you are logged in as." + ) + whoami_parser.set_defaults(func=lambda args: AuthWhoami(args)) + + # Existing subcommands + auth_switch_parser = auth_subparsers.add_parser("switch", help="Switch between access tokens") + auth_switch_parser.add_argument( + "--token-name", + type=str, + help="Optional: Name of the access token to switch to.", + ) + auth_switch_parser.add_argument( + "--add-to-git-credential", + action="store_true", + help="Optional: Save token to git credential helper.", + ) + auth_switch_parser.set_defaults(func=lambda args: AuthSwitch(args)) + + auth_list_parser = auth_subparsers.add_parser("list", help="List all stored access tokens") + auth_list_parser.set_defaults(func=lambda args: AuthList(args)) + + +class BaseAuthCommand: + def __init__(self, args): + self.args = args + self._api = HfApi() + + +class AuthLogin(BaseAuthCommand): + def run(self): + logging.set_verbosity_info() + login( + token=self.args.token, + add_to_git_credential=self.args.add_to_git_credential, + ) + + +class AuthLogout(BaseAuthCommand): + def run(self): + logging.set_verbosity_info() + logout(token_name=self.args.token_name) + + +class AuthSwitch(BaseAuthCommand): + def run(self): + logging.set_verbosity_info() + token_name = self.args.token_name + if token_name is None: + token_name = self._select_token_name() + + if token_name is None: + print("No token name provided. Aborting.") + exit() + auth_switch(token_name, add_to_git_credential=self.args.add_to_git_credential) + + def _select_token_name(self) -> Optional[str]: + token_names = list(get_stored_tokens().keys()) + + if not token_names: + logger.error("No stored tokens found. Please login first.") + return None + + if _inquirer_py_available: + return self._select_token_name_tui(token_names) + # if inquirer is not available, use a simpler terminal UI + print("Available stored tokens:") + for i, token_name in enumerate(token_names, 1): + print(f"{i}. {token_name}") + while True: + try: + choice = input("Enter the number of the token to switch to (or 'q' to quit): ") + if choice.lower() == "q": + return None + index = int(choice) - 1 + if 0 <= index < len(token_names): + return token_names[index] + else: + print("Invalid selection. Please try again.") + except ValueError: + print("Invalid input. Please enter a number or 'q' to quit.") + + def _select_token_name_tui(self, token_names: List[str]) -> Optional[str]: + choices = [Choice(token_name, name=token_name) for token_name in token_names] + try: + return inquirer.select( + message="Select a token to switch to:", + choices=choices, + default=None, + ).execute() + except KeyboardInterrupt: + logger.info("Token selection cancelled.") + return None + + +class AuthList(BaseAuthCommand): + def run(self): + logging.set_verbosity_info() + auth_list() + + +class AuthWhoami(BaseAuthCommand): + def run(self): + token = get_token() + if token is None: + print("Not logged in") + exit() + try: + info = self._api.whoami(token) + print(info["name"]) + orgs = [org["name"] for org in info["orgs"]] + if orgs: + print(ANSI.bold("orgs: "), ",".join(orgs)) + + if ENDPOINT != "https://huggingface.co": + print(f"Authenticated through private endpoint: {ENDPOINT}") + except HTTPError as e: + print(e) + print(ANSI.red(e.response.text)) + exit(1) diff --git a/src/huggingface_hub/cli/cache.py b/src/huggingface_hub/cli/cache.py new file mode 100644 index 0000000000..504fccc64d --- /dev/null +++ b/src/huggingface_hub/cli/cache.py @@ -0,0 +1,405 @@ +# coding=utf-8 +# Copyright 2025-present, the HuggingFace Inc. team. +# +# 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 the 'hf cache' command group with 'scan' and 'delete' subcommands.""" + +import os +import time +from argparse import Namespace, _SubParsersAction +from functools import wraps +from tempfile import mkstemp +from typing import Any, Callable, Iterable, List, Literal, Optional, Union + +from ..utils import ( + CachedRepoInfo, + CachedRevisionInfo, + CacheNotFound, + HFCacheInfo, + scan_cache_dir, +) +from . import BaseHuggingfaceCLICommand +from ._cli_utils import ANSI, tabulate + + +# --- DELETE helpers (from delete_cache.py) --- +try: + from InquirerPy import inquirer + from InquirerPy.base.control import Choice + from InquirerPy.separator import Separator + + _inquirer_py_available = True +except ImportError: + _inquirer_py_available = False + +SortingOption_T = Literal["alphabetical", "lastUpdated", "lastUsed", "size"] +_CANCEL_DELETION_STR = "CANCEL_DELETION" + + +def require_inquirer_py(fn: Callable) -> Callable: + @wraps(fn) + def _inner(*args, **kwargs): + if not _inquirer_py_available: + raise ImportError( + "The 'cache delete' command requires extra dependencies for the TUI.\n" + "Please run 'pip install huggingface_hub[cli]' to install them.\n" + "Otherwise, disable TUI using the '--disable-tui' flag." + ) + return fn(*args, **kwargs) + + return _inner + + +class CacheCommand(BaseHuggingfaceCLICommand): + @staticmethod + def register_subcommand(parser: _SubParsersAction): + cache_parser = parser.add_parser("cache", help="Manage local cache directory.") + cache_subparsers = cache_parser.add_subparsers(dest="cache_command", help="Cache subcommands") + # Scan subcommand + scan_parser = cache_subparsers.add_parser("scan", help="Scan cache directory.") + scan_parser.add_argument( + "--dir", + type=str, + default=None, + help="cache directory to scan (optional). Default to the default HuggingFace cache.", + ) + scan_parser.add_argument( + "-v", + "--verbose", + action="count", + default=0, + help="show a more verbose output", + ) + scan_parser.set_defaults(func=CacheCommand, cache_command="scan") + # Delete subcommand + delete_parser = cache_subparsers.add_parser("delete", help="Delete revisions from the cache directory.") + delete_parser.add_argument( + "--dir", + type=str, + default=None, + help="cache directory (optional). Default to the default HuggingFace cache.", + ) + delete_parser.add_argument( + "--disable-tui", + action="store_true", + help=( + "Disable Terminal User Interface (TUI) mode. Useful if your platform/terminal doesn't support the multiselect menu." + ), + ) + delete_parser.add_argument( + "--sort", + nargs="?", + choices=["alphabetical", "lastUpdated", "lastUsed", "size"], + help=( + "Sort repositories by the specified criteria. Options: " + "'alphabetical' (A-Z), " + "'lastUpdated' (newest first), " + "'lastUsed' (most recent first), " + "'size' (largest first)." + ), + ) + delete_parser.set_defaults(func=CacheCommand, cache_command="delete") + + def __init__(self, args: Namespace) -> None: + self.args = args + self.verbosity: int = getattr(args, "verbose", 0) + self.cache_dir: Optional[str] = getattr(args, "dir", None) + self.disable_tui: bool = getattr(args, "disable_tui", False) + self.sort_by: Optional[SortingOption_T] = getattr(args, "sort", None) + self.cache_command: Optional[str] = getattr(args, "cache_command", None) + + def run(self): + if self.cache_command == "scan": + self._run_scan() + elif self.cache_command == "delete": + self._run_delete() + else: + print("Please specify a cache subcommand (scan or delete). Use -h for help.") + + def _run_scan(self): + try: + t0 = time.time() + hf_cache_info = scan_cache_dir(self.cache_dir) + t1 = time.time() + except CacheNotFound as exc: + cache_dir = exc.cache_dir + print(f"Cache directory not found: {cache_dir}") + return + print(get_table(hf_cache_info, verbosity=self.verbosity)) + print( + f"\nDone in {round(t1 - t0, 1)}s. Scanned {len(hf_cache_info.repos)} repo(s)" + f" for a total of {ANSI.red(hf_cache_info.size_on_disk_str)}." + ) + if len(hf_cache_info.warnings) > 0: + message = f"Got {len(hf_cache_info.warnings)} warning(s) while scanning." + if self.verbosity >= 3: + print(ANSI.gray(message)) + for warning in hf_cache_info.warnings: + print(ANSI.gray(warning)) + else: + print(ANSI.gray(message + " Use -vvv to print details.")) + + def _run_delete(self): + hf_cache_info = scan_cache_dir(self.cache_dir) + if self.disable_tui: + selected_hashes = _manual_review_no_tui(hf_cache_info, preselected=[], sort_by=self.sort_by) + else: + selected_hashes = _manual_review_tui(hf_cache_info, preselected=[], sort_by=self.sort_by) + if len(selected_hashes) > 0 and _CANCEL_DELETION_STR not in selected_hashes: + confirm_message = _get_expectations_str(hf_cache_info, selected_hashes) + " Confirm deletion ?" + if self.disable_tui: + confirmed = _ask_for_confirmation_no_tui(confirm_message) + else: + confirmed = _ask_for_confirmation_tui(confirm_message) + if confirmed: + strategy = hf_cache_info.delete_revisions(*selected_hashes) + print("Start deletion.") + strategy.execute() + print( + f"Done. Deleted {len(strategy.repos)} repo(s) and" + f" {len(strategy.snapshots)} revision(s) for a total of" + f" {strategy.expected_freed_size_str}." + ) + return + print("Deletion is cancelled. Do nothing.") + + +def get_table(hf_cache_info: HFCacheInfo, *, verbosity: int = 0) -> str: + if verbosity == 0: + return tabulate( + rows=[ + [ + repo.repo_id, + repo.repo_type, + "{:>12}".format(repo.size_on_disk_str), + repo.nb_files, + repo.last_accessed_str, + repo.last_modified_str, + ", ".join(sorted(repo.refs)), + str(repo.repo_path), + ] + for repo in sorted(hf_cache_info.repos, key=lambda repo: repo.repo_path) + ], + headers=[ + "REPO ID", + "REPO TYPE", + "SIZE ON DISK", + "NB FILES", + "LAST_ACCESSED", + "LAST_MODIFIED", + "REFS", + "LOCAL PATH", + ], + ) + else: + return tabulate( + rows=[ + [ + repo.repo_id, + repo.repo_type, + revision.commit_hash, + "{:>12}".format(revision.size_on_disk_str), + revision.nb_files, + revision.last_modified_str, + ", ".join(sorted(revision.refs)), + str(revision.snapshot_path), + ] + for repo in sorted(hf_cache_info.repos, key=lambda repo: repo.repo_path) + for revision in sorted(repo.revisions, key=lambda revision: revision.commit_hash) + ], + headers=[ + "REPO ID", + "REPO TYPE", + "REVISION", + "SIZE ON DISK", + "NB FILES", + "LAST_MODIFIED", + "REFS", + "LOCAL PATH", + ], + ) + + +def _get_repo_sorting_key(repo: CachedRepoInfo, sort_by: Optional[SortingOption_T] = None): + if sort_by == "alphabetical": + return (repo.repo_type, repo.repo_id.lower()) + elif sort_by == "lastUpdated": + return -max(rev.last_modified for rev in repo.revisions) + elif sort_by == "lastUsed": + return -repo.last_accessed + elif sort_by == "size": + return -repo.size_on_disk + else: + return (repo.repo_type, repo.repo_id) + + +@require_inquirer_py +def _manual_review_tui( + hf_cache_info: HFCacheInfo, preselected: List[str], sort_by: Optional[SortingOption_T] = None +) -> List[str]: + choices = _get_tui_choices_from_scan(repos=hf_cache_info.repos, preselected=preselected, sort_by=sort_by) + checkbox = inquirer.checkbox( + message="Select revisions to delete:", + choices=choices, + cycle=False, + height=100, + instruction=_get_expectations_str( + hf_cache_info, selected_hashes=[c.value for c in choices if isinstance(c, Choice) and c.enabled] + ), + long_instruction="Press to select, to validate and to quit without modification.", + transformer=lambda result: f"{len(result)} revision(s) selected.", + ) + + def _update_expectations(_): + checkbox._instruction = _get_expectations_str( + hf_cache_info, + selected_hashes=[choice["value"] for choice in checkbox.content_control.choices if choice["enabled"]], + ) + + checkbox.kb_func_lookup["toggle"].append({"func": _update_expectations}) + try: + return checkbox.execute() + except KeyboardInterrupt: + return [] + + +@require_inquirer_py +def _ask_for_confirmation_tui(message: str, default: bool = True) -> bool: + return inquirer.confirm(message, default=default).execute() + + +def _get_tui_choices_from_scan( + repos: Iterable[CachedRepoInfo], preselected: List[str], sort_by: Optional[SortingOption_T] = None +) -> List: + choices: List[Union["Choice", "Separator"]] = [] + choices.append( + Choice( + _CANCEL_DELETION_STR, name="None of the following (if selected, nothing will be deleted).", enabled=False + ) + ) + sorted_repos = sorted(repos, key=lambda repo: _get_repo_sorting_key(repo, sort_by)) + for repo in sorted_repos: + choices.append( + Separator( + f"\n{repo.repo_type.capitalize()} {repo.repo_id} ({repo.size_on_disk_str}, used {repo.last_accessed_str})" + ) + ) + for revision in sorted(repo.revisions, key=_revision_sorting_order): + choices.append( + Choice( + revision.commit_hash, + name=( + f"{revision.commit_hash[:8]}: {', '.join(sorted(revision.refs)) or '(detached)'} # modified {revision.last_modified_str}" + ), + enabled=revision.commit_hash in preselected, + ) + ) + return choices + + +def _manual_review_no_tui( + hf_cache_info: HFCacheInfo, preselected: List[str], sort_by: Optional[SortingOption_T] = None +) -> List[str]: + fd, tmp_path = mkstemp(suffix=".txt") + os.close(fd) + lines = [] + sorted_repos = sorted(hf_cache_info.repos, key=lambda repo: _get_repo_sorting_key(repo, sort_by)) + for repo in sorted_repos: + lines.append( + f"\n# {repo.repo_type.capitalize()} {repo.repo_id} ({repo.size_on_disk_str}, used {repo.last_accessed_str})" + ) + for revision in sorted(repo.revisions, key=_revision_sorting_order): + lines.append( + f"{'' if revision.commit_hash in preselected else '#'} {revision.commit_hash} # Refs: {', '.join(sorted(revision.refs)) or '(detached)'} # modified {revision.last_modified_str}" + ) + with open(tmp_path, "w") as f: + f.write(_MANUAL_REVIEW_NO_TUI_INSTRUCTIONS) + f.write("\n".join(lines)) + instructions = f""" + TUI is disabled. In order to select which revisions you want to delete, please edit + the following file using the text editor of your choice. Instructions for manual + editing are located at the beginning of the file. Edit the file, save it and confirm + to continue. + File to edit: {ANSI.bold(tmp_path)} + """ + print("\n".join(line.strip() for line in instructions.strip().split("\n"))) + while True: + selected_hashes = _read_manual_review_tmp_file(tmp_path) + if _ask_for_confirmation_no_tui( + _get_expectations_str(hf_cache_info, selected_hashes) + " Continue ?", default=False + ): + break + os.remove(tmp_path) + return sorted(selected_hashes) + + +def _ask_for_confirmation_no_tui(message: str, default: bool = True) -> bool: + YES = ("y", "yes", "1") + NO = ("n", "no", "0") + DEFAULT = "" + ALL = YES + NO + (DEFAULT,) + full_message = message + (" (Y/n) " if default else " (y/N) ") + while True: + answer = input(full_message).lower() + if answer == DEFAULT: + return default + if answer in YES: + return True + if answer in NO: + return False + print(f"Invalid input. Must be one of {ALL}") + + +def _get_expectations_str(hf_cache_info: HFCacheInfo, selected_hashes: List[str]) -> str: + if _CANCEL_DELETION_STR in selected_hashes: + return "Nothing will be deleted." + strategy = hf_cache_info.delete_revisions(*selected_hashes) + return f"{len(selected_hashes)} revisions selected counting for {strategy.expected_freed_size_str}." + + +def _read_manual_review_tmp_file(tmp_path: str) -> List[str]: + with open(tmp_path) as f: + content = f.read() + lines = [line.strip() for line in content.split("\n")] + selected_lines = [line for line in lines if not line.startswith("#")] + selected_hashes = [line.split("#")[0].strip() for line in selected_lines] + return [hash for hash in selected_hashes if len(hash) > 0] + + +_MANUAL_REVIEW_NO_TUI_INSTRUCTIONS = f""" +# INSTRUCTIONS +# ------------ +# This is a temporary file created by running `hf cache delete --disable-tui`. It contains a set of revisions that can be deleted from your local cache directory. +# +# Please manually review the revisions you want to delete: +# - Revision hashes can be commented out with '#'. +# - Only non-commented revisions in this file will be deleted. +# - Revision hashes that are removed from this file are ignored as well. +# - If `{_CANCEL_DELETION_STR}` line is uncommented, the all cache deletion is cancelled and no changes will be applied. +# +# Once you've manually reviewed this file, please confirm deletion in the terminal. This file will be automatically removed once done. +# ------------ + +# KILL SWITCH +# ------------ +# Un-comment following line to completely cancel the deletion process +# {_CANCEL_DELETION_STR} +# ------------ + +# REVISIONS +# ------------ +""".strip() + + +def _revision_sorting_order(revision: CachedRevisionInfo) -> Any: + return revision.last_modified diff --git a/src/huggingface_hub/cli/download.py b/src/huggingface_hub/cli/download.py new file mode 100644 index 0000000000..5c22fce372 --- /dev/null +++ b/src/huggingface_hub/cli/download.py @@ -0,0 +1,200 @@ +# coding=utf-8 +# Copyright 202-present, the HuggingFace Inc. team. +# +# 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 command to download files from the Hub with the CLI. + +Usage: + hf download --help + + # Download file + hf download gpt2 config.json + + # Download entire repo + hf download fffiloni/zeroscope --repo-type=space --revision=refs/pr/78 + + # Download repo with filters + hf download gpt2 --include="*.safetensors" + + # Download with token + hf download Wauplin/private-model --token=hf_*** + + # Download quietly (no progress bar, no warnings, only the returned path) + hf download gpt2 config.json --quiet + + # Download to local dir + hf download gpt2 --local-dir=./models/gpt2 +""" + +import warnings +from argparse import Namespace, _SubParsersAction +from typing import List, Optional + +from huggingface_hub import logging +from huggingface_hub._snapshot_download import snapshot_download +from huggingface_hub.commands import BaseHuggingfaceCLICommand +from huggingface_hub.file_download import hf_hub_download +from huggingface_hub.utils import disable_progress_bars, enable_progress_bars + + +logger = logging.get_logger(__name__) + + +class DownloadCommand(BaseHuggingfaceCLICommand): + @staticmethod + def register_subcommand(parser: _SubParsersAction): + download_parser = parser.add_parser("download", help="Download files from the Hub") + download_parser.add_argument( + "repo_id", type=str, help="ID of the repo to download from (e.g. `username/repo-name`)." + ) + download_parser.add_argument( + "filenames", type=str, nargs="*", help="Files to download (e.g. `config.json`, `data/metadata.jsonl`)." + ) + download_parser.add_argument( + "--repo-type", + choices=["model", "dataset", "space"], + default="model", + help="Type of repo to download from (defaults to 'model').", + ) + download_parser.add_argument( + "--revision", + type=str, + help="An optional Git revision id which can be a branch name, a tag, or a commit hash.", + ) + download_parser.add_argument( + "--include", nargs="*", type=str, help="Glob patterns to match files to download." + ) + download_parser.add_argument( + "--exclude", nargs="*", type=str, help="Glob patterns to exclude from files to download." + ) + download_parser.add_argument( + "--cache-dir", type=str, help="Path to the directory where to save the downloaded files." + ) + download_parser.add_argument( + "--local-dir", + type=str, + help=( + "If set, the downloaded file will be placed under this directory. Check out" + " https://huggingface.co/docs/huggingface_hub/guides/download#download-files-to-local-folder for more" + " details." + ), + ) + download_parser.add_argument( + "--local-dir-use-symlinks", + choices=["auto", "True", "False"], + help=("Deprecated and ignored. Downloading to a local directory does not use symlinks anymore."), + ) + download_parser.add_argument( + "--force-download", + action="store_true", + help="If True, the files will be downloaded even if they are already cached.", + ) + download_parser.add_argument( + "--resume-download", + action="store_true", + help="Deprecated and ignored. Downloading a file to local dir always attempts to resume previously interrupted downloads (unless hf-transfer is enabled).", + ) + download_parser.add_argument( + "--token", type=str, help="A User Access Token generated from https://huggingface.co/settings/tokens" + ) + download_parser.add_argument( + "--quiet", + action="store_true", + help="If True, progress bars are disabled and only the path to the download files is printed.", + ) + download_parser.add_argument( + "--max-workers", + type=int, + default=8, + help="Maximum number of workers to use for downloading files. Default is 8.", + ) + download_parser.set_defaults(func=DownloadCommand) + + def __init__(self, args: Namespace) -> None: + self.token = args.token + self.repo_id: str = args.repo_id + self.filenames: List[str] = args.filenames + self.repo_type: str = args.repo_type + self.revision: Optional[str] = args.revision + self.include: Optional[List[str]] = args.include + self.exclude: Optional[List[str]] = args.exclude + self.cache_dir: Optional[str] = args.cache_dir + self.local_dir: Optional[str] = args.local_dir + self.force_download: bool = args.force_download + self.resume_download: Optional[bool] = args.resume_download or None + self.quiet: bool = args.quiet + self.max_workers: int = args.max_workers + + if args.local_dir_use_symlinks is not None: + warnings.warn( + "Ignoring --local-dir-use-symlinks. Downloading to a local directory does not use symlinks anymore.", + FutureWarning, + ) + + def run(self) -> None: + if self.quiet: + disable_progress_bars() + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + print(self._download()) # Print path to downloaded files + enable_progress_bars() + else: + logging.set_verbosity_info() + print(self._download()) # Print path to downloaded files + logging.set_verbosity_warning() + + def _download(self) -> str: + # Warn user if patterns are ignored + if len(self.filenames) > 0: + if self.include is not None and len(self.include) > 0: + warnings.warn("Ignoring `--include` since filenames have being explicitly set.") + if self.exclude is not None and len(self.exclude) > 0: + warnings.warn("Ignoring `--exclude` since filenames have being explicitly set.") + + # Single file to download: use `hf_hub_download` + if len(self.filenames) == 1: + return hf_hub_download( + repo_id=self.repo_id, + repo_type=self.repo_type, + revision=self.revision, + filename=self.filenames[0], + cache_dir=self.cache_dir, + resume_download=self.resume_download, + force_download=self.force_download, + token=self.token, + local_dir=self.local_dir, + library_name="hf", + ) + + # Otherwise: use `snapshot_download` to ensure all files comes from same revision + elif len(self.filenames) == 0: + allow_patterns = self.include + ignore_patterns = self.exclude + else: + allow_patterns = self.filenames + ignore_patterns = None + + return snapshot_download( + repo_id=self.repo_id, + repo_type=self.repo_type, + revision=self.revision, + allow_patterns=allow_patterns, + ignore_patterns=ignore_patterns, + resume_download=self.resume_download, + force_download=self.force_download, + cache_dir=self.cache_dir, + token=self.token, + local_dir=self.local_dir, + library_name="hf", + max_workers=self.max_workers, + ) diff --git a/src/huggingface_hub/cli/hf.py b/src/huggingface_hub/cli/hf.py new file mode 100644 index 0000000000..f17314b562 --- /dev/null +++ b/src/huggingface_hub/cli/hf.py @@ -0,0 +1,64 @@ +# Copyright 2020 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. + +from argparse import ArgumentParser + +from huggingface_hub.cli.auth import AuthCommands +from huggingface_hub.cli.cache import CacheCommand +from huggingface_hub.cli.download import DownloadCommand +from huggingface_hub.cli.lfs import LfsCommands +from huggingface_hub.cli.repo import RepoCommands +from huggingface_hub.cli.repo_files import RepoFilesCommand +from huggingface_hub.cli.system import EnvironmentCommand, VersionCommand +from huggingface_hub.cli.upload import UploadCommand +from huggingface_hub.cli.upload_large_folder import UploadLargeFolderCommand + + +def main(): + parser = ArgumentParser("hf", usage="hf []") + commands_parser = parser.add_subparsers(help="hf command helpers") + + # Register commands + AuthCommands.register_subcommand(commands_parser) + CacheCommand.register_subcommand(commands_parser) + DownloadCommand.register_subcommand(commands_parser) + RepoCommands.register_subcommand(commands_parser) + RepoFilesCommand.register_subcommand(commands_parser) + UploadCommand.register_subcommand(commands_parser) + UploadLargeFolderCommand.register_subcommand(commands_parser) + + # System commands + EnvironmentCommand.register_subcommand(commands_parser) + VersionCommand.register_subcommand(commands_parser) + + # LFS commands (hidden in --help) + LfsCommands.register_subcommand(commands_parser) + + # Legacy commands + + # Experimental + + # Let's go + args = parser.parse_args() + if not hasattr(args, "func"): + parser.print_help() + exit(1) + + # Run + service = args.func(args) + service.run() + + +if __name__ == "__main__": + main() diff --git a/src/huggingface_hub/cli/lfs.py b/src/huggingface_hub/cli/lfs.py new file mode 100644 index 0000000000..e4c5b900c8 --- /dev/null +++ b/src/huggingface_hub/cli/lfs.py @@ -0,0 +1,198 @@ +""" +Implementation of a custom transfer agent for the transfer type "multipart" for +git-lfs. + +Inspired by: +github.com/cbartz/git-lfs-swift-transfer-agent/blob/master/git_lfs_swift_transfer.py + +Spec is: github.com/git-lfs/git-lfs/blob/master/docs/custom-transfers.md + + +To launch debugger while developing: + +``` [lfs "customtransfer.multipart"] +path = /path/to/huggingface_hub/.env/bin/python args = -m debugpy --listen 5678 +--wait-for-client +/path/to/huggingface_hub/src/huggingface_hub/commands/huggingface_cli.py +lfs-multipart-upload ```""" + +import json +import os +import subprocess +import sys +from argparse import _SubParsersAction +from typing import Dict, List, Optional + +from huggingface_hub.commands import BaseHuggingfaceCLICommand +from huggingface_hub.lfs import LFS_MULTIPART_UPLOAD_COMMAND + +from ..utils import get_session, hf_raise_for_status, logging +from ..utils._lfs import SliceFileObj + + +logger = logging.get_logger(__name__) + + +class LfsCommands(BaseHuggingfaceCLICommand): + """ + Implementation of a custom transfer agent for the transfer type "multipart" + for git-lfs. This lets users upload large files >5GB 🔥. Spec for LFS custom + transfer agent is: + https://github.com/git-lfs/git-lfs/blob/master/docs/custom-transfers.md + + This introduces two commands to the CLI: + + 1. $ hf lfs-enable-largefiles + + This should be executed once for each model repo that contains a model file + >5GB. It's documented in the error message you get if you just try to git + push a 5GB file without having enabled it before. + + 2. $ hf lfs-multipart-upload + + This command is called by lfs directly and is not meant to be called by the + user. + """ + + @staticmethod + def register_subcommand(parser: _SubParsersAction): + enable_parser = parser.add_parser("lfs-enable-largefiles", add_help=False) + enable_parser.add_argument("path", type=str, help="Local path to repository you want to configure.") + enable_parser.set_defaults(func=lambda args: LfsEnableCommand(args)) + + # Command will get called by git-lfs, do not call it directly. + upload_parser = parser.add_parser(LFS_MULTIPART_UPLOAD_COMMAND, add_help=False) + upload_parser.set_defaults(func=lambda args: LfsUploadCommand(args)) + + +class LfsEnableCommand: + def __init__(self, args): + self.args = args + + def run(self): + local_path = os.path.abspath(self.args.path) + if not os.path.isdir(local_path): + print("This does not look like a valid git repo.") + exit(1) + subprocess.run( + "git config lfs.customtransfer.multipart.path hf".split(), + check=True, + cwd=local_path, + ) + subprocess.run( + f"git config lfs.customtransfer.multipart.args {LFS_MULTIPART_UPLOAD_COMMAND}".split(), + check=True, + cwd=local_path, + ) + print("Local repo set up for largefiles") + + +def write_msg(msg: Dict): + """Write out the message in Line delimited JSON.""" + msg_str = json.dumps(msg) + "\n" + sys.stdout.write(msg_str) + sys.stdout.flush() + + +def read_msg() -> Optional[Dict]: + """Read Line delimited JSON from stdin.""" + msg = json.loads(sys.stdin.readline().strip()) + + if "terminate" in (msg.get("type"), msg.get("event")): + # terminate message received + return None + + if msg.get("event") not in ("download", "upload"): + logger.critical("Received unexpected message") + sys.exit(1) + + return msg + + +class LfsUploadCommand: + def __init__(self, args) -> None: + self.args = args + + def run(self) -> None: + # Immediately after invoking a custom transfer process, git-lfs + # sends initiation data to the process over stdin. + # This tells the process useful information about the configuration. + init_msg = json.loads(sys.stdin.readline().strip()) + if not (init_msg.get("event") == "init" and init_msg.get("operation") == "upload"): + write_msg({"error": {"code": 32, "message": "Wrong lfs init operation"}}) + sys.exit(1) + + # The transfer process should use the information it needs from the + # initiation structure, and also perform any one-off setup tasks it + # needs to do. It should then respond on stdout with a simple empty + # confirmation structure, as follows: + write_msg({}) + + # After the initiation exchange, git-lfs will send any number of + # transfer requests to the stdin of the transfer process, in a serial sequence. + while True: + msg = read_msg() + if msg is None: + # When all transfers have been processed, git-lfs will send + # a terminate event to the stdin of the transfer process. + # On receiving this message the transfer process should + # clean up and terminate. No response is expected. + sys.exit(0) + + oid = msg["oid"] + filepath = msg["path"] + completion_url = msg["action"]["href"] + header = msg["action"]["header"] + chunk_size = int(header.pop("chunk_size")) + presigned_urls: List[str] = list(header.values()) + + # Send a "started" progress event to allow other workers to start. + # Otherwise they're delayed until first "progress" event is reported, + # i.e. after the first 5GB by default (!) + write_msg( + { + "event": "progress", + "oid": oid, + "bytesSoFar": 1, + "bytesSinceLast": 0, + } + ) + + parts = [] + with open(filepath, "rb") as file: + for i, presigned_url in enumerate(presigned_urls): + with SliceFileObj( + file, + seek_from=i * chunk_size, + read_limit=chunk_size, + ) as data: + r = get_session().put(presigned_url, data=data) + hf_raise_for_status(r) + parts.append( + { + "etag": r.headers.get("etag"), + "partNumber": i + 1, + } + ) + # In order to support progress reporting while data is uploading / downloading, + # the transfer process should post messages to stdout + write_msg( + { + "event": "progress", + "oid": oid, + "bytesSoFar": (i + 1) * chunk_size, + "bytesSinceLast": chunk_size, + } + ) + # Not precise but that's ok. + + r = get_session().post( + completion_url, + json={ + "oid": oid, + "parts": parts, + }, + ) + hf_raise_for_status(r) + + write_msg({"event": "complete", "oid": oid}) diff --git a/src/huggingface_hub/cli/repo.py b/src/huggingface_hub/cli/repo.py new file mode 100644 index 0000000000..64c80617aa --- /dev/null +++ b/src/huggingface_hub/cli/repo.py @@ -0,0 +1,285 @@ +# 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 repo create my-cool-dataset --repo-type=dataset + + # create a private model repo on the Hub + hf repo create my-cool-model --private +""" + +import argparse +from argparse import _SubParsersAction +from typing import Optional + +from requests.exceptions import HTTPError + +from huggingface_hub.commands import BaseHuggingfaceCLICommand +from huggingface_hub.commands._cli_utils import ANSI +from huggingface_hub.constants import REPO_TYPES, SPACES_SDK_TYPES +from huggingface_hub.errors import HfHubHTTPError, RepositoryNotFoundError, RevisionNotFoundError +from huggingface_hub.hf_api import HfApi +from huggingface_hub.utils import logging + + +logger = logging.get_logger(__name__) + + +class RepoCommands(BaseHuggingfaceCLICommand): + @staticmethod + def register_subcommand(parser: _SubParsersAction): + repo_parser = parser.add_parser("repo", help="Manage repos on the Hub.") + repo_subparsers = repo_parser.add_subparsers(help="huggingface.co repos related commands") + # CREATE + repo_create_parser = repo_subparsers.add_parser("create", help="Create a new repo on huggingface.co") + repo_create_parser.add_argument( + "repo_id", + type=str, + help="The ID of the repo to create to (e.g. `username/repo-name`). The username is optional and will be set to your username if not provided.", + ) + repo_create_parser.add_argument( + "--repo-type", + type=str, + help='Optional: set to "dataset" or "space" if creating a dataset or space, default is model.', + ) + repo_create_parser.add_argument( + "--space_sdk", + type=str, + help='Optional: Hugging Face Spaces SDK type. Required when --type is set to "space".', + choices=SPACES_SDK_TYPES, + ) + repo_create_parser.add_argument( + "--private", + action="store_true", + help="Whether to create a private repository. Defaults to public unless the organization's default is private.", + ) + repo_create_parser.add_argument( + "--token", + type=str, + help="Hugging Face token. Will default to the locally saved token if not provided.", + ) + repo_create_parser.add_argument( + "--exist-ok", + action="store_true", + help="Do not raise an error if repo already exists.", + ) + repo_create_parser.add_argument( + "--resource-group-id", + type=str, + help="Resource group in which to create the repo. Resource groups is only available for Enterprise Hub organizations.", + ) + repo_create_parser.add_argument( + "--type", + type=str, + help="[Deprecated]: use --repo-type instead.", + ) + repo_create_parser.add_argument( + "-y", + "--yes", + action="store_true", + help="[Deprecated] no effect.", + ) + repo_create_parser.add_argument( + "--organization", type=str, help="[Deprecated] Pass the organization namespace directly in the repo_id." + ) + repo_create_parser.set_defaults(func=lambda args: RepoCreateCommand(args)) + + # TAG SUBCOMMANDS + repo_tag_parser = repo_subparsers.add_parser("tag", help="Manage tags for a repo on the Hub.") + tag_subparsers = repo_tag_parser.add_subparsers(help="Tag actions", dest="tag_action", required=True) + + # tag create + tag_create_parser = tag_subparsers.add_parser("create", help="Create a tag for a repo.") + tag_create_parser.add_argument( + "repo_id", type=str, help="The ID of the repo to tag (e.g. `username/repo-name`)." + ) + tag_create_parser.add_argument("tag", type=str, help="The name of the tag to create.") + tag_create_parser.add_argument("-m", "--message", type=str, help="The description of the tag to create.") + tag_create_parser.add_argument("--revision", type=str, help="The git revision to tag.") + tag_create_parser.add_argument( + "--token", type=str, help="A User Access Token generated from https://huggingface.co/settings/tokens." + ) + tag_create_parser.add_argument( + "--repo-type", + choices=["model", "dataset", "space"], + default="model", + help="Set the type of repository (model, dataset, or space).", + ) + tag_create_parser.set_defaults(func=lambda args: RepoTagCreateCommand(args)) + + # tag list + tag_list_parser = tag_subparsers.add_parser("list", help="List tags for a repo.") + tag_list_parser.add_argument( + "repo_id", type=str, help="The ID of the repo to list tags for (e.g. `username/repo-name`)." + ) + tag_list_parser.add_argument( + "--token", type=str, help="A User Access Token generated from https://huggingface.co/settings/tokens." + ) + tag_list_parser.add_argument( + "--repo-type", + choices=["model", "dataset", "space"], + default="model", + help="Set the type of repository (model, dataset, or space).", + ) + tag_list_parser.set_defaults(func=lambda args: RepoTagListCommand(args)) + + # tag delete + tag_delete_parser = tag_subparsers.add_parser("delete", help="Delete a tag from a repo.") + tag_delete_parser.add_argument( + "repo_id", type=str, help="The ID of the repo to delete the tag from (e.g. `username/repo-name`)." + ) + tag_delete_parser.add_argument("tag", type=str, help="The name of the tag to delete.") + tag_delete_parser.add_argument("-y", "--yes", action="store_true", help="Answer Yes to prompts automatically.") + tag_delete_parser.add_argument( + "--token", type=str, help="A User Access Token generated from https://huggingface.co/settings/tokens." + ) + tag_delete_parser.add_argument( + "--repo-type", + choices=["model", "dataset", "space"], + default="model", + help="Set the type of repository (model, dataset, or space).", + ) + tag_delete_parser.set_defaults(func=lambda args: RepoTagDeleteCommand(args)) + + +class RepoCreateCommand: + def __init__(self, args: argparse.Namespace): + self.repo_id: str = args.repo_id + self.repo_type: Optional[str] = args.repo_type or args.type + self.space_sdk: Optional[str] = args.space_sdk + self.organization: Optional[str] = args.organization + self.yes: bool = args.yes + self.private: bool = args.private + self.token: Optional[str] = args.token + self.exist_ok: bool = args.exist_ok + self.resource_group_id: Optional[str] = args.resource_group_id + + if args.type is not None: + print( + ANSI.yellow( + "The --type argument is deprecated and will be removed in a future version. Use --repo-type instead." + ) + ) + if self.organization is not None: + print( + ANSI.yellow( + "The --organization argument is deprecated and will be removed in a future version. Pass the organization namespace directly in the repo_id." + ) + ) + if self.yes: + print( + ANSI.yellow( + "The --yes argument is deprecated and will be removed in a future version. It does not have any effect." + ) + ) + + self._api = HfApi() + + def run(self): + if self.organization is not None: + if "/" in self.repo_id: + print(ANSI.red("You cannot pass both --organization and a repo_id with a namespace.")) + exit(1) + self.repo_id = f"{self.organization}/{self.repo_id}" + + repo_url = self._api.create_repo( + repo_id=self.repo_id, + repo_type=self.repo_type, + private=self.private, + token=self.token, + exist_ok=self.exist_ok, + resource_group_id=self.resource_group_id, + space_sdk=self.space_sdk, + ) + print(f"Successfully created {ANSI.bold(repo_url.repo_id)} on the Hub.") + print(f"Your repo is now available at {ANSI.bold(repo_url)}") + + +class RepoTagCommand: + def __init__(self, args): + self.args = args + self.api = HfApi(token=getattr(args, "token", None)) + self.repo_id = args.repo_id + self.repo_type = getattr(args, "repo_type", "model") + if self.repo_type not in REPO_TYPES: + print("Invalid repo --repo-type") + exit(1) + + +class RepoTagCreateCommand(RepoTagCommand): + def run(self): + print(f"You are about to create tag {ANSI.bold(self.args.tag)} on {self.repo_type} {ANSI.bold(self.repo_id)}") + try: + self.api.create_tag( + repo_id=self.repo_id, + tag=self.args.tag, + tag_message=getattr(self.args, "message", None), + revision=getattr(self.args, "revision", None), + repo_type=self.repo_type, + ) + except RepositoryNotFoundError: + print(f"{self.repo_type.capitalize()} {ANSI.bold(self.repo_id)} not found.") + exit(1) + except RevisionNotFoundError: + print(f"Revision {ANSI.bold(getattr(self.args, 'revision', None))} not found.") + exit(1) + except HfHubHTTPError as e: + if e.response.status_code == 409: + print(f"Tag {ANSI.bold(self.args.tag)} already exists on {ANSI.bold(self.repo_id)}") + exit(1) + raise e + print(f"Tag {ANSI.bold(self.args.tag)} created on {ANSI.bold(self.repo_id)}") + + +class RepoTagListCommand(RepoTagCommand): + def run(self): + try: + refs = self.api.list_repo_refs( + repo_id=self.repo_id, + repo_type=self.repo_type, + ) + except RepositoryNotFoundError: + print(f"{self.repo_type.capitalize()} {ANSI.bold(self.repo_id)} not found.") + exit(1) + except HTTPError as e: + print(e) + print(ANSI.red(e.response.text)) + exit(1) + if len(refs.tags) == 0: + print("No tags found") + exit(0) + print(f"Tags for {self.repo_type} {ANSI.bold(self.repo_id)}:") + for tag in refs.tags: + print(tag.name) + + +class RepoTagDeleteCommand(RepoTagCommand): + def run(self): + print(f"You are about to delete tag {ANSI.bold(self.args.tag)} on {self.repo_type} {ANSI.bold(self.repo_id)}") + if not getattr(self.args, "yes", False): + choice = input("Proceed? [Y/n] ").lower() + if choice not in ("", "y", "yes"): + print("Abort") + exit() + try: + self.api.delete_tag(repo_id=self.repo_id, tag=self.args.tag, repo_type=self.repo_type) + except RepositoryNotFoundError: + print(f"{self.repo_type.capitalize()} {ANSI.bold(self.repo_id)} not found.") + exit(1) + except RevisionNotFoundError: + print(f"Tag {ANSI.bold(self.args.tag)} not found on {ANSI.bold(self.repo_id)}") + exit(1) + print(f"Tag {ANSI.bold(self.args.tag)} deleted on {ANSI.bold(self.repo_id)}") diff --git a/src/huggingface_hub/cli/repo_files.py b/src/huggingface_hub/cli/repo_files.py new file mode 100644 index 0000000000..34fbeb09c2 --- /dev/null +++ b/src/huggingface_hub/cli/repo_files.py @@ -0,0 +1,128 @@ +# coding=utf-8 +# Copyright 2023-present, the HuggingFace Inc. team. +# +# 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 command to update or delete files in a repository using the CLI. + +Usage: + # delete all + hf repo-files delete "*" + + # delete single file + hf repo-files delete file.txt + + # delete single folder + hf repo-files delete folder/ + + # delete multiple + hf repo-files delete file.txt folder/ file2.txt + + # delete multiple patterns + hf repo-files delete file.txt "*.json" "folder/*.parquet" + + # delete from different revision / repo-type + hf repo-files delete file.txt --revision=refs/pr/1 --repo-type=dataset +""" + +from argparse import _SubParsersAction +from typing import List, Optional + +from huggingface_hub import logging +from huggingface_hub.commands import BaseHuggingfaceCLICommand +from huggingface_hub.hf_api import HfApi + + +logger = logging.get_logger(__name__) + + +class DeleteFilesSubCommand: + def __init__(self, args) -> None: + self.args = args + self.repo_id: str = args.repo_id + self.repo_type: Optional[str] = args.repo_type + self.revision: Optional[str] = args.revision + self.api: HfApi = HfApi(token=args.token, library_name="hf") + self.patterns: List[str] = args.patterns + self.commit_message: Optional[str] = args.commit_message + self.commit_description: Optional[str] = args.commit_description + self.create_pr: bool = args.create_pr + self.token: Optional[str] = args.token + + def run(self) -> None: + logging.set_verbosity_info() + url = self.api.delete_files( + delete_patterns=self.patterns, + repo_id=self.repo_id, + repo_type=self.repo_type, + revision=self.revision, + commit_message=self.commit_message, + commit_description=self.commit_description, + create_pr=self.create_pr, + ) + print(f"Files correctly deleted from repo. Commit: {url}.") + logging.set_verbosity_warning() + + +class RepoFilesCommand(BaseHuggingfaceCLICommand): + @staticmethod + def register_subcommand(parser: _SubParsersAction): + repo_files_parser = parser.add_parser("repo-files", help="Manage files in a repo on the Hub.") + repo_files_subparsers = repo_files_parser.add_subparsers( + help="Action to execute against the files.", + required=True, + ) + delete_subparser = repo_files_subparsers.add_parser( + "delete", + help="Delete files from a repo on the Hub", + ) + delete_subparser.set_defaults(func=lambda args: DeleteFilesSubCommand(args)) + delete_subparser.add_argument( + "repo_id", type=str, help="The ID of the repo to manage (e.g. `username/repo-name`)." + ) + delete_subparser.add_argument( + "patterns", + nargs="+", + type=str, + help="Glob patterns to match files to delete.", + ) + delete_subparser.add_argument( + "--repo-type", + choices=["model", "dataset", "space"], + default="model", + help="Type of the repo to upload to (e.g. `dataset`).", + ) + delete_subparser.add_argument( + "--revision", + type=str, + help=( + "An optional Git revision to push to. It can be a branch name " + "or a PR reference. If revision does not" + " exist and `--create-pr` is not set, a branch will be automatically created." + ), + ) + delete_subparser.add_argument( + "--commit-message", type=str, help="The summary / title / first line of the generated commit." + ) + delete_subparser.add_argument( + "--commit-description", type=str, help="The description of the generated commit." + ) + delete_subparser.add_argument( + "--create-pr", action="store_true", help="Whether to create a new Pull Request for these changes." + ) + delete_subparser.add_argument( + "--token", + type=str, + help="A User Access Token generated from https://huggingface.co/settings/tokens", + ) + + repo_files_parser.set_defaults(func=RepoFilesCommand) diff --git a/src/huggingface_hub/cli/system.py b/src/huggingface_hub/cli/system.py new file mode 100644 index 0000000000..03650175e9 --- /dev/null +++ b/src/huggingface_hub/cli/system.py @@ -0,0 +1,52 @@ +# Copyright 2022 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 print information about the environment and version. + +Usage: + hf env + hf version +""" + +from argparse import _SubParsersAction + +from huggingface_hub import __version__ + +from ..utils import dump_environment_info +from . import BaseHuggingfaceCLICommand + + +class EnvironmentCommand(BaseHuggingfaceCLICommand): + def __init__(self, args): + self.args = args + + @staticmethod + def register_subcommand(parser: _SubParsersAction): + env_parser = parser.add_parser("env", help="Print information about the environment.") + env_parser.set_defaults(func=EnvironmentCommand) + + def run(self) -> None: + dump_environment_info() + + +class VersionCommand(BaseHuggingfaceCLICommand): + def __init__(self, args): + self.args = args + + @staticmethod + def register_subcommand(parser: _SubParsersAction): + version_parser = parser.add_parser("version", help="Print information about the hf version.") + version_parser.set_defaults(func=VersionCommand) + + def run(self) -> None: + print(f"huggingface_hub version: {__version__}") diff --git a/src/huggingface_hub/cli/upload.py b/src/huggingface_hub/cli/upload.py new file mode 100644 index 0000000000..07ab79bf24 --- /dev/null +++ b/src/huggingface_hub/cli/upload.py @@ -0,0 +1,316 @@ +# coding=utf-8 +# Copyright 2023-present, the HuggingFace Inc. team. +# +# 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 command to upload a repo or file with the CLI. + +Usage: + # Upload file (implicit) + hf upload my-cool-model ./my-cool-model.safetensors + + # Upload file (explicit) + hf upload my-cool-model ./my-cool-model.safetensors model.safetensors + + # Upload directory (implicit). If `my-cool-model/` is a directory it will be uploaded, otherwise an exception is raised. + hf upload my-cool-model + + # Upload directory (explicit) + hf upload my-cool-model ./models/my-cool-model . + + # Upload filtered directory (example: tensorboard logs except for the last run) + hf upload my-cool-model ./model/training /logs --include "*.tfevents.*" --exclude "*20230905*" + + # Upload with wildcard + hf upload my-cool-model "./model/training/*.safetensors" + + # Upload private dataset + hf upload Wauplin/my-cool-dataset ./data . --repo-type=dataset --private + + # Upload with token + hf upload Wauplin/my-cool-model --token=hf_**** + + # Sync local Space with Hub (upload new files, delete removed files) + hf upload Wauplin/space-example --repo-type=space --exclude="/logs/*" --delete="*" --commit-message="Sync local Space with Hub" + + # Schedule commits every 30 minutes + hf upload Wauplin/my-cool-model --every=30 +""" + +import os +import time +import warnings +from argparse import Namespace, _SubParsersAction +from typing import List, Optional + +from huggingface_hub import logging +from huggingface_hub._commit_scheduler import CommitScheduler +from huggingface_hub.commands import BaseHuggingfaceCLICommand +from huggingface_hub.constants import HF_HUB_ENABLE_HF_TRANSFER +from huggingface_hub.errors import RevisionNotFoundError +from huggingface_hub.hf_api import HfApi +from huggingface_hub.utils import disable_progress_bars, enable_progress_bars +from huggingface_hub.utils._runtime import is_xet_available + + +logger = logging.get_logger(__name__) + + +class UploadCommand(BaseHuggingfaceCLICommand): + @staticmethod + def register_subcommand(parser: _SubParsersAction): + upload_parser = parser.add_parser( + "upload", help="Upload a file or a folder to the Hub. Recommended for single-commit uploads." + ) + upload_parser.add_argument( + "repo_id", type=str, help="The ID of the repo to upload to (e.g. `username/repo-name`)." + ) + upload_parser.add_argument( + "local_path", + nargs="?", + help="Local path to the file or folder to upload. Wildcard patterns are supported. Defaults to current directory.", + ) + upload_parser.add_argument( + "path_in_repo", + nargs="?", + help="Path of the file or folder in the repo. Defaults to the relative path of the file or folder.", + ) + upload_parser.add_argument( + "--repo-type", + choices=["model", "dataset", "space"], + default="model", + help="Type of the repo to upload to (e.g. `dataset`).", + ) + upload_parser.add_argument( + "--revision", + type=str, + help=( + "An optional Git revision to push to. It can be a branch name or a PR reference. If revision does not" + " exist and `--create-pr` is not set, a branch will be automatically created." + ), + ) + upload_parser.add_argument( + "--private", + action="store_true", + help=( + "Whether to create a private repo if repo doesn't exist on the Hub. Ignored if the repo already" + " exists." + ), + ) + upload_parser.add_argument("--include", nargs="*", type=str, help="Glob patterns to match files to upload.") + upload_parser.add_argument( + "--exclude", nargs="*", type=str, help="Glob patterns to exclude from files to upload." + ) + upload_parser.add_argument( + "--delete", + nargs="*", + type=str, + help="Glob patterns for file to be deleted from the repo while committing.", + ) + upload_parser.add_argument( + "--commit-message", type=str, help="The summary / title / first line of the generated commit." + ) + upload_parser.add_argument("--commit-description", type=str, help="The description of the generated commit.") + upload_parser.add_argument( + "--create-pr", action="store_true", help="Whether to upload content as a new Pull Request." + ) + upload_parser.add_argument( + "--every", + type=float, + help="If set, a background job is scheduled to create commits every `every` minutes.", + ) + upload_parser.add_argument( + "--token", type=str, help="A User Access Token generated from https://huggingface.co/settings/tokens" + ) + upload_parser.add_argument( + "--quiet", + action="store_true", + help="If True, progress bars are disabled and only the path to the uploaded files is printed.", + ) + upload_parser.set_defaults(func=UploadCommand) + + def __init__(self, args: Namespace) -> None: + self.repo_id: str = args.repo_id + self.repo_type: Optional[str] = args.repo_type + self.revision: Optional[str] = args.revision + self.private: bool = args.private + + self.include: Optional[List[str]] = args.include + self.exclude: Optional[List[str]] = args.exclude + self.delete: Optional[List[str]] = args.delete + + self.commit_message: Optional[str] = args.commit_message + self.commit_description: Optional[str] = args.commit_description + self.create_pr: bool = args.create_pr + self.api: HfApi = HfApi(token=args.token, library_name="hf") + self.quiet: bool = args.quiet # disable warnings and progress bars + + # Check `--every` is valid + if args.every is not None and args.every <= 0: + raise ValueError(f"`every` must be a positive value (got '{args.every}')") + self.every: Optional[float] = args.every + + # Resolve `local_path` and `path_in_repo` + repo_name: str = args.repo_id.split("/")[-1] # e.g. "Wauplin/my-cool-model" => "my-cool-model" + self.local_path: str + self.path_in_repo: str + + if args.local_path is not None and any(c in args.local_path for c in ["*", "?", "["]): + if args.include is not None: + raise ValueError("Cannot set `--include` when passing a `local_path` containing a wildcard.") + if args.path_in_repo is not None and args.path_in_repo != ".": + raise ValueError("Cannot set `path_in_repo` when passing a `local_path` containing a wildcard.") + self.local_path = "." + self.include = args.local_path + self.path_in_repo = "." + elif args.local_path is None and os.path.isfile(repo_name): + # Implicit case 1: user provided only a repo_id which happen to be a local file as well => upload it with same name + self.local_path = repo_name + self.path_in_repo = repo_name + elif args.local_path is None and os.path.isdir(repo_name): + # Implicit case 2: user provided only a repo_id which happen to be a local folder as well => upload it at root + self.local_path = repo_name + self.path_in_repo = "." + elif args.local_path is None: + # Implicit case 3: user provided only a repo_id that does not match a local file or folder + # => the user must explicitly provide a local_path => raise exception + raise ValueError(f"'{repo_name}' is not a local file or folder. Please set `local_path` explicitly.") + elif args.path_in_repo is None and os.path.isfile(args.local_path): + # Explicit local path to file, no path in repo => upload it at root with same name + self.local_path = args.local_path + self.path_in_repo = os.path.basename(args.local_path) + elif args.path_in_repo is None: + # Explicit local path to folder, no path in repo => upload at root + self.local_path = args.local_path + self.path_in_repo = "." + else: + # Finally, if both paths are explicit + self.local_path = args.local_path + self.path_in_repo = args.path_in_repo + + def run(self) -> None: + if self.quiet: + disable_progress_bars() + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + print(self._upload()) + enable_progress_bars() + else: + logging.set_verbosity_info() + print(self._upload()) + logging.set_verbosity_warning() + + def _upload(self) -> str: + if os.path.isfile(self.local_path): + if self.include is not None and len(self.include) > 0: + warnings.warn("Ignoring `--include` since a single file is uploaded.") + if self.exclude is not None and len(self.exclude) > 0: + warnings.warn("Ignoring `--exclude` since a single file is uploaded.") + if self.delete is not None and len(self.delete) > 0: + warnings.warn("Ignoring `--delete` since a single file is uploaded.") + + if not is_xet_available() and not HF_HUB_ENABLE_HF_TRANSFER: + logger.info( + "Consider using `hf_transfer` for faster uploads. This solution comes with some limitations. See" + " https://huggingface.co/docs/huggingface_hub/hf_transfer for more details." + ) + + # Schedule commits if `every` is set + if self.every is not None: + if os.path.isfile(self.local_path): + # If file => watch entire folder + use allow_patterns + folder_path = os.path.dirname(self.local_path) + path_in_repo = ( + self.path_in_repo[: -len(self.local_path)] # remove filename from path_in_repo + if self.path_in_repo.endswith(self.local_path) + else self.path_in_repo + ) + allow_patterns = [self.local_path] + ignore_patterns = [] + else: + folder_path = self.local_path + path_in_repo = self.path_in_repo + allow_patterns = self.include or [] + ignore_patterns = self.exclude or [] + if self.delete is not None and len(self.delete) > 0: + warnings.warn("Ignoring `--delete` when uploading with scheduled commits.") + + scheduler = CommitScheduler( + folder_path=folder_path, + repo_id=self.repo_id, + repo_type=self.repo_type, + revision=self.revision, + allow_patterns=allow_patterns, + ignore_patterns=ignore_patterns, + path_in_repo=path_in_repo, + private=self.private, + every=self.every, + hf_api=self.api, + ) + print(f"Scheduling commits every {self.every} minutes to {scheduler.repo_id}.") + try: # Block main thread until KeyboardInterrupt + while True: + time.sleep(100) + except KeyboardInterrupt: + scheduler.stop() + return "Stopped scheduled commits." + + # Otherwise, create repo and proceed with the upload + if not os.path.isfile(self.local_path) and not os.path.isdir(self.local_path): + raise FileNotFoundError(f"No such file or directory: '{self.local_path}'.") + repo_id = self.api.create_repo( + repo_id=self.repo_id, + repo_type=self.repo_type, + exist_ok=True, + private=self.private, + space_sdk="gradio" if self.repo_type == "space" else None, + # ^ We don't want it to fail when uploading to a Space => let's set Gradio by default. + # ^ I'd rather not add CLI args to set it explicitly as we already have `hf repo create` for that. + ).repo_id + + # Check if branch already exists and if not, create it + if self.revision is not None and not self.create_pr: + try: + self.api.repo_info(repo_id=repo_id, repo_type=self.repo_type, revision=self.revision) + except RevisionNotFoundError: + logger.info(f"Branch '{self.revision}' not found. Creating it...") + self.api.create_branch(repo_id=repo_id, repo_type=self.repo_type, branch=self.revision, exist_ok=True) + # ^ `exist_ok=True` to avoid race concurrency issues + + # File-based upload + if os.path.isfile(self.local_path): + return self.api.upload_file( + path_or_fileobj=self.local_path, + path_in_repo=self.path_in_repo, + repo_id=repo_id, + repo_type=self.repo_type, + revision=self.revision, + commit_message=self.commit_message, + commit_description=self.commit_description, + create_pr=self.create_pr, + ) + + # Folder-based upload + else: + return self.api.upload_folder( + folder_path=self.local_path, + path_in_repo=self.path_in_repo, + repo_id=repo_id, + repo_type=self.repo_type, + revision=self.revision, + commit_message=self.commit_message, + commit_description=self.commit_description, + create_pr=self.create_pr, + allow_patterns=self.include, + ignore_patterns=self.exclude, + delete_patterns=self.delete, + ) diff --git a/src/huggingface_hub/cli/upload_large_folder.py b/src/huggingface_hub/cli/upload_large_folder.py new file mode 100644 index 0000000000..618cd21b52 --- /dev/null +++ b/src/huggingface_hub/cli/upload_large_folder.py @@ -0,0 +1,132 @@ +# coding=utf-8 +# Copyright 2023-present, the HuggingFace Inc. team. +# +# 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 command to upload a large folder with the CLI.""" + +import os +from argparse import Namespace, _SubParsersAction +from typing import List, Optional + +from huggingface_hub import logging +from huggingface_hub.commands import BaseHuggingfaceCLICommand +from huggingface_hub.hf_api import HfApi +from huggingface_hub.utils import disable_progress_bars + +from ._cli_utils import ANSI + + +logger = logging.get_logger(__name__) + + +class UploadLargeFolderCommand(BaseHuggingfaceCLICommand): + @staticmethod + def register_subcommand(parser: _SubParsersAction): + subparser = parser.add_parser( + "upload-large-folder", + help="Upload a large folder to the Hub. Recommended for resumable uploads.", + ) + subparser.add_argument( + "repo_id", type=str, help="The ID of the repo to upload to (e.g. `username/repo-name`)." + ) + subparser.add_argument("local_path", type=str, help="Local path to the file or folder to upload.") + subparser.add_argument( + "--repo-type", + choices=["model", "dataset", "space"], + help="Type of the repo to upload to (e.g. `dataset`).", + ) + subparser.add_argument( + "--revision", + type=str, + help=("An optional Git revision to push to. It can be a branch name or a PR reference."), + ) + subparser.add_argument( + "--private", + action="store_true", + help=( + "Whether to create a private repo if repo doesn't exist on the Hub. Ignored if the repo already exists." + ), + ) + subparser.add_argument("--include", nargs="*", type=str, help="Glob patterns to match files to upload.") + subparser.add_argument("--exclude", nargs="*", type=str, help="Glob patterns to exclude from files to upload.") + subparser.add_argument( + "--token", type=str, help="A User Access Token generated from https://huggingface.co/settings/tokens" + ) + subparser.add_argument( + "--num-workers", type=int, help="Number of workers to use to hash, upload and commit files." + ) + subparser.add_argument("--no-report", action="store_true", help="Whether to disable regular status report.") + subparser.add_argument("--no-bars", action="store_true", help="Whether to disable progress bars.") + subparser.set_defaults(func=UploadLargeFolderCommand) + + def __init__(self, args: Namespace) -> None: + self.repo_id: str = args.repo_id + self.local_path: str = args.local_path + self.repo_type: str = args.repo_type + self.revision: Optional[str] = args.revision + self.private: bool = args.private + + self.include: Optional[List[str]] = args.include + self.exclude: Optional[List[str]] = args.exclude + + self.api: HfApi = HfApi(token=args.token, library_name="hf") + + self.num_workers: Optional[int] = args.num_workers + self.no_report: bool = args.no_report + self.no_bars: bool = args.no_bars + + if not os.path.isdir(self.local_path): + raise ValueError("Large upload is only supported for folders.") + + def run(self) -> None: + logging.set_verbosity_info() + + print( + ANSI.yellow( + "You are about to upload a large folder to the Hub using `hf upload-large-folder`. " + "This is a new feature so feedback is very welcome!\n" + "\n" + "A few things to keep in mind:\n" + " - Repository limits still apply: https://huggingface.co/docs/hub/repositories-recommendations\n" + " - Do not start several processes in parallel.\n" + " - You can interrupt and resume the process at any time. " + "The script will pick up where it left off except for partially uploaded files that would have to be entirely reuploaded.\n" + " - Do not upload the same folder to several repositories. If you need to do so, you must delete the `./.cache/huggingface/` folder first.\n" + "\n" + f"Some temporary metadata will be stored under `{self.local_path}/.cache/huggingface`.\n" + " - You must not modify those files manually.\n" + " - You must not delete the `./.cache/huggingface/` folder while a process is running.\n" + " - You can delete the `./.cache/huggingface/` folder to reinitialize the upload state when process is not running. Files will have to be hashed and preuploaded again, except for already committed files.\n" + "\n" + "If the process output is too verbose, you can disable the progress bars with `--no-bars`. " + "You can also entirely disable the status report with `--no-report`.\n" + "\n" + "For more details, run `hf upload-large-folder --help` or check the documentation at " + "https://huggingface.co/docs/huggingface_hub/guides/upload#upload-a-large-folder." + ) + ) + + if self.no_bars: + disable_progress_bars() + + self.api.upload_large_folder( + repo_id=self.repo_id, + folder_path=self.local_path, + repo_type=self.repo_type, + revision=self.revision, + private=self.private, + allow_patterns=self.include, + ignore_patterns=self.exclude, + num_workers=self.num_workers, + print_report=not self.no_report, + ) diff --git a/src/huggingface_hub/commands/_cli_utils.py b/src/huggingface_hub/commands/_cli_utils.py index bd56ad6896..bf4a1c0373 100644 --- a/src/huggingface_hub/commands/_cli_utils.py +++ b/src/huggingface_hub/commands/_cli_utils.py @@ -67,3 +67,8 @@ def tabulate(rows: List[List[Union[str, int]]], headers: List[str]) -> str: for row in rows: lines.append(row_format.format(*row)) return "\n".join(lines) + + +def show_deprecation_warning(old_command: str, new_command: str): + """Show a yellow warning about deprecated CLI command.""" + print(ANSI.yellow(f"⚠️ Warning: '{old_command}' is deprecated. Use '{new_command}' instead.")) diff --git a/src/huggingface_hub/commands/delete_cache.py b/src/huggingface_hub/commands/delete_cache.py index fc9eecf469..25fffd07ee 100644 --- a/src/huggingface_hub/commands/delete_cache.py +++ b/src/huggingface_hub/commands/delete_cache.py @@ -64,7 +64,7 @@ from ..utils import CachedRepoInfo, CachedRevisionInfo, HFCacheInfo, scan_cache_dir from . import BaseHuggingfaceCLICommand -from ._cli_utils import ANSI +from ._cli_utils import ANSI, show_deprecation_warning try: @@ -144,6 +144,8 @@ def __init__(self, args: Namespace) -> None: def run(self): """Run `delete-cache` command with or without TUI.""" + show_deprecation_warning("huggingface-cli delete-cache", "hf cache delete") + # Scan cache directory hf_cache_info = scan_cache_dir(self.cache_dir) diff --git a/src/huggingface_hub/commands/download.py b/src/huggingface_hub/commands/download.py index 10e22c3d1e..0dd2c1070e 100644 --- a/src/huggingface_hub/commands/download.py +++ b/src/huggingface_hub/commands/download.py @@ -46,6 +46,8 @@ from huggingface_hub.file_download import hf_hub_download from huggingface_hub.utils import disable_progress_bars, enable_progress_bars +from ._cli_utils import show_deprecation_warning + logger = logging.get_logger(__name__) @@ -142,6 +144,8 @@ def __init__(self, args: Namespace) -> None: ) def run(self) -> None: + show_deprecation_warning("huggingface-cli download", "hf download") + if self.quiet: disable_progress_bars() with warnings.catch_warnings(): diff --git a/src/huggingface_hub/commands/env.py b/src/huggingface_hub/commands/env.py index 23f2828bbf..ad674738b2 100644 --- a/src/huggingface_hub/commands/env.py +++ b/src/huggingface_hub/commands/env.py @@ -21,6 +21,7 @@ from ..utils import dump_environment_info from . import BaseHuggingfaceCLICommand +from ._cli_utils import show_deprecation_warning class EnvironmentCommand(BaseHuggingfaceCLICommand): @@ -33,4 +34,6 @@ def register_subcommand(parser: _SubParsersAction): env_parser.set_defaults(func=EnvironmentCommand) def run(self) -> None: + show_deprecation_warning("huggingface-cli env", "hf env") + dump_environment_info() diff --git a/src/huggingface_hub/commands/huggingface_cli.py b/src/huggingface_hub/commands/huggingface_cli.py index 4e30f305c2..697c85d1e3 100644 --- a/src/huggingface_hub/commands/huggingface_cli.py +++ b/src/huggingface_hub/commands/huggingface_cli.py @@ -14,6 +14,7 @@ from argparse import ArgumentParser +from huggingface_hub.commands._cli_utils import show_deprecation_warning from huggingface_hub.commands.delete_cache import DeleteCacheCommand from huggingface_hub.commands.download import DownloadCommand from huggingface_hub.commands.env import EnvironmentCommand @@ -51,6 +52,7 @@ def main(): # Let's go args = parser.parse_args() if not hasattr(args, "func"): + show_deprecation_warning("huggingface-cli", "hf") parser.print_help() exit(1) diff --git a/src/huggingface_hub/commands/repo.py b/src/huggingface_hub/commands/repo.py index 5d12778040..fe75349d67 100644 --- a/src/huggingface_hub/commands/repo.py +++ b/src/huggingface_hub/commands/repo.py @@ -31,6 +31,8 @@ from huggingface_hub.hf_api import HfApi from huggingface_hub.utils import logging +from ._cli_utils import show_deprecation_warning + logger = logging.get_logger(__name__) @@ -128,6 +130,8 @@ def __init__(self, args: argparse.Namespace): self._api = HfApi() def run(self): + show_deprecation_warning("huggingface-cli repo", "hf repo") + if self.organization is not None: if "/" in self.repo_id: print(ANSI.red("You cannot pass both --organization and a repo_id with a namespace.")) diff --git a/src/huggingface_hub/commands/repo_files.py b/src/huggingface_hub/commands/repo_files.py index f15bbed04f..da9685315e 100644 --- a/src/huggingface_hub/commands/repo_files.py +++ b/src/huggingface_hub/commands/repo_files.py @@ -41,6 +41,8 @@ from huggingface_hub.commands import BaseHuggingfaceCLICommand from huggingface_hub.hf_api import HfApi +from ._cli_utils import show_deprecation_warning + logger = logging.get_logger(__name__) @@ -59,6 +61,8 @@ def __init__(self, args) -> None: self.token: Optional[str] = args.token def run(self) -> None: + show_deprecation_warning("huggingface-cli repo-files", "hf repo-files") + logging.set_verbosity_info() url = self.api.delete_files( delete_patterns=self.patterns, diff --git a/src/huggingface_hub/commands/scan_cache.py b/src/huggingface_hub/commands/scan_cache.py index 799b9ba552..45662fb973 100644 --- a/src/huggingface_hub/commands/scan_cache.py +++ b/src/huggingface_hub/commands/scan_cache.py @@ -27,7 +27,7 @@ from ..utils import CacheNotFound, HFCacheInfo, scan_cache_dir from . import BaseHuggingfaceCLICommand -from ._cli_utils import ANSI, tabulate +from ._cli_utils import ANSI, show_deprecation_warning, tabulate class ScanCacheCommand(BaseHuggingfaceCLICommand): @@ -55,6 +55,8 @@ def __init__(self, args: Namespace) -> None: self.cache_dir: Optional[str] = args.dir def run(self): + show_deprecation_warning("huggingface-cli scan-cache", "hf cache scan") + try: t0 = time.time() hf_cache_info = scan_cache_dir(self.cache_dir) diff --git a/src/huggingface_hub/commands/tag.py b/src/huggingface_hub/commands/tag.py index c3beab90a0..405d407f81 100644 --- a/src/huggingface_hub/commands/tag.py +++ b/src/huggingface_hub/commands/tag.py @@ -41,7 +41,7 @@ from huggingface_hub.hf_api import HfApi from ..errors import HfHubHTTPError, RepositoryNotFoundError, RevisionNotFoundError -from ._cli_utils import ANSI +from ._cli_utils import ANSI, show_deprecation_warning class TagCommands(BaseHuggingfaceCLICommand): @@ -71,6 +71,8 @@ def register_subcommand(parser: _SubParsersAction): def handle_commands(args: Namespace): + show_deprecation_warning("huggingface-cli tag", "hf repo tag") + if args.list: return TagListCommand(args) elif args.delete: diff --git a/src/huggingface_hub/commands/upload.py b/src/huggingface_hub/commands/upload.py index 3d4caebd5f..c778555cda 100644 --- a/src/huggingface_hub/commands/upload.py +++ b/src/huggingface_hub/commands/upload.py @@ -61,6 +61,8 @@ from huggingface_hub.utils import disable_progress_bars, enable_progress_bars from huggingface_hub.utils._runtime import is_xet_available +from ._cli_utils import show_deprecation_warning + logger = logging.get_logger(__name__) @@ -196,6 +198,8 @@ def __init__(self, args: Namespace) -> None: self.path_in_repo = args.path_in_repo def run(self) -> None: + show_deprecation_warning("huggingface-cli upload", "hf upload") + if self.quiet: disable_progress_bars() with warnings.catch_warnings(): diff --git a/src/huggingface_hub/commands/upload_large_folder.py b/src/huggingface_hub/commands/upload_large_folder.py index 61c12a9f62..3105ba3f57 100644 --- a/src/huggingface_hub/commands/upload_large_folder.py +++ b/src/huggingface_hub/commands/upload_large_folder.py @@ -23,7 +23,7 @@ from huggingface_hub.hf_api import HfApi from huggingface_hub.utils import disable_progress_bars -from ._cli_utils import ANSI +from ._cli_utils import ANSI, show_deprecation_warning logger = logging.get_logger(__name__) @@ -86,6 +86,8 @@ def __init__(self, args: Namespace) -> None: raise ValueError("Large upload is only supported for folders.") def run(self) -> None: + show_deprecation_warning("huggingface-cli upload-large-folder", "hf upload-large-folder") + logging.set_verbosity_info() print( diff --git a/src/huggingface_hub/commands/user.py b/src/huggingface_hub/commands/user.py index c8b943f761..3f4da0f45d 100644 --- a/src/huggingface_hub/commands/user.py +++ b/src/huggingface_hub/commands/user.py @@ -41,7 +41,7 @@ from .._login import auth_list, auth_switch, login, logout from ..utils import get_stored_tokens, get_token, logging -from ._cli_utils import ANSI +from ._cli_utils import ANSI, show_deprecation_warning logger = logging.get_logger(__name__) @@ -107,6 +107,8 @@ def __init__(self, args): class LoginCommand(BaseUserCommand): def run(self): + show_deprecation_warning("huggingface-cli login", "hf auth login") + logging.set_verbosity_info() login( token=self.args.token, @@ -116,12 +118,16 @@ def run(self): class LogoutCommand(BaseUserCommand): def run(self): + show_deprecation_warning("huggingface-cli logout", "hf auth logout") + logging.set_verbosity_info() logout(token_name=self.args.token_name) class AuthSwitchCommand(BaseUserCommand): def run(self): + show_deprecation_warning("huggingface-cli auth switch", "hf auth switch") + logging.set_verbosity_info() token_name = self.args.token_name if token_name is None: @@ -173,12 +179,16 @@ def _select_token_name_tui(self, token_names: List[str]) -> Optional[str]: class AuthListCommand(BaseUserCommand): def run(self): + show_deprecation_warning("huggingface-cli auth list", "hf auth list") + logging.set_verbosity_info() auth_list() class WhoamiCommand(BaseUserCommand): def run(self): + show_deprecation_warning("huggingface-cli whoami", "hf auth whoami") + token = get_token() if token is None: print("Not logged in") diff --git a/src/huggingface_hub/commands/version.py b/src/huggingface_hub/commands/version.py index f7e866b76f..10d341bcdb 100644 --- a/src/huggingface_hub/commands/version.py +++ b/src/huggingface_hub/commands/version.py @@ -22,6 +22,7 @@ from huggingface_hub import __version__ from . import BaseHuggingfaceCLICommand +from ._cli_utils import show_deprecation_warning class VersionCommand(BaseHuggingfaceCLICommand): @@ -34,4 +35,6 @@ def register_subcommand(parser: _SubParsersAction): version_parser.set_defaults(func=VersionCommand) def run(self) -> None: + show_deprecation_warning("huggingface-cli version", "hf version") + print(f"huggingface_hub version: {__version__}") From 5699a5eb2792941460616ffad5eb92467a033888 Mon Sep 17 00:00:00 2001 From: Lucain Pouget Date: Tue, 22 Jul 2025 14:35:45 +0200 Subject: [PATCH 2/7] fix test_command_delete_cache.py --- tests/test_command_delete_cache.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/tests/test_command_delete_cache.py b/tests/test_command_delete_cache.py index 482c2bb8e3..1d82b50ad8 100644 --- a/tests/test_command_delete_cache.py +++ b/tests/test_command_delete_cache.py @@ -352,10 +352,7 @@ def test_run_and_delete_with_tui( strategy_mock.execute.assert_called_once_with() # Check output - self.assertEqual( - output.getvalue(), - "Start deletion.\nDone. Deleted 0 repo(s) and 0 revision(s) for a total of 5.1M.\n", - ) + assert "Start deletion.\nDone. Deleted 0 repo(s) and 0 revision(s) for a total of 5.1M.\n" in output.getvalue() def test_run_nothing_selected_with_tui(self, mock__manual_review_tui: Mock) -> None: """Test command run but nothing is selected in manual review.""" @@ -368,7 +365,7 @@ def test_run_nothing_selected_with_tui(self, mock__manual_review_tui: Mock) -> N self.command.run() # Check output - self.assertEqual(output.getvalue(), "Deletion is cancelled. Do nothing.\n") + assert "Deletion is cancelled. Do nothing.\n" in output.getvalue() def test_run_stuff_selected_but_cancel_item_as_well_with_tui(self, mock__manual_review_tui: Mock) -> None: """Test command run when some are selected but "cancel item" as well.""" @@ -385,7 +382,7 @@ def test_run_stuff_selected_but_cancel_item_as_well_with_tui(self, mock__manual_ self.command.run() # Check output - self.assertEqual(output.getvalue(), "Deletion is cancelled. Do nothing.\n") + assert "Deletion is cancelled. Do nothing.\n" in output.getvalue() def test_run_and_delete_no_tui( self, @@ -423,10 +420,7 @@ def test_run_and_delete_no_tui( strategy_mock.execute.assert_called_once_with() # Check output - self.assertEqual( - output.getvalue(), - "Start deletion.\nDone. Deleted 0 repo(s) and 0 revision(s) for a total of 5.1M.\n", - ) + assert "Start deletion.\nDone. Deleted 0 repo(s) and 0 revision(s) for a total of 5.1M.\n" in output.getvalue() def test_run_with_sorting(self): """Test command run with sorting enabled.""" From 1ba33a6f89b41419dd112ea0aaf756732d2dedff Mon Sep 17 00:00:00 2001 From: Lucain Pouget Date: Tue, 22 Jul 2025 14:43:10 +0200 Subject: [PATCH 3/7] fix tests/test_utils_cache.py --- tests/test_utils_cache.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/tests/test_utils_cache.py b/tests/test_utils_cache.py index 6b16f989a7..2609867abd 100644 --- a/tests/test_utils_cache.py +++ b/tests/test_utils_cache.py @@ -3,6 +3,7 @@ import time import unittest from pathlib import Path +from typing import Any, List from unittest.mock import Mock import pytest @@ -260,9 +261,9 @@ def test_cli_scan_cache_quiet(self) -> None: Done in 0.0s. Scanned 2 repo(s) for a total of \x1b[1m\x1b[31m3.8K\x1b[0m. """ - self.assertListEqual( - output.getvalue().replace("-", "").split(), + assert is_sublist( expected_output.replace("-", "").split(), + output.getvalue().replace("-", "").split(), ) @xfail_on_windows("Size on disk and paths differ on Windows. Not useful to test.") @@ -289,9 +290,9 @@ def test_cli_scan_cache_verbose(self) -> None: Done in 0.0s. Scanned 2 repo(s) for a total of \x1b[1m\x1b[31m3.8K\x1b[0m. """ - self.assertListEqual( - output.getvalue().replace("-", "").split(), + assert is_sublist( expected_output.replace("-", "").split(), + output.getvalue().replace("-", "").split(), ) def test_cli_scan_missing_cache(self) -> None: @@ -313,7 +314,7 @@ def test_cli_scan_missing_cache(self) -> None: Cache directory not found: {Path(tmp_dir).resolve()} """ - self.assertListEqual(output.getvalue().split(), expected_output.split()) + assert is_sublist(expected_output.split(), output.getvalue().split()) @pytest.mark.usefixtures("fx_cache_dir") @@ -861,3 +862,8 @@ def test_format_timesince(self) -> None: expected, msg=f"Wrong formatting for {ts} == '{expected}'", ) + + +def is_sublist(sub: List[Any], full: List[Any]) -> bool: + it = iter(full) + return all(item in it for item in sub) From 3330251c83a3b72a13d42c6dd0fd384698e3bf33 Mon Sep 17 00:00:00 2001 From: Lucain Date: Wed, 23 Jul 2025 10:15:09 +0200 Subject: [PATCH 4/7] Update src/huggingface_hub/cli/__init__.py Co-authored-by: Lysandre Debut --- src/huggingface_hub/cli/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/huggingface_hub/cli/__init__.py b/src/huggingface_hub/cli/__init__.py index 53927cbfb1..7a1a8d793b 100644 --- a/src/huggingface_hub/cli/__init__.py +++ b/src/huggingface_hub/cli/__init__.py @@ -1,4 +1,4 @@ -# Copyright 202 The HuggingFace Team. All rights reserved. +# 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. From 6d820c467510cd5c740ce54df350e76eb38e15bc Mon Sep 17 00:00:00 2001 From: Lucain Pouget Date: Wed, 23 Jul 2025 11:13:48 +0200 Subject: [PATCH 5/7] Document hf instead of huggingface-cli everywhere --- .github/ISSUE_TEMPLATE/bug-report.yml | 2 +- CONTRIBUTING.md | 2 +- README.md | 4 +- docs/source/cn/quick-start.md | 6 +- docs/source/de/guides/download.md | 20 +- docs/source/de/guides/manage-cache.md | 24 +- docs/source/de/guides/model-cards.md | 2 +- docs/source/de/guides/upload.md | 6 +- docs/source/de/quick-start.md | 6 +- docs/source/en/guides/cli.md | 170 ++++---- docs/source/en/guides/download.md | 8 +- docs/source/en/guides/manage-cache.md | 27 +- docs/source/en/guides/model-cards.md | 2 +- docs/source/en/guides/upload.md | 16 +- .../environment_variables.md | 2 +- docs/source/en/package_reference/hf_api.md | 2 +- docs/source/en/quick-start.md | 8 +- docs/source/fr/quick-start.md | 6 +- docs/source/hi/quick-start.md | 8 +- docs/source/ko/guides/cli.md | 137 ++++--- docs/source/ko/guides/download.md | 20 +- docs/source/ko/guides/manage-cache.md | 20 +- docs/source/ko/guides/model-cards.md | 2 +- docs/source/ko/guides/upload.md | 12 +- .../environment_variables.md | 2 +- docs/source/ko/package_reference/hf_api.md | 2 +- docs/source/ko/quick-start.md | 6 +- i18n/README_cn.md | 4 +- i18n/README_de.md | 4 +- i18n/README_hi.md | 4 +- i18n/README_ko.md | 4 +- src/huggingface_hub/README.md | 29 +- src/huggingface_hub/_login.py | 10 +- src/huggingface_hub/_oauth.py | 2 +- src/huggingface_hub/cli/repo.py | 14 - src/huggingface_hub/hf_api.py | 6 +- src/huggingface_hub/hub_mixin.py | 6 +- .../inference/_providers/_common.py | 2 +- src/huggingface_hub/keras_mixin.py | 4 +- src/huggingface_hub/repository.py | 4 +- src/huggingface_hub/utils/_auth.py | 2 +- src/huggingface_hub/utils/_cache_manager.py | 4 +- src/huggingface_hub/utils/_headers.py | 2 +- tests/test_cli.py | 364 +++++++++--------- tests/test_hf_api.py | 6 +- 45 files changed, 485 insertions(+), 508 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index d41710ba61..a273c8b387 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -33,7 +33,7 @@ body: description: | Please dump your environment info by running the following command and copy-paste the result here: ```txt - huggingface-cli env + hf env ``` If you are working in a notebook, please run it in a code cell: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d393ec2d98..4d72fb10b9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -59,7 +59,7 @@ Did not find it? :( So we can act quickly on it, please follow these steps: - A short, self-contained, code snippet that allows us to reproduce the bug in less than 30s; - Provide the _full_ traceback if an exception is raised by copying the text from your terminal in the issue description. -- Include information about your local setup. You can dump this information by running `huggingface-cli env` in your terminal; +- Include information about your local setup. You can dump this information by running `hf env` in your terminal; ### Do you want a new feature? diff --git a/README.md b/README.md index f3816392b7..f86234d3a3 100644 --- a/README.md +++ b/README.md @@ -97,9 +97,9 @@ Files will be downloaded in a local cache folder. More details in [this guide](h The Hugging Face Hub uses tokens to authenticate applications (see [docs](https://huggingface.co/docs/hub/security-tokens)). To log in your machine, run the following CLI: ```bash -huggingface-cli login +hf auth login # or using an environment variable -huggingface-cli login --token $HUGGINGFACE_TOKEN +hf auth login --token $HUGGINGFACE_TOKEN ``` ### Create a repository diff --git a/docs/source/cn/quick-start.md b/docs/source/cn/quick-start.md index 4a4a809a79..d78671da35 100644 --- a/docs/source/cn/quick-start.md +++ b/docs/source/cn/quick-start.md @@ -49,9 +49,9 @@ filename: 要下载的文件名,这里是 "config.json" 运行以下代码,这将使用您的用户访问令牌登录到Hugging Face模型库 ```bash -huggingface-cli login +hf auth login -huggingface-cli login --token $HUGGINGFACE_TOKEN +hf auth login --token $HUGGINGFACE_TOKEN ``` 或者,你可以在笔记本电脑或脚本中使用 [`login`] 来进行程序化登录,请运行以下代码: @@ -63,7 +63,7 @@ huggingface-cli login --token $HUGGINGFACE_TOKEN 您还可以直接将令牌传递给 [`login`],如下所示:`login(token="hf_xxx")`。这将使用您的用户访问令牌登录到 Hugging Face 模型库,而无需您输入任何内容。但是,如果您这样做,请在共享源代码时要小心。最好从安全保管库中加载令牌,而不是在代码库/笔记本中显式保存它 -您一次只能登录一个帐户。如果您使用另一个帐户登录您的机器,您将会从之前的帐户注销。请确保使用命令 `huggingface-cli whoami`来检查您当前使用的是哪个帐户。如果您想在同一个脚本中处理多个帐户,您可以在调用每个方法时提供您的令牌。这对于您不想在您的机器上存储任何令牌也很有用 +您一次只能登录一个帐户。如果您使用另一个帐户登录您的机器,您将会从之前的帐户注销。请确保使用命令 `hf auth whoami`来检查您当前使用的是哪个帐户。如果您想在同一个脚本中处理多个帐户,您可以在调用每个方法时提供您的令牌。这对于您不想在您的机器上存储任何令牌也很有用 diff --git a/docs/source/de/guides/download.md b/docs/source/de/guides/download.md index ec195d4035..07324ca174 100644 --- a/docs/source/de/guides/download.md +++ b/docs/source/de/guides/download.md @@ -136,24 +136,24 @@ Hier ist eine Tabelle, die die verschiedenen Optionen zusammenfasst, um Ihnen zu ## Herunterladen mit dem CLI -Sie können den `huggingface-cli download`-Befehl im Terminal verwenden, um Dateien direkt aus dem Hub herunterzuladen. Intern verwendet es die gleichen [`hf_hub_download`] und [`snapshot_download`] Helfer, die oben beschrieben wurden, und gibt den zurückgegebenen Pfad im Terminal aus: +Sie können den `hf download`-Befehl im Terminal verwenden, um Dateien direkt aus dem Hub herunterzuladen. Intern verwendet es die gleichen [`hf_hub_download`] und [`snapshot_download`] Helfer, die oben beschrieben wurden, und gibt den zurückgegebenen Pfad im Terminal aus: ```bash ->>> huggingface-cli download gpt2 config.json +>>> hf download gpt2 config.json /home/wauplin/.cache/huggingface/hub/models--gpt2/snapshots/11c5a3d5811f50298f278a704980280950aedb10/config.json ``` -Standardmäßig wird das lokal gespeicherte Token (mit `huggingface-cli login`) verwendet. Wenn Sie sich ausdrücklich authentifizieren möchten, verwenden Sie die `--token` Option: +Standardmäßig wird das lokal gespeicherte Token (mit `hf auth login`) verwendet. Wenn Sie sich ausdrücklich authentifizieren möchten, verwenden Sie die `--token` Option: ```bash ->>> huggingface-cli download gpt2 config.json --token=hf_**** +>>> hf download gpt2 config.json --token=hf_**** /home/wauplin/.cache/huggingface/hub/models--gpt2/snapshots/11c5a3d5811f50298f278a704980280950aedb10/config.json ``` Sie können mehrere Dateien gleichzeitig herunterladen, wobei eine Fortschrittsleiste angezeigt wird und der Snapshot-Pfad zurückgegeben wird, in dem sich die Dateien befinden: ```bash ->>> huggingface-cli download gpt2 config.json model.safetensors +>>> hf download gpt2 config.json model.safetensors Fetching 2 files: 100%|████████████████████████████████████████████| 2/2 [00:00<00:00, 23831.27it/s] /home/wauplin/.cache/huggingface/hub/models--gpt2/snapshots/11c5a3d5811f50298f278a704980280950aedb10 ``` @@ -161,28 +161,28 @@ Fetching 2 files: 100%|███████████████████ Wenn Sie die Fortschrittsleisten und mögliche Warnungen stummschalten möchten, verwenden Sie die Option `--quiet`. Dies kann nützlich sein, wenn Sie die Ausgabe an einen anderen Befehl in einem Skript weitergeben möchten. ```bash ->>> huggingface-cli download gpt2 config.json model.safetensors +>>> hf download gpt2 config.json model.safetensors /home/wauplin/.cache/huggingface/hub/models--gpt2/snapshots/11c5a3d5811f50298f278a704980280950aedb10 ``` Standardmäßig werden Dateien im Cache-Verzeichnis heruntergeladen, das durch die Umgebungsvariable `HF_HOME` definiert ist (oder `~/.cache/huggingface/hub`, wenn nicht angegeben). Sie können dies mit der Option `--cache-dir` überschreiben: ```bash ->>> huggingface-cli download gpt2 config.json --cache-dir=./cache +>>> hf download gpt2 config.json --cache-dir=./cache ./cache/models--gpt2/snapshots/11c5a3d5811f50298f278a704980280950aedb10/config.json ``` Wenn Sie Dateien in einen lokalen Ordner herunterladen möchten, ohne die Cache-Verzeichnisstruktur, können Sie `--local-dir` verwenden. Das Herunterladen in einen lokalen Ordner hat seine Einschränkungen, die in dieser [Tabelle](https://huggingface.co/docs/huggingface_hub/guides/download#download-files-to-local-folder) aufgeführt sind. ```bash ->>> huggingface-cli download gpt2 config.json --local-dir=./models/gpt2 +>>> hf download gpt2 config.json --local-dir=./models/gpt2 ./models/gpt2/config.json ``` Es gibt weitere Argumente, die Sie angeben können, um aus verschiedenen Repo-Typen oder Revisionen herunterzuladen und Dateien zum Herunterladen mit Glob-Mustern ein- oder auszuschließen: ```bash ->>> huggingface-cli download bigcode/the-stack --repo-type=dataset --revision=v1.2 --include="data/python/*" --exclu +>>> hf download bigcode/the-stack --repo-type=dataset --revision=v1.2 --include="data/python/*" --exclu de="*.json" --exclude="*.zip" Fetching 206 files: 100%|████████████████████████████████████████████| 206/206 [02:31<2:31, ?it/s] /home/wauplin/.cache/huggingface/hub/datasets--bigcode--the-stack/snapshots/9ca8fa6acdbc8ce920a0cb58adcdafc495818ae7 @@ -191,5 +191,5 @@ Fetching 206 files: 100%|█████████████████ Für eine vollständige Liste der Argumente führen Sie bitte den folgenden Befehl aus: ```bash -huggingface-cli download --help +hf download --help ``` diff --git a/docs/source/de/guides/manage-cache.md b/docs/source/de/guides/manage-cache.md index 779c34b683..4c1945fb76 100644 --- a/docs/source/de/guides/manage-cache.md +++ b/docs/source/de/guides/manage-cache.md @@ -238,12 +238,12 @@ Derzeit werden zwischengespeicherte Dateien nie aus Ihrem lokalen Verzeichnis ge Wenn Sie eine neue Revision eines Zweiges herunterladen, werden vorherige Dateien aufbewahrt, falls Sie sie wieder benötigen. Daher kann es nützlich sein, Ihr Cache-Verzeichnis zu scannen, um zu erfahren, welche Repos und Revisionen den meisten Speicherplatz beanspruchen. -`huggingface_hub` bietet einen Helfer dafür, der über `huggingface-cli` oder in einem Python-Skript verwendet werden kann. +`huggingface_hub` bietet einen Helfer dafür, der über `hf` oder in einem Python-Skript verwendet werden kann. ### Cache vom Terminal aus scannen -Die einfachste Möglichkeit, Ihr HF-Cache-System zu scannen, besteht darin, den Befehl `scan-cache` -aus dem `huggingface-cli`-Tool zu verwenden. Dieser Befehl scannt den Cache und gibt einen Bericht +Die einfachste Möglichkeit, Ihr HF-Cache-System zu scannen, besteht darin, den Befehl `cache scan` +aus dem `hf`-Tool zu verwenden. Dieser Befehl scannt den Cache und gibt einen Bericht mit Informationen wie Repo-ID, Repo-Typ, Speicherverbrauch, Referenzen und vollständigen lokalen Pfad aus. Im folgenden Ausschnitt wird ein Scan-Bericht in einem Ordner angezeigt, in dem 4 Modelle und 2 Datensätze @@ -251,7 +251,7 @@ gecached sind. ```text -➜ huggingface-cli scan-cache +➜ hf cache scan REPO ID REPO TYPE SIZE ON DISK NB FILES LAST_ACCESSED LAST_MODIFIED REFS LOCAL PATH --------------------------- --------- ------------ -------- ------------- ------------- ------------------- ------------------------------------------------------------------------- glue dataset 116.3K 15 4 days ago 4 days ago 2.4.0, main, 1.17.0 /home/wauplin/.cache/huggingface/hub/datasets--glue @@ -274,7 +274,7 @@ Zum Beispiel hat hier `bert-base-cased` 2 Revisionen von 1,4G und 1,5G, aber der gesamte Festplattenspeicher beträgt nur 1,9G. ```text -➜ huggingface-cli scan-cache -v +➜ hf cache scan -v REPO ID REPO TYPE REVISION SIZE ON DISK NB FILES LAST_MODIFIED REFS LOCAL PATH --------------------------- --------- ---------------------------------------- ------------ -------- ------------- ----------- ---------------------------------------------------------------------------------------------------------------------------- glue dataset 9338f7b671827df886678df2bdd7cc7b4f36dffd 97.7K 14 4 days ago main, 2.4.0 /home/wauplin/.cache/huggingface/hub/datasets--glue/snapshots/9338f7b671827df886678df2bdd7cc7b4f36dffd @@ -300,7 +300,7 @@ um die Einträge zu filtern. Hier ein Beispiel, um nur Revisionen vom Modell "t5 auf einem Unix-basierten Gerät zu filtern. ```text -➜ eval "huggingface-cli scan-cache -v" | grep "t5-small" +➜ eval "hf cache scan -v" | grep "t5-small" t5-small model 98ffebbb27340ec1b1abd7c45da12c253ee1882a 726.2M 6 1 week ago refs/pr/1 /home/wauplin/.cache/huggingface/hub/models--t5-small/snapshots/98ffebbb27340ec1b1abd7c45da12c253ee1882a t5-small model d0a119eedb3718e34c648e594394474cf95e0617 485.8M 6 4 weeks ago /home/wauplin/.cache/huggingface/hub/models--t5-small/snapshots/d0a119eedb3718e34c648e594394474cf95e0617 t5-small model d78aea13fa7ecd06c29e3e46195d6341255065d5 970.7M 9 1 week ago main /home/wauplin/.cache/huggingface/hub/models--t5-small/snapshots/d78aea13fa7ecd06c29e3e46195d6341255065d5 @@ -374,7 +374,7 @@ HFCacheInfo( Das Durchsuchen Ihres Caches ist interessant, aber was Sie normalerweise als Nächstes tun möchten, ist einige Teile zu löschen, um etwas Speicherplatz auf Ihrem Laufwerk freizugeben. Dies ist möglich mit dem -`delete-cache` CLI-Befehl. Man kann auch programmatisch den +`cache delete` CLI-Befehl. Man kann auch programmatisch den [`~HFCacheInfo.delete_revisions`] Helfer vom [`HFCacheInfo`] Objekt verwenden, das beim Durchsuchen des Caches zurückgegeben wird. @@ -413,7 +413,7 @@ Fehler ausgelöst. Die Löschung wird für andere Pfade im ### Cache vom Terminal aus leeren Der einfachste Weg, einige Revisionen aus Ihrem HF-Cache-System zu löschen, ist die Verwendung des -`delete-cache` Befehls vom `huggingface-cli` Tool. Der Befehl hat zwei Modi. Standardmäßig wird dem Benutzer +`cache delete` Befehls vom `hf` Tool. Der Befehl hat zwei Modi. Standardmäßig wird dem Benutzer eine TUI (Terminal User Interface) angezeigt, um auszuwählen, welche Revisionen gelöscht werden sollen. Diese TUI befindet sich derzeit in der Beta-Phase, da sie nicht auf allen Plattformen getestet wurde. Wenn die TUI auf Ihrem Gerät nicht funktioniert, können Sie sie mit dem Flag `--disable-tui` deaktivieren. @@ -430,7 +430,7 @@ pip install huggingface_hub["cli"] Führen Sie dann den Befehl aus: ``` -huggingface-cli delete-cache +hf cache delete ``` Sie sollten jetzt eine Liste von Revisionen sehen, die Sie auswählen/abwählen können: @@ -455,7 +455,7 @@ letzte Bestätigungsnachricht angezeigt. Drücken Sie erneut ``, und die abbrechen möchten, geben Sie `n` ein. ```txt -✗ huggingface-cli delete-cache --dir ~/.cache/huggingface/hub +✗ hf cache delete --dir ~/.cache/huggingface/hub ? Select revisions to delete: 2 revision(s) selected. ? 2 revisions selected counting for 3.1G. Confirm deletion ? Yes Start deletion. @@ -480,7 +480,7 @@ wie viel Speicherplatz mit der aktualisierten Revisionsliste freigegeben würde. Sie können die Datei weiter bearbeiten oder mit `"y"` bestätigen. ```sh -huggingface-cli delete-cache --disable-tui +hf cache delete --disable-tui ``` Beispiel für eine Befehlsdatei: @@ -488,7 +488,7 @@ Beispiel für eine Befehlsdatei: ```txt # INSTRUCTIONS # ------------ -# This is a temporary file created by running `huggingface-cli delete-cache` with the +# This is a temporary file created by running `hf cache delete` with the # `--disable-tui` option. It contains a set of revisions that can be deleted from your # local cache directory. # diff --git a/docs/source/de/guides/model-cards.md b/docs/source/de/guides/model-cards.md index 01858c41b8..0670d400ed 100644 --- a/docs/source/de/guides/model-cards.md +++ b/docs/source/de/guides/model-cards.md @@ -188,7 +188,7 @@ print(card) ## Model Cards teilen -Wenn Sie mit dem Hugging Face Hub authentifiziert sind (entweder durch Verwendung von `huggingface-cli login` oder [`login`]), können Sie Karten zum Hub hinzufügen, indem Sie einfach [`ModelCard.push_to_hub`] aufrufen. Schauen wir uns an, wie das funktioniert... +Wenn Sie mit dem Hugging Face Hub authentifiziert sind (entweder durch Verwendung von `hf auth login` oder [`login`]), können Sie Karten zum Hub hinzufügen, indem Sie einfach [`ModelCard.push_to_hub`] aufrufen. Schauen wir uns an, wie das funktioniert... Zuerst erstellen wir ein neues Repo namens 'hf-hub-modelcards-pr-test' im Namensraum des authentifizierten Benutzers: diff --git a/docs/source/de/guides/upload.md b/docs/source/de/guides/upload.md index 38db944c7e..d0f87bf497 100644 --- a/docs/source/de/guides/upload.md +++ b/docs/source/de/guides/upload.md @@ -16,9 +16,9 @@ Wenn Sie Dateien auf den Hub hochladen möchten, müssen Sie sich bei Ihrem Hugg - Melden Sie sich bei Ihrem Hugging Face-Konto mit dem folgenden Befehl an: ```bash - huggingface-cli login + hf auth login # oder mit einer Umgebungsvariable - huggingface-cli login --token $HUGGINGFACE_TOKEN + hf auth login --token $HUGGINGFACE_TOKEN ``` - Alternativ können Sie sich in einem Notebook oder einem Skript programmatisch mit [`login`] anmelden: @@ -378,7 +378,7 @@ Für weitere Details zu dieser Empfehlung werfen Sie bitte einen Blick auf diese Git LFS verarbeitet automatisch Dateien, die größer als 10MB sind. Für sehr große Dateien (>5GB) müssen Sie jedoch einen benutzerdefinierten Transferagenten für Git LFS installieren: ```bash -huggingface-cli lfs-enable-largefiles +hf lfs-enable-largefiles . ``` Sie sollten dies für jedes Repository installieren, das eine sehr große Datei enthält. Einmal installiert, können Sie Dateien hochladen, die größer als 5GB sind. diff --git a/docs/source/de/quick-start.md b/docs/source/de/quick-start.md index 83ce0a24e0..b413b3faf8 100644 --- a/docs/source/de/quick-start.md +++ b/docs/source/de/quick-start.md @@ -53,9 +53,9 @@ In vielen Fällen müssen Sie mit einem Hugging Face-Konto angemeldet sein, um m Sobald Sie Ihr "User Access Token" haben, führen Sie den folgenden Befehl in Ihrem Terminal aus: ```bash -huggingface-cli login +hf auth login # or using an environment variable -huggingface-cli login --token $HUGGINGFACE_TOKEN +hf auth login --token $HUGGINGFACE_TOKEN ``` Alternativ können Sie sich auch programmatisch in einem Notebook oder einem Skript mit [`login`] anmelden: @@ -67,7 +67,7 @@ Alternativ können Sie sich auch programmatisch in einem Notebook oder einem Skr Es ist auch möglich, sich programmatisch anzumelden, ohne aufgefordert zu werden, Ihr Token einzugeben, indem Sie das Token direkt an [`login`] weitergeben, wie z.B. `login(token="hf_xxx")`. Seien Sie vorsichtig, wenn Sie Ihren Quellcode teilen. Es ist eine bewährte Methode, das Token aus einem sicheren Tresor/Vault zu laden, anstatt es explizit in Ihrer Codebasis/Notebook zu speichern. -Sie können nur auf 1 Konto gleichzeitig angemeldet sein. Wenn Sie Ihren Computer mit einem neuen Konto anmelden, werden Sie vom vorherigen abgemeldet. Mit dem Befehl `huggingface-cli whoami` stellen Sie sicher, dass Sie immer wissen, welches Konto Sie gerade verwenden. Wenn Sie mehrere Konten im selben Skript verwalten möchten, können Sie Ihr Token bereitstellen, wenn Sie jede Methode aufrufen. Dies ist auch nützlich, wenn Sie kein Token auf Ihrem Computer speichern möchten. +Sie können nur auf 1 Konto gleichzeitig angemeldet sein. Wenn Sie Ihren Computer mit einem neuen Konto anmelden, werden Sie vom vorherigen abgemeldet. Mit dem Befehl `hf auth whoami` stellen Sie sicher, dass Sie immer wissen, welches Konto Sie gerade verwenden. Wenn Sie mehrere Konten im selben Skript verwalten möchten, können Sie Ihr Token bereitstellen, wenn Sie jede Methode aufrufen. Dies ist auch nützlich, wenn Sie kein Token auf Ihrem Computer speichern möchten. diff --git a/docs/source/en/guides/cli.md b/docs/source/en/guides/cli.md index 481cbf4c79..b7ce964a4b 100644 --- a/docs/source/en/guides/cli.md +++ b/docs/source/en/guides/cli.md @@ -4,7 +4,7 @@ rendered properly in your Markdown viewer. # Command Line Interface (CLI) -The `huggingface_hub` Python package comes with a built-in CLI called `huggingface-cli`. This tool allows you to interact with the Hugging Face Hub directly from a terminal. For example, you can login to your account, create a repository, upload and download files, etc. It also comes with handy features to configure your machine or manage your cache. In this guide, we will have a look at the main features of the CLI and how to use them. +The `huggingface_hub` Python package comes with a built-in CLI called `hf`. This tool allows you to interact with the Hugging Face Hub directly from a terminal. For example, you can login to your account, create a repository, upload and download files, etc. It also comes with handy features to configure your machine or manage your cache. In this guide, we will have a look at the main features of the CLI and how to use them. ## Getting started @@ -16,41 +16,39 @@ First of all, let's install the CLI: -In the snippet above, we also installed the `[cli]` extra dependencies to make the user experience better, especially when using the `delete-cache` command. +In the snippet above, we also installed the `[cli]` extra dependencies to make the user experience better, especially when using the `cache delete` command. Once installed, you can check that the CLI is correctly setup: ``` ->>> huggingface-cli --help -usage: huggingface-cli [] +>>> hf --help +usage: hf [] positional arguments: - {env,login,whoami,logout,repo,upload,download,lfs-enable-largefiles,lfs-multipart-upload,scan-cache,delete-cache,tag} - huggingface-cli command helpers - env Print information about the environment. - login Log in using a token from huggingface.co/settings/tokens - whoami Find out which huggingface.co account you are logged in as. - logout Log out - repo {create} Commands to interact with your huggingface.co repos. - upload Upload a file or a folder to a repo on the Hub + {auth,cache,download,repo,repo-files,upload,upload-large-folder,env,version,lfs-enable-largefiles,lfs-multipart-upload} + hf command helpers + auth Manage authentication (login, logout, etc.). + cache Manage local cache directory. download Download files from the Hub - lfs-enable-largefiles - Configure your repository to enable upload of files > 5GB. - scan-cache Scan cache directory. - delete-cache Delete revisions from the cache directory. - tag (create, list, delete) tags for a repo in the hub + repo Manage repos on the Hub. + repo-files Manage files in a repo on the Hub. + upload Upload a file or a folder to the Hub. Recommended for single-commit uploads. + upload-large-folder + Upload a large folder to the Hub. Recommended for resumable uploads. + env Print information about the environment. + version Print information about the hf version. options: -h, --help show this help message and exit ``` -If the CLI is correctly installed, you should see a list of all the options available in the CLI. If you get an error message such as `command not found: huggingface-cli`, please refer to the [Installation](../installation) guide. +If the CLI is correctly installed, you should see a list of all the options available in the CLI. If you get an error message such as `command not found: hf`, please refer to the [Installation](../installation) guide. -The `--help` option is very convenient for getting more details about a command. You can use it anytime to list all available options and their details. For example, `huggingface-cli upload --help` provides more information on how to upload files using the CLI. +The `--help` option is very convenient for getting more details about a command. You can use it anytime to list all available options and their details. For example, `hf upload --help` provides more information on how to upload files using the CLI. @@ -82,14 +80,14 @@ You can also install the CLI using [Homebrew](https://brew.sh/): Check out the Homebrew huggingface page [here](https://formulae.brew.sh/formula/huggingface-cli) for more details. -## huggingface-cli login +## hf auth login In many cases, you must be logged in to a Hugging Face account to interact with the Hub (download private repos, upload files, create PRs, etc.). To do so, you need a [User Access Token](https://huggingface.co/docs/hub/security-tokens) from your [Settings page](https://huggingface.co/settings/tokens). The User Access Token is used to authenticate your identity to the Hub. Make sure to set a token with write access if you want to upload or modify content. Once you have your token, run the following command in your terminal: ```bash ->>> huggingface-cli login +>>> hf auth login ``` This command will prompt you for a token. Copy-paste yours and press *Enter*. Then, you'll be asked if the token should also be saved as a git credential. Press *Enter* again (default to yes) if you plan to use `git` locally. Finally, it will call the Hub to check that your token is valid and save it locally. @@ -114,7 +112,7 @@ Alternatively, if you want to log-in without being prompted, you can pass the to ```bash # Or using an environment variable ->>> huggingface-cli login --token $HF_TOKEN --add-to-git-credential +>>> hf auth login --token $HF_TOKEN --add-to-git-credential Token is valid (permission: write). The token `token_name` has been saved to /home/wauplin/.cache/huggingface/stored_tokens Your token has been saved in your configured git credential helpers (store). @@ -125,31 +123,31 @@ The current active token is: `token_name` For more details about authentication, check out [this section](../quick-start#authentication). -## huggingface-cli whoami +## hf auth whoami -If you want to know if you are logged in, you can use `huggingface-cli whoami`. This command doesn't have any options and simply prints your username and the organizations you are a part of on the Hub: +If you want to know if you are logged in, you can use `hf auth whoami`. This command doesn't have any options and simply prints your username and the organizations you are a part of on the Hub: ```bash -huggingface-cli whoami +hf auth whoami Wauplin orgs: huggingface,eu-test,OAuthTesters,hf-accelerate,HFSmolCluster ``` If you are not logged in, an error message will be printed. -## huggingface-cli logout +## hf auth logout This command logs you out. In practice, it will delete all tokens stored on your machine. If you want to remove a specific token, you can specify the token name as an argument. This command will not log you out if you are logged in using the `HF_TOKEN` environment variable (see [reference](../package_reference/environment_variables#hftoken)). If that is the case, you must unset the environment variable in your machine configuration. -## huggingface-cli download +## hf download -Use the `huggingface-cli download` command to download files from the Hub directly. Internally, it uses the same [`hf_hub_download`] and [`snapshot_download`] helpers described in the [Download](./download) guide and prints the returned path to the terminal. In the examples below, we will walk through the most common use cases. For a full list of available options, you can run: +Use the `hf download` command to download files from the Hub directly. Internally, it uses the same [`hf_hub_download`] and [`snapshot_download`] helpers described in the [Download](./download) guide and prints the returned path to the terminal. In the examples below, we will walk through the most common use cases. For a full list of available options, you can run: ```bash -huggingface-cli download --help +hf download --help ``` ### Download a single file @@ -157,7 +155,7 @@ huggingface-cli download --help To download a single file from a repo, simply provide the repo_id and filename as follow: ```bash ->>> huggingface-cli download gpt2 config.json +>>> hf download gpt2 config.json downloading https://huggingface.co/gpt2/resolve/main/config.json to /home/wauplin/.cache/huggingface/hub/tmpwrq8dm5o (…)ingface.co/gpt2/resolve/main/config.json: 100%|██████████████████████████████████| 665/665 [00:00<00:00, 2.49MB/s] /home/wauplin/.cache/huggingface/hub/models--gpt2/snapshots/11c5a3d5811f50298f278a704980280950aedb10/config.json @@ -168,7 +166,7 @@ The command will always print on the last line the path to the file on your loca To download a file located in a subdirectory of the repo, you should provide the path of the file in the repo in posix format like this: ```bash ->>> huggingface-cli download HiDream-ai/HiDream-I1-Full text_encoder/model.safetensors +>>> hf download HiDream-ai/HiDream-I1-Full text_encoder/model.safetensors ``` ### Download an entire repository @@ -176,7 +174,7 @@ To download a file located in a subdirectory of the repo, you should provide the In some cases, you just want to download all the files from a repository. This can be done by just specifying the repo id: ```bash ->>> huggingface-cli download HuggingFaceH4/zephyr-7b-beta +>>> hf download HuggingFaceH4/zephyr-7b-beta Fetching 23 files: 0%| | 0/23 [00:00>> huggingface-cli download gpt2 config.json model.safetensors +>>> hf download gpt2 config.json model.safetensors Fetching 2 files: 0%| | 0/2 [00:00>> huggingface-cli download stabilityai/stable-diffusion-xl-base-1.0 --include "*.safetensors" --exclude "*.fp16.*"* +>>> hf download stabilityai/stable-diffusion-xl-base-1.0 --include "*.safetensors" --exclude "*.fp16.*"* Fetching 8 files: 0%| | 0/8 [00:00>> huggingface-cli download HuggingFaceH4/ultrachat_200k --repo-type dataset +>>> hf download HuggingFaceH4/ultrachat_200k --repo-type dataset # https://huggingface.co/spaces/HuggingFaceH4/zephyr-chat ->>> huggingface-cli download HuggingFaceH4/zephyr-chat --repo-type space +>>> hf download HuggingFaceH4/zephyr-chat --repo-type space ... ``` @@ -226,7 +224,7 @@ The examples above show how to download from a model repository. To download a d The examples above show how to download from the latest commit on the main branch. To download from a specific revision (commit hash, branch name or tag), use the `--revision` option: ```bash ->>> huggingface-cli download bigcode/the-stack --repo-type dataset --revision v1.1 +>>> hf download bigcode/the-stack --repo-type dataset --revision v1.1 ... ``` @@ -243,7 +241,7 @@ For more details on how downloading to a local file works, check out the [downlo ```bash ->>> huggingface-cli download adept/fuyu-8b model-00001-of-00002.safetensors --local-dir fuyu +>>> hf download adept/fuyu-8b model-00001-of-00002.safetensors --local-dir fuyu ... fuyu/model-00001-of-00002.safetensors ``` @@ -253,26 +251,26 @@ fuyu/model-00001-of-00002.safetensors If not using `--local-dir`, all files will be downloaded by default to the cache directory defined by the `HF_HOME` [environment variable](../package_reference/environment_variables#hfhome). You can specify a custom cache using `--cache-dir`: ```bash ->>> huggingface-cli download adept/fuyu-8b --cache-dir ./path/to/cache +>>> hf download adept/fuyu-8b --cache-dir ./path/to/cache ... ./path/to/cache/models--adept--fuyu-8b/snapshots/ddcacbcf5fdf9cc59ff01f6be6d6662624d9c745 ``` ### Specify a token -To access private or gated repositories, you must use a token. By default, the token saved locally (using `huggingface-cli login`) will be used. If you want to authenticate explicitly, use the `--token` option: +To access private or gated repositories, you must use a token. By default, the token saved locally (using `hf auth login`) will be used. If you want to authenticate explicitly, use the `--token` option: ```bash ->>> huggingface-cli download gpt2 config.json --token=hf_**** +>>> hf download gpt2 config.json --token=hf_**** /home/wauplin/.cache/huggingface/hub/models--gpt2/snapshots/11c5a3d5811f50298f278a704980280950aedb10/config.json ``` ### Quiet mode -By default, the `huggingface-cli download` command will be verbose. It will print details such as warning messages, information about the downloaded files, and progress bars. If you want to silence all of this, use the `--quiet` option. Only the last line (i.e. the path to the downloaded files) is printed. This can prove useful if you want to pass the output to another command in a script. +By default, the `hf download` command will be verbose. It will print details such as warning messages, information about the downloaded files, and progress bars. If you want to silence all of this, use the `--quiet` option. Only the last line (i.e. the path to the downloaded files) is printed. This can prove useful if you want to pass the output to another command in a script. ```bash ->>> huggingface-cli download gpt2 --quiet +>>> hf download gpt2 --quiet /home/wauplin/.cache/huggingface/hub/models--gpt2/snapshots/11c5a3d5811f50298f278a704980280950aedb10 ``` @@ -290,12 +288,12 @@ export HF_HUB_DOWNLOAD_TIMEOUT=30 For more details, check out the [environment variables reference](../package_reference/environment_variables#hfhubdownloadtimeout). And rerun your download command. -## huggingface-cli upload +## hf upload -Use the `huggingface-cli upload` command to upload files to the Hub directly. Internally, it uses the same [`upload_file`] and [`upload_folder`] helpers described in the [Upload](./upload) guide. In the examples below, we will walk through the most common use cases. For a full list of available options, you can run: +Use the `hf upload` command to upload files to the Hub directly. Internally, it uses the same [`upload_file`] and [`upload_folder`] helpers described in the [Upload](./upload) guide. In the examples below, we will walk through the most common use cases. For a full list of available options, you can run: ```bash ->>> huggingface-cli upload --help +>>> hf upload --help ``` ### Upload an entire folder @@ -303,13 +301,13 @@ Use the `huggingface-cli upload` command to upload files to the Hub directly. In The default usage for this command is: ```bash -# Usage: huggingface-cli upload [repo_id] [local_path] [path_in_repo] +# Usage: hf upload [repo_id] [local_path] [path_in_repo] ``` To upload the current directory at the root of the repo, use: ```bash ->>> huggingface-cli upload my-cool-model . . +>>> hf upload my-cool-model . . https://huggingface.co/Wauplin/my-cool-model/tree/main/ ``` @@ -322,14 +320,14 @@ If the repo doesn't exist yet, it will be created automatically. You can also upload a specific folder: ```bash ->>> huggingface-cli upload my-cool-model ./models . +>>> hf upload my-cool-model ./models . https://huggingface.co/Wauplin/my-cool-model/tree/main/ ``` Finally, you can upload a folder to a specific destination on the repo: ```bash ->>> huggingface-cli upload my-cool-model ./path/to/curated/data /data/train +>>> hf upload my-cool-model ./path/to/curated/data /data/train https://huggingface.co/Wauplin/my-cool-model/tree/main/data/train ``` @@ -338,14 +336,14 @@ https://huggingface.co/Wauplin/my-cool-model/tree/main/data/train You can also upload a single file by setting `local_path` to point to a file on your machine. If that's the case, `path_in_repo` is optional and will default to the name of your local file: ```bash ->>> huggingface-cli upload Wauplin/my-cool-model ./models/model.safetensors +>>> hf upload Wauplin/my-cool-model ./models/model.safetensors https://huggingface.co/Wauplin/my-cool-model/blob/main/model.safetensors ``` If you want to upload a single file to a specific directory, set `path_in_repo` accordingly: ```bash ->>> huggingface-cli upload Wauplin/my-cool-model ./models/model.safetensors /vae/model.safetensors +>>> hf upload Wauplin/my-cool-model ./models/model.safetensors /vae/model.safetensors https://huggingface.co/Wauplin/my-cool-model/blob/main/vae/model.safetensors ``` @@ -355,7 +353,7 @@ To upload multiple files from a folder at once without uploading the entire fold ```bash # Sync local Space with Hub (upload new files except from logs/, delete removed files) ->>> huggingface-cli upload Wauplin/space-example --repo-type=space --exclude="/logs/*" --delete="*" --commit-message="Sync local Space with Hub" +>>> hf upload Wauplin/space-example --repo-type=space --exclude="/logs/*" --delete="*" --commit-message="Sync local Space with Hub" ... ``` @@ -364,7 +362,7 @@ To upload multiple files from a folder at once without uploading the entire fold To upload to a dataset or a Space, use the `--repo-type` option: ```bash ->>> huggingface-cli upload Wauplin/my-cool-dataset ./data /train --repo-type=dataset +>>> hf upload Wauplin/my-cool-dataset ./data /train --repo-type=dataset ... ``` @@ -373,7 +371,7 @@ To upload to a dataset or a Space, use the `--repo-type` option: To upload content to a repo owned by an organization instead of a personal repo, you must explicitly specify it in the `repo_id`: ```bash ->>> huggingface-cli upload MyCoolOrganization/my-cool-model . . +>>> hf upload MyCoolOrganization/my-cool-model . . https://huggingface.co/MyCoolOrganization/my-cool-model/tree/main/ ``` @@ -383,7 +381,7 @@ By default, files are uploaded to the `main` branch. If you want to upload files ```bash # Upload files to a PR ->>> huggingface-cli upload bigcode/the-stack . . --repo-type dataset --revision refs/pr/104 +>>> hf upload bigcode/the-stack . . --repo-type dataset --revision refs/pr/104 ... ``` @@ -395,7 +393,7 @@ If you don't have the permission to push to a repo, you must open a PR and let t ```bash # Create a PR and upload the files to it ->>> huggingface-cli upload bigcode/the-stack . . --repo-type dataset --revision refs/pr/104 +>>> hf upload bigcode/the-stack . . --repo-type dataset --revision refs/pr/104 https://huggingface.co/datasets/bigcode/the-stack/blob/refs%2Fpr%2F104/ ``` @@ -405,7 +403,7 @@ In some cases, you might want to push regular updates to a repo. For example, th ```bash # Upload new logs every 10 minutes -huggingface-cli upload training-model logs/ --every=10 +hf upload training-model logs/ --every=10 ``` ### Specify a commit message @@ -413,70 +411,70 @@ huggingface-cli upload training-model logs/ --every=10 Use the `--commit-message` and `--commit-description` to set a custom message and description for your commit instead of the default one ```bash ->>> huggingface-cli upload Wauplin/my-cool-model ./models . --commit-message="Epoch 34/50" --commit-description="Val accuracy: 68%. Check tensorboard for more details." +>>> hf upload Wauplin/my-cool-model ./models . --commit-message="Epoch 34/50" --commit-description="Val accuracy: 68%. Check tensorboard for more details." ... https://huggingface.co/Wauplin/my-cool-model/tree/main ``` ### Specify a token -To upload files, you must use a token. By default, the token saved locally (using `huggingface-cli login`) will be used. If you want to authenticate explicitly, use the `--token` option: +To upload files, you must use a token. By default, the token saved locally (using `hf auth login`) will be used. If you want to authenticate explicitly, use the `--token` option: ```bash ->>> huggingface-cli upload Wauplin/my-cool-model ./models . --token=hf_**** +>>> hf upload Wauplin/my-cool-model ./models . --token=hf_**** ... https://huggingface.co/Wauplin/my-cool-model/tree/main ``` ### Quiet mode -By default, the `huggingface-cli upload` command will be verbose. It will print details such as warning messages, information about the uploaded files, and progress bars. If you want to silence all of this, use the `--quiet` option. Only the last line (i.e. the URL to the uploaded files) is printed. This can prove useful if you want to pass the output to another command in a script. +By default, the `hf upload` command will be verbose. It will print details such as warning messages, information about the uploaded files, and progress bars. If you want to silence all of this, use the `--quiet` option. Only the last line (i.e. the URL to the uploaded files) is printed. This can prove useful if you want to pass the output to another command in a script. ```bash ->>> huggingface-cli upload Wauplin/my-cool-model ./models . --quiet +>>> hf upload Wauplin/my-cool-model ./models . --quiet https://huggingface.co/Wauplin/my-cool-model/tree/main ``` -## huggingface-cli repo-files +## hf repo-files -If you want to delete files from a Hugging Face repository, use the `huggingface-cli repo-files` command. +If you want to delete files from a Hugging Face repository, use the `hf repo-files` command. ### Delete files -The `huggingface-cli repo-files delete` sub-command allows you to delete files from a repository. Here are some usage examples. +The `hf repo-files delete ` sub-command allows you to delete files from a repository. Here are some usage examples. Delete a folder : ```bash ->>> huggingface-cli repo-files Wauplin/my-cool-model delete folder/ +>>> hf repo-files delete Wauplin/my-cool-model folder/ Files correctly deleted from repo. Commit: https://huggingface.co/Wauplin/my-cool-mo... ``` Delete multiple files: ```bash ->>> huggingface-cli repo-files Wauplin/my-cool-model delete file.txt folder/pytorch_model.bin +>>> hf repo-files delete Wauplin/my-cool-model file.txt folder/pytorch_model.bin Files correctly deleted from repo. Commit: https://huggingface.co/Wauplin/my-cool-mo... ``` Use Unix-style wildcards to delete sets of files: ```bash ->>> huggingface-cli repo-files Wauplin/my-cool-model delete "*.txt" "folder/*.bin" +>>> hf repo-files delete Wauplin/my-cool-model "*.txt" "folder/*.bin" Files correctly deleted from repo. Commit: https://huggingface.co/Wauplin/my-cool-mo... ``` ### Specify a token -To delete files from a repo you must be authenticated and authorized. By default, the token saved locally (using `huggingface-cli login`) will be used. If you want to authenticate explicitly, use the `--token` option: +To delete files from a repo you must be authenticated and authorized. By default, the token saved locally (using `hf auth login`) will be used. If you want to authenticate explicitly, use the `--token` option: ```bash ->>> huggingface-cli repo-files --token=hf_**** Wauplin/my-cool-model delete file.txt +>>> hf repo-files delete --token=hf_**** Wauplin/my-cool-model file.txt ``` -## huggingface-cli scan-cache +## hf cache scan -Scanning your cache directory is useful if you want to know which repos you have downloaded and how much space it takes on your disk. You can do that by running `huggingface-cli scan-cache`: +Scanning your cache directory is useful if you want to know which repos you have downloaded and how much space it takes on your disk. You can do that by running `hf cache scan`: ```bash ->>> huggingface-cli scan-cache +>>> hf cache scan REPO ID REPO TYPE SIZE ON DISK NB FILES LAST_ACCESSED LAST_MODIFIED REFS LOCAL PATH --------------------------- --------- ------------ -------- ------------- ------------- ------------------- ------------------------------------------------------------------------- glue dataset 116.3K 15 4 days ago 4 days ago 2.4.0, main, 1.17.0 /home/wauplin/.cache/huggingface/hub/datasets--glue @@ -492,20 +490,20 @@ Got 1 warning(s) while scanning. Use -vvv to print details. For more details about how to scan your cache directory, please refer to the [Manage your cache](./manage-cache#scan-cache-from-the-terminal) guide. -## huggingface-cli delete-cache +## hf cache delete -`huggingface-cli delete-cache` is a tool that helps you delete parts of your cache that you don't use anymore. This is useful for saving and freeing disk space. To learn more about using this command, please refer to the [Manage your cache](./manage-cache#clean-cache-from-the-terminal) guide. +`hf cache delete` is a tool that helps you delete parts of your cache that you don't use anymore. This is useful for saving and freeing disk space. To learn more about using this command, please refer to the [Manage your cache](./manage-cache#clean-cache-from-the-terminal) guide. -## huggingface-cli tag +## hf repo tag create -The `huggingface-cli tag` command allows you to tag, untag, and list tags for repositories. +The `hf repo tag create` command allows you to tag, untag, and list tags for repositories. ### Tag a model To tag a repo, you need to provide the `repo_id` and the `tag` name: ```bash ->>> huggingface-cli tag Wauplin/my-cool-model v1.0 +>>> hf repo tag create Wauplin/my-cool-model v1.0 You are about to create tag v1.0 on model Wauplin/my-cool-model Tag v1.0 created on Wauplin/my-cool-model ``` @@ -515,7 +513,7 @@ Tag v1.0 created on Wauplin/my-cool-model If you want to tag a specific revision, you can use the `--revision` option. By default, the tag will be created on the `main` branch: ```bash ->>> huggingface-cli tag Wauplin/my-cool-model v1.0 --revision refs/pr/104 +>>> hf repo tag create Wauplin/my-cool-model v1.0 --revision refs/pr/104 You are about to create tag v1.0 on model Wauplin/my-cool-model Tag v1.0 created on Wauplin/my-cool-model ``` @@ -525,7 +523,7 @@ Tag v1.0 created on Wauplin/my-cool-model If you want to tag a dataset or Space, you must specify the `--repo-type` option: ```bash ->>> huggingface-cli tag bigcode/the-stack v1.0 --repo-type dataset +>>> hf repo tag create bigcode/the-stack v1.0 --repo-type dataset You are about to create tag v1.0 on dataset bigcode/the-stack Tag v1.0 created on bigcode/the-stack ``` @@ -535,7 +533,7 @@ Tag v1.0 created on bigcode/the-stack To list all tags for a repository, use the `-l` or `--list` option: ```bash ->>> huggingface-cli tag Wauplin/gradio-space-ci -l --repo-type space +>>> hf repo tag create Wauplin/gradio-space-ci -l --repo-type space Tags for space Wauplin/gradio-space-ci: 0.2.2 0.2.1 @@ -550,7 +548,7 @@ Tags for space Wauplin/gradio-space-ci: To delete a tag, use the `-d` or `--delete` option: ```bash ->>> huggingface-cli tag -d Wauplin/my-cool-model v1.0 +>>> hf repo tag create -d Wauplin/my-cool-model v1.0 You are about to delete tag v1.0 on model Wauplin/my-cool-model Proceed? [Y/n] y Tag v1.0 deleted on Wauplin/my-cool-model @@ -558,12 +556,12 @@ Tag v1.0 deleted on Wauplin/my-cool-model You can also pass `-y` to skip the confirmation step. -## huggingface-cli env +## hf env -The `huggingface-cli env` command prints details about your machine setup. This is useful when you open an issue on [GitHub](https://github.com/huggingface/huggingface_hub) to help the maintainers investigate your problem. +The `hf env` command prints details about your machine setup. This is useful when you open an issue on [GitHub](https://github.com/huggingface/huggingface_hub) to help the maintainers investigate your problem. ```bash ->>> huggingface-cli env +>>> hf env Copy-and-paste the text below in your GitHub issue. diff --git a/docs/source/en/guides/download.md b/docs/source/en/guides/download.md index 647f9d6dcc..2db66f2079 100644 --- a/docs/source/en/guides/download.md +++ b/docs/source/en/guides/download.md @@ -144,12 +144,12 @@ Don't worry about the `.cache/huggingface/` folder when committing changes to th ## Download from the CLI -You can use the `huggingface-cli download` command from the terminal to directly download files from the Hub. +You can use the `hf download` command from the terminal to directly download files from the Hub. Internally, it uses the same [`hf_hub_download`] and [`snapshot_download`] helpers described above and prints the returned path to the terminal. ```bash ->>> huggingface-cli download gpt2 config.json +>>> hf download gpt2 config.json /home/wauplin/.cache/huggingface/hub/models--gpt2/snapshots/11c5a3d5811f50298f278a704980280950aedb10/config.json ``` @@ -157,12 +157,12 @@ You can download multiple files at once which displays a progress bar and return are located: ```bash ->>> huggingface-cli download gpt2 config.json model.safetensors +>>> hf download gpt2 config.json model.safetensors Fetching 2 files: 100%|████████████████████████████████████████████| 2/2 [00:00<00:00, 23831.27it/s] /home/wauplin/.cache/huggingface/hub/models--gpt2/snapshots/11c5a3d5811f50298f278a704980280950aedb10 ``` -For more details about the CLI download command, please refer to the [CLI guide](./cli#huggingface-cli-download). +For more details about the CLI download command, please refer to the [CLI guide](./cli#hf-download). ## Faster downloads diff --git a/docs/source/en/guides/manage-cache.md b/docs/source/en/guides/manage-cache.md index 77990d4ca1..ab384dd79b 100644 --- a/docs/source/en/guides/manage-cache.md +++ b/docs/source/en/guides/manage-cache.md @@ -365,19 +365,16 @@ At the moment, cached files are never deleted from your local directory: when yo a new revision of a branch, previous files are kept in case you need them again. Therefore it can be useful to scan your cache directory in order to know which repos and revisions are taking the most disk space. `huggingface_hub` provides an helper to -do so that can be used via `huggingface-cli` or in a python script. +do so that can be used via `hf` CLI or in a python script. **Scan cache from the terminal** -The easiest way to scan your HF cache-system is to use the `scan-cache` command from -`huggingface-cli` tool. This command scans the cache and prints a report with information -like repo id, repo type, disk usage, refs and full local path. +The easiest way to scan your HF cache-system is to use the `hf cache scan` command line. This command scans the cache and prints a report with information like repo id, repo type, disk usage, refs and full local path. -The snippet below shows a scan report in a folder in which 4 models and 2 datasets are -cached. +The snippet below shows a scan report in a folder in which 4 models and 2 datasets are cached. ```text -➜ huggingface-cli scan-cache +➜ hf cache scan REPO ID REPO TYPE SIZE ON DISK NB FILES LAST_ACCESSED LAST_MODIFIED REFS LOCAL PATH --------------------------- --------- ------------ -------- ------------- ------------- ------------------- ------------------------------------------------------------------------- glue dataset 116.3K 15 4 days ago 4 days ago 2.4.0, main, 1.17.0 /home/wauplin/.cache/huggingface/hub/datasets--glue @@ -399,7 +396,7 @@ For example, here `bert-base-cased` has 2 revisions of 1.4G and 1.5G but the tot usage is only 1.9G. ```text -➜ huggingface-cli scan-cache -v +➜ hf cache scan -v REPO ID REPO TYPE REVISION SIZE ON DISK NB FILES LAST_MODIFIED REFS LOCAL PATH --------------------------- --------- ---------------------------------------- ------------ -------- ------------- ----------- ---------------------------------------------------------------------------------------------------------------------------- glue dataset 9338f7b671827df886678df2bdd7cc7b4f36dffd 97.7K 14 4 days ago main, 2.4.0 /home/wauplin/.cache/huggingface/hub/datasets--glue/snapshots/9338f7b671827df886678df2bdd7cc7b4f36dffd @@ -425,7 +422,7 @@ filter the entries. Here is an example to filter only revisions from the "t5-sma model on a Unix-based machine. ```text -➜ eval "huggingface-cli scan-cache -v" | grep "t5-small" +➜ eval "hf cache scan -v" | grep "t5-small" t5-small model 98ffebbb27340ec1b1abd7c45da12c253ee1882a 726.2M 6 1 week ago refs/pr/1 /home/wauplin/.cache/huggingface/hub/models--t5-small/snapshots/98ffebbb27340ec1b1abd7c45da12c253ee1882a t5-small model d0a119eedb3718e34c648e594394474cf95e0617 485.8M 6 4 weeks ago /home/wauplin/.cache/huggingface/hub/models--t5-small/snapshots/d0a119eedb3718e34c648e594394474cf95e0617 t5-small model d78aea13fa7ecd06c29e3e46195d6341255065d5 970.7M 9 1 week ago main /home/wauplin/.cache/huggingface/hub/models--t5-small/snapshots/d78aea13fa7ecd06c29e3e46195d6341255065d5 @@ -499,7 +496,7 @@ HFCacheInfo( Scanning your cache is interesting but what you really want to do next is usually to delete some portions to free up some space on your drive. This is possible using the -`delete-cache` CLI command. One can also programmatically use the +`cache delete` CLI command. One can also programmatically use the [`~HFCacheInfo.delete_revisions`] helper from [`HFCacheInfo`] object returned when scanning the cache. @@ -538,7 +535,7 @@ error is thrown. The deletion continues for other paths contained in the **Clean cache from the terminal** The easiest way to delete some revisions from your HF cache-system is to use the -`delete-cache` command from `huggingface-cli` tool. The command has two modes. By +`hf cache delete` CLI tool. The command has two modes. By default, a TUI (Terminal User Interface) is displayed to the user to select which revisions to delete. This TUI is currently in beta as it has not been tested on all platforms. If the TUI doesn't work on your machine, you can disable it using the @@ -556,7 +553,7 @@ pip install huggingface_hub["cli"] Then run the command: ``` -huggingface-cli delete-cache +hf cache delete ``` You should now see a list of revisions that you can select/deselect: @@ -581,7 +578,7 @@ confirmation message will be prompted. Press `` again and the deletion wi effective. If you want to cancel, enter `n`. ```txt -✗ huggingface-cli delete-cache --dir ~/.cache/huggingface/hub +✗ hf cache delete --dir ~/.cache/huggingface/hub ? Select revisions to delete: 2 revision(s) selected. ? 2 revisions selected counting for 3.1G. Confirm deletion ? Yes Start deletion. @@ -605,7 +602,7 @@ and press ``. By default it will compute how much space would be freed wi updated list of revisions. You can continue to edit the file or confirm with `"y"`. ```sh -huggingface-cli delete-cache --disable-tui +hf cache delete --disable-tui ``` Example of command file: @@ -613,7 +610,7 @@ Example of command file: ```txt # INSTRUCTIONS # ------------ -# This is a temporary file created by running `huggingface-cli delete-cache` with the +# This is a temporary file created by running `hf cache delete` with the # `--disable-tui` option. It contains a set of revisions that can be deleted from your # local cache directory. # diff --git a/docs/source/en/guides/model-cards.md b/docs/source/en/guides/model-cards.md index 84a5251960..b31593600b 100644 --- a/docs/source/en/guides/model-cards.md +++ b/docs/source/en/guides/model-cards.md @@ -183,7 +183,7 @@ print(card) ## Share Model Cards -If you're authenticated with the Hugging Face Hub (either by using `huggingface-cli login` or [`login`]), you can push cards to the Hub by simply calling [`ModelCard.push_to_hub`]. Let's take a look at how to do that... +If you're authenticated with the Hugging Face Hub (either by using `hf auth login` or [`login`]), you can push cards to the Hub by simply calling [`ModelCard.push_to_hub`]. Let's take a look at how to do that... First, we'll create a new repo called 'hf-hub-modelcards-pr-test' under the authenticated user's namespace: diff --git a/docs/source/en/guides/upload.md b/docs/source/en/guides/upload.md index a74599ec7e..69930887eb 100644 --- a/docs/source/en/guides/upload.md +++ b/docs/source/en/guides/upload.md @@ -83,16 +83,16 @@ but before that, all previous logs on the repo on deleted. All of this in a sing ## Upload from the CLI -You can use the `huggingface-cli upload` command from the terminal to directly upload files to the Hub. Internally it uses the same [`upload_file`] and [`upload_folder`] helpers described above. +You can use the `hf upload` command from the terminal to directly upload files to the Hub. Internally it uses the same [`upload_file`] and [`upload_folder`] helpers described above. You can either upload a single file or an entire folder: ```bash -# Usage: huggingface-cli upload [repo_id] [local_path] [path_in_repo] ->>> huggingface-cli upload Wauplin/my-cool-model ./models/model.safetensors model.safetensors +# Usage: hf upload [repo_id] [local_path] [path_in_repo] +>>> hf upload Wauplin/my-cool-model ./models/model.safetensors model.safetensors https://huggingface.co/Wauplin/my-cool-model/blob/main/model.safetensors ->>> huggingface-cli upload Wauplin/my-cool-model ./models . +>>> hf upload Wauplin/my-cool-model ./models . https://huggingface.co/Wauplin/my-cool-model/tree/main ``` @@ -101,11 +101,11 @@ check if a local folder or file has the same name as the `repo_id`. If that's th Otherwise, an exception is raised asking the user to explicitly set `local_path`. In any case, if `path_in_repo` is not set, files are uploaded at the root of the repo. -For more details about the CLI upload command, please refer to the [CLI guide](./cli#huggingface-cli-upload). +For more details about the CLI upload command, please refer to the [CLI guide](./cli#hf-upload). ## Upload a large folder -In most cases, the [`upload_folder`] method and `huggingface-cli upload` command should be the go-to solutions to upload files to the Hub. They ensure a single commit will be made, handle a lot of use cases, and fail explicitly when something wrong happens. However, when dealing with a large amount of data, you will usually prefer a resilient process even if it leads to more commits or requires more CPU usage. The [`upload_large_folder`] method has been implemented in that spirit: +In most cases, the [`upload_folder`] method and `hf upload` command should be the go-to solutions to upload files to the Hub. They ensure a single commit will be made, handle a lot of use cases, and fail explicitly when something wrong happens. However, when dealing with a large amount of data, you will usually prefer a resilient process even if it leads to more commits or requires more CPU usage. The [`upload_large_folder`] method has been implemented in that spirit: - it is resumable: the upload process is split into many small tasks (hashing files, pre-uploading them, and committing them). Each time a task is completed, the result is cached locally in a `./cache/huggingface` folder inside the folder you are trying to upload. By doing so, restarting the process after an interruption will resume all completed tasks. - it is multi-threaded: hashing large files and pre-uploading them benefits a lot from multithreading if your machine allows it. - it is resilient to errors: a high-level retry-mechanism has been added to retry each independent task indefinitely until it passes (no matter if it's a OSError, ConnectionError, PermissionError, etc.). This mechanism is double-edged. If transient errors happen, the process will continue and retry. If permanent errors happen (e.g. permission denied), it will retry indefinitely without solving the root cause. @@ -139,7 +139,7 @@ First, the repo is created if it didn't exist before. Then, the local folder is A command line is also provided. You can define the number of workers and the level of verbosity in the terminal: ```sh -huggingface-cli upload-large-folder HuggingFaceM4/Docmatix --repo-type=dataset /path/to/local/docmatix --num-workers=16 +hf upload-large-folder HuggingFaceM4/Docmatix --repo-type=dataset /path/to/local/docmatix --num-workers=16 ``` @@ -503,7 +503,7 @@ core differences between HTTP-based and Git-based approaches. Git LFS automatically handles files larger than 10MB. But for very large files (>5GB), you need to install a custom transfer agent for Git LFS: ```bash -huggingface-cli lfs-enable-largefiles +hf lfs-enable-largefiles ``` You should install this for each repository that has a very large file. Once installed, you'll be able to push files larger than 5GB. diff --git a/docs/source/en/package_reference/environment_variables.md b/docs/source/en/package_reference/environment_variables.md index 8116c6fcaf..974d611208 100644 --- a/docs/source/en/package_reference/environment_variables.md +++ b/docs/source/en/package_reference/environment_variables.md @@ -232,7 +232,7 @@ Boolean value. Equivalent to `HF_HUB_DISABLE_TELEMETRY`. When set to true, telem ### NO_COLOR -Boolean value. When set, `huggingface-cli` tool will not print any ANSI color. +Boolean value. When set, `hf` CLI will not print any ANSI color. See [no-color.org](https://no-color.org/). ### XDG_CACHE_HOME diff --git a/docs/source/en/package_reference/hf_api.md b/docs/source/en/package_reference/hf_api.md index 7ed523e22b..99ce0c2905 100644 --- a/docs/source/en/package_reference/hf_api.md +++ b/docs/source/en/package_reference/hf_api.md @@ -10,7 +10,7 @@ All methods from the `HfApi` are also accessible from the package's root directl Using the root method is more straightforward but the [`HfApi`] class gives you more flexibility. In particular, you can pass a token that will be reused in all HTTP calls. This is different -than `huggingface-cli login` or [`login`] as the token is not persisted on the machine. +than `hf auth login` or [`login`] as the token is not persisted on the machine. It is also possible to provide a different endpoint or configure a custom user-agent. ```python diff --git a/docs/source/en/quick-start.md b/docs/source/en/quick-start.md index 0370b3934b..969d9c1c9c 100644 --- a/docs/source/en/quick-start.md +++ b/docs/source/en/quick-start.md @@ -73,7 +73,7 @@ Tokens can have `read` or `write` permissions. Make sure to have a `write` acces The easiest way to authenticate is to save the token on your machine. You can do that from the terminal using the [`login`] command: ```bash -huggingface-cli login +hf auth login ``` The command will tell you if you are already logged in and prompt you for your token. The token is then validated and saved in your `HF_HOME` directory (defaults to `~/.cache/huggingface/token`). Any script or library interacting with the Hub will use this token when sending requests. @@ -85,7 +85,7 @@ Alternatively, you can programmatically login using [`login`] in a notebook or a >>> login() ``` -You can only be logged in to one account at a time. Logging in to a new account will automatically log you out of the previous one. To determine your currently active account, simply run the `huggingface-cli whoami` command. +You can only be logged in to one account at a time. Logging in to a new account will automatically log you out of the previous one. To determine your currently active account, simply run the `hf auth whoami` command. @@ -98,13 +98,13 @@ Once logged in, all requests to the Hub - even methods that don't necessarily re You can save multiple tokens on your machine by simply logging in with the [`login`] command with each token. If you need to switch between these tokens locally, you can use the [`auth switch`] command: ```bash -huggingface-cli auth switch +hf auth switch ``` This command will prompt you to select a token by its name from a list of saved tokens. Once selected, the chosen token becomes the _active_ token, and it will be used for all interactions with the Hub. -You can list all available access tokens on your machine with `huggingface-cli auth list`. +You can list all available access tokens on your machine with `hf auth list`. ### Environment variable diff --git a/docs/source/fr/quick-start.md b/docs/source/fr/quick-start.md index 1a99d830d7..073e9331da 100644 --- a/docs/source/fr/quick-start.md +++ b/docs/source/fr/quick-start.md @@ -66,9 +66,9 @@ Une fois que vous avez votre token d'authentification, lancez la commande suivan dans votre terminal: ```bash -huggingface-cli login +hf auth login # ou en utilisant une varible d'environnement: -huggingface-cli login --token $HUGGINGFACE_TOKEN +hf auth login --token $HUGGINGFACE_TOKEN ``` Sinon, vous pouvez vous connecter en utilisant [`login`] dans un notebook ou @@ -87,7 +87,7 @@ codebase/notebook. Vous ne pouvez être connecté qu'à un seul compte à la fois. Si vous connectez votre machine à un autre compte, vous serez déconnecté du premier compte. Vérifiez toujours le compte que vous utilisez avec la commande -`huggingface-cli whoami`. Si vous voulez gérer plusieurs compte dans le même script, vous pouvez passer votre +`hf auth whoami`. Si vous voulez gérer plusieurs compte dans le même script, vous pouvez passer votre token à chaque appel de méthode. C'est aussi utile si vous ne voulez pas sauvegarder de token sur votre machine. diff --git a/docs/source/hi/quick-start.md b/docs/source/hi/quick-start.md index 5f7e9c438f..e7bf3fe606 100644 --- a/docs/source/hi/quick-start.md +++ b/docs/source/hi/quick-start.md @@ -65,7 +65,7 @@ pip install --upgrade huggingface_hub आप [`login`] कमांड का उपयोग करके टर्मिनल से ऐसा कर सकते हैं: ```bash -huggingface-cli login +hf auth login ``` कमांड आपको बताएगा कि क्या आप पहले से लॉग इन हैं और आपसे आपके टोकन के लिए पूछेगा। फिर टोकन को मान्य किया जाता है और आपकी `HF_HOME` निर्देशिका (डिफ़ॉल्ट रूप से `~/.cache/huggingface/token`) में सहेजा जाता है। @@ -79,7 +79,7 @@ huggingface-cli login ``` आप एक समय में केवल एक ही खाते में लॉग इन कर सकते हैं। नए खाते में लॉग इन करने से आप स्वचालित रूप से पिछले खाते से लॉग आउट हो जाएंगे। -अपने वर्तमान में सक्रिय खाते को निर्धारित करने के लिए, बस `huggingface-cli whoami` कमांड चलाएँ। +अपने वर्तमान में सक्रिय खाते को निर्धारित करने के लिए, बस `hf auth whoami` कमांड चलाएँ। @@ -92,12 +92,12 @@ huggingface-cli login आप प्रत्येक टोकन के साथ [`login`] कमांड से लॉग इन करके अपनी मशीन पर कई टोकन सहेज सकते हैं। यदि आपको इन टोकन के बीच स्थानीय रूप से स्विच करने की आवश्यकता है, तो आप [auth switch] कमांड का उपयोग कर सकते हैं: ```bash -huggingface-cli auth switch +hf auth switch ``` यह कमांड आपको सहेजे गए टोकन की सूची से उसके नाम से एक टोकन चुनने के लिए कहेगा। एक बार चुने जाने के बाद, चुना गया टोकन `_active_` टोकन बन जाता है, और इसका उपयोग हब के साथ सभी इंटरैक्शन के लिए किया जाएगा। -आप `huggingface-cli auth list` के साथ अपनी मशीन पर उपलब्ध सभी एक्सेस टोकन सूचीबद्ध कर सकते हैं। +आप `hf auth list` के साथ अपनी मशीन पर उपलब्ध सभी एक्सेस टोकन सूचीबद्ध कर सकते हैं। ### पर्यावरण चर diff --git a/docs/source/ko/guides/cli.md b/docs/source/ko/guides/cli.md index 13c4352235..71f03095dd 100644 --- a/docs/source/ko/guides/cli.md +++ b/docs/source/ko/guides/cli.md @@ -4,7 +4,7 @@ rendered properly in your Markdown viewer. # 명령줄 인터페이스 (CLI) [[command-line-interface]] -`huggingface_hub` Python 패키지는 `huggingface-cli`라는 내장 CLI를 함께 제공합니다. 이 도구를 사용하면 터미널에서 Hugging Face Hub와 직접 상호 작용할 수 있습니다. 계정에 로그인하고, 리포지토리를 생성하고, 파일을 업로드 및 다운로드하는 등의 다양한 작업을 수행할 수 있습니다. 또한 머신을 구성하거나 캐시를 관리하는 데 유용한 기능도 제공합니다. 이 가이드는 CLI의 주요 기능과 사용 방법에 관해 설명합니다. +`huggingface_hub` Python 패키지는 `hf`라는 내장 CLI를 함께 제공합니다. 이 도구를 사용하면 터미널에서 Hugging Face Hub와 직접 상호 작용할 수 있습니다. 계정에 로그인하고, 리포지토리를 생성하고, 파일을 업로드 및 다운로드하는 등의 다양한 작업을 수행할 수 있습니다. 또한 머신을 구성하거나 캐시를 관리하는 데 유용한 기능도 제공합니다. 이 가이드는 CLI의 주요 기능과 사용 방법에 관해 설명합니다. ## 시작하기 [[getting-started]] @@ -16,40 +16,39 @@ rendered properly in your Markdown viewer. -위의 코드에서 사용자 경험을 높이기 위해 `[cli]` 추가 종속성을 포함하였습니다. 이는 `delete-cache` 명령을 사용할 때 특히 유용합니다. +위의 코드에서 사용자 경험을 높이기 위해 `[cli]` 추가 종속성을 포함하였습니다. 이는 `cache delete` 명령을 사용할 때 특히 유용합니다. 설치가 완료되면, CLI가 올바르게 설정되었는지 확인할 수 있습니다: ``` ->>> huggingface-cli --help -usage: huggingface-cli [] +>>> hf --help +usage: hf [] positional arguments: - {env,login,whoami,logout,repo,upload,download,lfs-enable-largefiles,lfs-multipart-upload,scan-cache,delete-cache} - huggingface-cli command helpers - env Print information about the environment. - login Log in using a token from huggingface.co/settings/tokens - whoami Find out which huggingface.co account you are logged in as. - logout Log out - repo {create} Commands to interact with your huggingface.co repos. - upload Upload a file or a folder to a repo on the Hub + {auth,cache,download,repo,repo-files,upload,upload-large-folder,env,version,lfs-enable-largefiles,lfs-multipart-upload} + hf command helpers + auth Manage authentication (login, logout, etc.). + cache Manage local cache directory. download Download files from the Hub - lfs-enable-largefiles - Configure your repository to enable upload of files > 5GB. - scan-cache Scan cache directory. - delete-cache Delete revisions from the cache directory. + repo Manage repos on the Hub. + repo-files Manage files in a repo on the Hub. + upload Upload a file or a folder to the Hub. Recommended for single-commit uploads. + upload-large-folder + Upload a large folder to the Hub. Recommended for resumable uploads. + env Print information about the environment. + version Print information about the hf version. options: -h, --help show this help message and exit ``` -CLI가 제대로 설치되었다면 CLI에서 사용 가능한 모든 옵션 목록이 출력됩니다. `command not found: huggingface-cli`와 같은 오류 메시지가 표시된다면 [설치](../installation) 가이드를 확인하세요. +CLI가 제대로 설치되었다면 CLI에서 사용 가능한 모든 옵션 목록이 출력됩니다. `command not found: hf`와 같은 오류 메시지가 표시된다면 [설치](../installation) 가이드를 확인하세요. -`--help` 옵션을 사용하면 명령어에 대한 자세한 정보를 얻을 수 있습니다. 언제든지 사용 가능한 모든 옵션과 그 세부 사항을 확인할 수 있습니다. 예를 들어 `huggingface-cli upload --help`는 CLI를 사용하여 파일을 업로드하는 구체적인 방법을 알려줍니다. +`--help` 옵션을 사용하면 명령어에 대한 자세한 정보를 얻을 수 있습니다. 언제든지 사용 가능한 모든 옵션과 그 세부 사항을 확인할 수 있습니다. 예를 들어 `hf upload --help`는 CLI를 사용하여 파일을 업로드하는 구체적인 방법을 알려줍니다. @@ -81,13 +80,13 @@ pkgx huggingface에 대한 자세한 내용은 [여기](https://pkgx.dev/pkgs/hu Homebrew huggingface에 대한 자세한 내용은 [여기](https://formulae.brew.sh/formula/huggingface-cli)에서 확인할 수 있습니다. -## huggingface-cli login [[huggingface-cli-login]] +## hf auth login [[hf-login]] Hugging Face Hub에 접근하는 대부분의 작업(비공개 리포지토리 액세스, 파일 업로드, PR 제출 등)을 위해서는 Hugging Face 계정에 로그인해야 합니다. 로그인을 하기 위해서 [설정 페이지](https://huggingface.co/settings/tokens)에서 생성한 [사용자 액세스 토큰](https://huggingface.co/docs/hub/security-tokens)이 필요하며, 이 토큰은 Hub에서의 사용자 인증에 사용됩니다. 파일 업로드나 콘텐츠 수정을 위해선 쓰기 권한이 있는 토큰이 필요합니다. 토큰을 받은 후에 터미널에서 다음 명령을 실행하세요: ```bash ->>> huggingface-cli login +>>> hf auth login ``` 이 명령은 토큰을 입력하라는 메시지를 표시합니다. 토큰을 복사하여 붙여넣고 Enter 키를 입력합니다. 그런 다음 토큰을 git 자격 증명으로 저장할지 묻는 메시지가 표시됩니다. 로컬에서 `git`을 사용할 계획이라면 Enter 키를 입력합니다(기본값은 yes). 마지막으로 Hub에서 토큰의 유효성을 검증한 후 로컬에 저장합니다. @@ -112,7 +111,7 @@ Login successful ```bash # Or using an environment variable ->>> huggingface-cli login --token $HUGGINGFACE_TOKEN --add-to-git-credential +>>> hf auth login --token $HUGGINGFACE_TOKEN --add-to-git-credential Token is valid (permission: write). Your token has been saved in your configured git credential helpers (store). Your token has been saved to /home/wauplin/.cache/huggingface/token @@ -121,31 +120,31 @@ Login successful [이 단락](../quick-start#authentication)에서 인증에 대한 더 자세한 내용을 확인할 수 있습니다. -## huggingface-cli whoami [[huggingface-cli-whoami]] +## hf auth whoami [[hf-whoami]] -로그인 여부를 확인하기 위해 `huggingface-cli whoami` 명령어를 사용할 수 있습니다. 이 명령어는 옵션이 없으며, 간단하게 사용자 이름과 소속된 조직들을 출력합니다: +로그인 여부를 확인하기 위해 `hf auth whoami` 명령어를 사용할 수 있습니다. 이 명령어는 옵션이 없으며, 간단하게 사용자 이름과 소속된 조직들을 출력합니다: ```bash -huggingface-cli whoami +hf auth whoami Wauplin orgs: huggingface,eu-test,OAuthTesters,hf-accelerate,HFSmolCluster ``` 로그인하지 않은 경우 오류 메시지가 출력됩니다. -## huggingface-cli logout [[huggingface-cli-logout]] +## hf auth logout [[hf-auth-logout]] 이 명령어를 사용하여 로그아웃할 수 있습니다. 실제로는 컴퓨터에 저장된 토큰을 삭제합니다. 하지만 `HF_TOKEN` 환경 변수를 사용하여 로그인했다면, 이 명령어로는 로그아웃할 수 없습니다([참조]((../package_reference/environment_variables#hftoken))). 대신 컴퓨터의 환경 설정에서 `HF_TOKEN` 변수를 제거하면 됩니다. -## huggingface-cli download [[huggingface-cli-download]] +## hf download [[hf-download]] -`huggingface-cli download` 명령어를 사용하여 Hub에서 직접 파일을 다운로드할 수 있습니다. [다운로드](./download) 가이드에서 설명된 [`hf_hub_download`], [`snapshot_download`] 헬퍼 함수를 사용하여 반환된 경로를 터미널에 출력합니다. 우리는 아래 예시에서 가장 일반적인 사용 사례를 살펴볼 것입니다. 사용 가능한 모든 옵션을 보려면 아래 명령어를 실행해보세요: +`hf download` 명령어를 사용하여 Hub에서 직접 파일을 다운로드할 수 있습니다. [다운로드](./download) 가이드에서 설명된 [`hf_hub_download`], [`snapshot_download`] 헬퍼 함수를 사용하여 반환된 경로를 터미널에 출력합니다. 우리는 아래 예시에서 가장 일반적인 사용 사례를 살펴볼 것입니다. 사용 가능한 모든 옵션을 보려면 아래 명령어를 실행해보세요: ```bash -huggingface-cli download --help +hf download --help ``` ### 파일 한 개 다운로드하기 [[download-a-single-file]] @@ -153,7 +152,7 @@ huggingface-cli download --help 리포지토리에서 파일 하나를 다운로드하고 싶다면, repo_id와 다운받고 싶은 파일명을 아래와 같이 입력하세요: ```bash ->>> huggingface-cli download gpt2 config.json +>>> hf download gpt2 config.json downloading https://huggingface.co/gpt2/resolve/main/config.json to /home/wauplin/.cache/huggingface/hub/tmpwrq8dm5o (…)ingface.co/gpt2/resolve/main/config.json: 100%|██████████████████████████████████| 665/665 [00:00<00:00, 2.49MB/s] /home/wauplin/.cache/huggingface/hub/models--gpt2/snapshots/11c5a3d5811f50298f278a704980280950aedb10/config.json @@ -166,7 +165,7 @@ downloading https://huggingface.co/gpt2/resolve/main/config.json to /home/waupli 리포지토리의 모든 파일을 다운로드하고 싶을 때에는 repo id만 입력하면 됩니다: ```bash ->>> huggingface-cli download HuggingFaceH4/zephyr-7b-beta +>>> hf download HuggingFaceH4/zephyr-7b-beta Fetching 23 files: 0%| | 0/23 [00:00>> huggingface-cli download gpt2 config.json model.safetensors +>>> hf download gpt2 config.json model.safetensors Fetching 2 files: 0%| | 0/2 [00:00>> huggingface-cli download stabilityai/stable-diffusion-xl-base-1.0 --include "*.safetensors" --exclude "*.fp16.*"* +>>> hf download stabilityai/stable-diffusion-xl-base-1.0 --include "*.safetensors" --exclude "*.fp16.*"* Fetching 8 files: 0%| | 0/8 [00:00>> huggingface-cli download HuggingFaceH4/ultrachat_200k --repo-type dataset +>>> hf download HuggingFaceH4/ultrachat_200k --repo-type dataset # https://huggingface.co/spaces/HuggingFaceH4/zephyr-chat ->>> huggingface-cli download HuggingFaceH4/zephyr-chat --repo-type space +>>> hf download HuggingFaceH4/zephyr-chat --repo-type space ... ``` @@ -216,7 +215,7 @@ Fetching 8 files: 100%|███████████████████ 따로 리비전을 지정하지 않는다면 기본적으로 main 브랜치의 최신 커밋에서 파일을 다운로드합니다. 특정 리비전(커밋 해시, 브랜치 이름 또는 태그)에서 다운로드하려면 `--revision` 옵션을 사용하세요: ```bash ->>> huggingface-cli download bigcode/the-stack --repo-type dataset --revision v1.1 +>>> hf download bigcode/the-stack --repo-type dataset --revision v1.1 ... ``` @@ -231,7 +230,7 @@ Hub에서 파일을 다운로드하는 권장되고 기본적인 방법은 캐 ```bash ->>> huggingface-cli download adept/fuyu-8b model-00001-of-00002.safetensors --local-dir . +>>> hf download adept/fuyu-8b model-00001-of-00002.safetensors --local-dir . ... ./model-00001-of-00002.safetensors ``` @@ -241,35 +240,35 @@ Hub에서 파일을 다운로드하는 권장되고 기본적인 방법은 캐 기본적으로 모든 파일은 `HF_HOME` [환경 변수](../package_reference/environment_variables#hfhome)에서 정의한 캐시 디렉터리에 다운로드됩니다. `--cache-dir`을 사용하여 직접 캐시 위치를 지정할 수 있습니다: ```bash ->>> huggingface-cli download adept/fuyu-8b --cache-dir ./path/to/cache +>>> hf download adept/fuyu-8b --cache-dir ./path/to/cache ... ./path/to/cache/models--adept--fuyu-8b/snapshots/ddcacbcf5fdf9cc59ff01f6be6d6662624d9c745 ``` ### 토큰 설정하기 [[specify-a-token]] -비공개 또는 접근이 제한된 리포지토리들에 접근하기 위해서는 토큰이 필요합니다. 기본적으로 로컬에 저장된 토큰(`huggingface-cli login`)이 사용됩니다. 직접 인증하고 싶다면 `--token` 옵션을 사용해보세요: +비공개 또는 접근이 제한된 리포지토리들에 접근하기 위해서는 토큰이 필요합니다. 기본적으로 로컬에 저장된 토큰(`hf auth login`)이 사용됩니다. 직접 인증하고 싶다면 `--token` 옵션을 사용해보세요: ```bash ->>> huggingface-cli download gpt2 config.json --token=hf_**** +>>> hf download gpt2 config.json --token=hf_**** /home/wauplin/.cache/huggingface/hub/models--gpt2/snapshots/11c5a3d5811f50298f278a704980280950aedb10/config.json ``` ### 조용한 모드 [[quiet-mode]] -`huggingface-cli download` 명령은 상세한 정보를 출력합니다. 경고 메시지, 다운로드된 파일 정보, 진행률 등이 포함됩니다. 이 모든 출력을 숨기려면 `--quiet` 옵션을 사용하세요. 이 옵션을 사용하면 다운로드된 파일의 경로가 표시되는 마지막 줄만 출력됩니다. 이 기능은 스크립트에서 다른 명령어로 출력을 전달하고자 할 때 유용할 수 있습니다. +`hf download` 명령은 상세한 정보를 출력합니다. 경고 메시지, 다운로드된 파일 정보, 진행률 등이 포함됩니다. 이 모든 출력을 숨기려면 `--quiet` 옵션을 사용하세요. 이 옵션을 사용하면 다운로드된 파일의 경로가 표시되는 마지막 줄만 출력됩니다. 이 기능은 스크립트에서 다른 명령어로 출력을 전달하고자 할 때 유용할 수 있습니다. ```bash ->>> huggingface-cli download gpt2 --quiet +>>> hf download gpt2 --quiet /home/wauplin/.cache/huggingface/hub/models--gpt2/snapshots/11c5a3d5811f50298f278a704980280950aedb10 ``` -## huggingface-cli upload [[huggingface-cli-upload]] +## hf upload [[hf-upload]] -`huggingface-cli upload` 명령어로 Hub에 직접 파일을 업로드할 수 있습니다. [업로드](./upload) 가이드에서 설명된 [`upload_file`], [`upload_folder`] 헬퍼 함수를 사용합니다. 우리는 아래 예시에서 가장 일반적인 사용 사례를 살펴볼 것입니다. 사용 가능한 모든 옵션을 보려면 아래 명령어를 실행해보세요: +`hf upload` 명령어로 Hub에 직접 파일을 업로드할 수 있습니다. [업로드](./upload) 가이드에서 설명된 [`upload_file`], [`upload_folder`] 헬퍼 함수를 사용합니다. 우리는 아래 예시에서 가장 일반적인 사용 사례를 살펴볼 것입니다. 사용 가능한 모든 옵션을 보려면 아래 명령어를 실행해보세요: ```bash ->>> huggingface-cli upload --help +>>> hf upload --help ``` ### 전체 폴더 업로드하기 [[upload-an-entire-folder]] @@ -277,13 +276,13 @@ Hub에서 파일을 다운로드하는 권장되고 기본적인 방법은 캐 이 명령어의 기본 사용법은 다음과 같습니다: ```bash -# Usage: huggingface-cli upload [repo_id] [local_path] [path_in_repo] +# Usage: hf upload [repo_id] [local_path] [path_in_repo] ``` 현재 디텍터리를 리포지토리의 루트 위치에 업로드하려면, 아래 명령어를 사용하세요: ```bash ->>> huggingface-cli upload my-cool-model . . +>>> hf upload my-cool-model . . https://huggingface.co/Wauplin/my-cool-model/tree/main/ ``` @@ -296,14 +295,14 @@ https://huggingface.co/Wauplin/my-cool-model/tree/main/ 또한, 특정 폴더만 업로드하는 것도 가능합니다: ```bash ->>> huggingface-cli upload my-cool-model ./models . +>>> hf upload my-cool-model ./models . https://huggingface.co/Wauplin/my-cool-model/tree/main/ ``` 마지막으로, 리포지토리의 특정 위치에 폴더를 업로드할 수 있습니다: ```bash ->>> huggingface-cli upload my-cool-model ./path/to/curated/data /data/train +>>> hf upload my-cool-model ./path/to/curated/data /data/train https://huggingface.co/Wauplin/my-cool-model/tree/main/data/train ``` @@ -312,14 +311,14 @@ https://huggingface.co/Wauplin/my-cool-model/tree/main/data/train 컴퓨터에 있는 파일을 가리키도록 `local_path`를 설정함으로써 파일 한 개를 업로드할 수 있습니다. 이때, `path_in_repo`는 선택사항이며 로컬 파일 이름을 기본값으로 사용합니다: ```bash ->>> huggingface-cli upload Wauplin/my-cool-model ./models/model.safetensors +>>> hf upload Wauplin/my-cool-model ./models/model.safetensors https://huggingface.co/Wauplin/my-cool-model/blob/main/model.safetensors ``` 파일 한 개를 특정 디렉터리에 업로드하고 싶다면, `path_in_repo`를 그에 맞게 설정하세요: ```bash ->>> huggingface-cli upload Wauplin/my-cool-model ./models/model.safetensors /vae/model.safetensors +>>> hf upload Wauplin/my-cool-model ./models/model.safetensors /vae/model.safetensors https://huggingface.co/Wauplin/my-cool-model/blob/main/vae/model.safetensors ``` @@ -329,7 +328,7 @@ https://huggingface.co/Wauplin/my-cool-model/blob/main/vae/model.safetensors ```bash # Sync local Space with Hub (upload new files except from logs/, delete removed files) ->>> huggingface-cli upload Wauplin/space-example --repo-type=space --exclude="/logs/*" --delete="*" --commit-message="Sync local Space with Hub" +>>> hf upload Wauplin/space-example --repo-type=space --exclude="/logs/*" --delete="*" --commit-message="Sync local Space with Hub" ... ``` @@ -338,7 +337,7 @@ https://huggingface.co/Wauplin/my-cool-model/blob/main/vae/model.safetensors 데이터 세트나 Space에 업로드하려면 `--repo-type` 옵션을 사용하세요: ```bash ->>> huggingface-cli upload Wauplin/my-cool-dataset ./data /train --repo-type=dataset +>>> hf upload Wauplin/my-cool-dataset ./data /train --repo-type=dataset ... ``` @@ -347,7 +346,7 @@ https://huggingface.co/Wauplin/my-cool-model/blob/main/vae/model.safetensors 개인 리포지토리 대신 조직이 소유한 리포지토리에 파일을 업로드하려면 `repo_id`를 입력해야 합니다: ```bash ->>> huggingface-cli upload MyCoolOrganization/my-cool-model . . +>>> hf upload MyCoolOrganization/my-cool-model . . https://huggingface.co/MyCoolOrganization/my-cool-model/tree/main/ ``` @@ -357,7 +356,7 @@ https://huggingface.co/MyCoolOrganization/my-cool-model/tree/main/ ```bash # Upload files to a PR ->>> huggingface-cli upload bigcode/the-stack . . --repo-type dataset --revision refs/pr/104 +>>> hf upload bigcode/the-stack . . --repo-type dataset --revision refs/pr/104 ... ``` @@ -369,7 +368,7 @@ https://huggingface.co/MyCoolOrganization/my-cool-model/tree/main/ ```bash # Create a PR and upload the files to it ->>> huggingface-cli upload bigcode/the-stack . . --repo-type dataset --revision refs/pr/104 +>>> hf upload bigcode/the-stack . . --repo-type dataset --revision refs/pr/104 https://huggingface.co/datasets/bigcode/the-stack/blob/refs%2Fpr%2F104/ ``` @@ -379,7 +378,7 @@ https://huggingface.co/datasets/bigcode/the-stack/blob/refs%2Fpr%2F104/ ```bash # Upload new logs every 10 minutes -huggingface-cli upload training-model logs/ --every=10 +hf upload training-model logs/ --every=10 ``` ### 커밋 메시지 지정하기 [[specify-a-commit-message]] @@ -387,36 +386,36 @@ huggingface-cli upload training-model logs/ --every=10 `--commit-message`와 `--commit-description`을 사용하여 기본 메시지 대신 사용자 지정 메시지와 설명을 커밋에 설정하세요: ```bash ->>> huggingface-cli upload Wauplin/my-cool-model ./models . --commit-message="Epoch 34/50" --commit-description="Val accuracy: 68%. Check tensorboard for more details." +>>> hf upload Wauplin/my-cool-model ./models . --commit-message="Epoch 34/50" --commit-description="Val accuracy: 68%. Check tensorboard for more details." ... https://huggingface.co/Wauplin/my-cool-model/tree/main ``` ### 토큰 지정하기 [[specify-a-token]] -파일을 업로드하려면 토큰이 필요합니다. 기본적으로 로컬에 저장된 토큰(`huggingface-cli login`)이 사용됩니다. 직접 인증하고 싶다면 `--token` 옵션을 사용해보세요: +파일을 업로드하려면 토큰이 필요합니다. 기본적으로 로컬에 저장된 토큰(`hf auth login`)이 사용됩니다. 직접 인증하고 싶다면 `--token` 옵션을 사용해보세요: ```bash ->>> huggingface-cli upload Wauplin/my-cool-model ./models . --token=hf_**** +>>> hf upload Wauplin/my-cool-model ./models . --token=hf_**** ... https://huggingface.co/Wauplin/my-cool-model/tree/main ``` ### 조용한 모드 [[quiet-mode]] -기본적으로 `huggingface-cli upload` 명령은 상세한 정보를 출력합니다. 경고 메시지, 업로드된 파일 정보, 진행률 등이 포함됩니다. 이 모든 출력을 숨기려면 `--quiet` 옵션을 사용하세요. 이 옵션을 사용하면 업로드된 파일의 URL이 표시되는 마지막 줄만 출력됩니다. 이 기능은 스크립트에서 다른 명령어로 출력을 전달하고자 할 때 유용할 수 있습니다. +기본적으로 `hf upload` 명령은 상세한 정보를 출력합니다. 경고 메시지, 업로드된 파일 정보, 진행률 등이 포함됩니다. 이 모든 출력을 숨기려면 `--quiet` 옵션을 사용하세요. 이 옵션을 사용하면 업로드된 파일의 URL이 표시되는 마지막 줄만 출력됩니다. 이 기능은 스크립트에서 다른 명령어로 출력을 전달하고자 할 때 유용할 수 있습니다. ```bash ->>> huggingface-cli upload Wauplin/my-cool-model ./models . --quiet +>>> hf upload Wauplin/my-cool-model ./models . --quiet https://huggingface.co/Wauplin/my-cool-model/tree/main ``` -## huggingface-cli scan-cache [[huggingface-cli-scan-cache]] +## hf cache scan [[hf-cache-scan]] -캐시 디렉토리를 스캔하여 다운로드한 리포지토리가 무엇인지와 디스크에서 차지하는 공간을 알 수 있습니다. `huggingface-cli scan-cache` 명령어를 사용하여 이를 확인해보세요: +캐시 디렉토리를 스캔하여 다운로드한 리포지토리가 무엇인지와 디스크에서 차지하는 공간을 알 수 있습니다. `hf cache scan` 명령어를 사용하여 이를 확인해보세요: ```bash ->>> huggingface-cli scan-cache +>>> hf cache scan REPO ID REPO TYPE SIZE ON DISK NB FILES LAST_ACCESSED LAST_MODIFIED REFS LOCAL PATH --------------------------- --------- ------------ -------- ------------- ------------- ------------------- ------------------------------------------------------------------------- glue dataset 116.3K 15 4 days ago 4 days ago 2.4.0, main, 1.17.0 /home/wauplin/.cache/huggingface/hub/datasets--glue @@ -432,16 +431,16 @@ Got 1 warning(s) while scanning. Use -vvv to print details. 캐시 디렉토리 스캔에 대한 자세한 내용을 알고 싶다면, [캐시 관리](./manage-cache#scan-cache-from-the-terminal) 가이드를 확인해보세요. -## huggingface-cli delete-cache [[huggingface-cli-delete-cache]] +## hf cache delete [[hf-cache-delete]] -사용하지 않는 캐시를 삭제하고 싶다면 `huggingface-cli delete-cache`를 사용해보세요. 이는 디스크 공간을 절약하고 확보하는 데 유용합니다. 이에 대한 자세한 내용은 [캐시 관리](./manage-cache#clean-cache-from-the-terminal) 가이드에서 확인할 수 있습니다. +사용하지 않는 캐시를 삭제하고 싶다면 `hf cache delete`를 사용해보세요. 이는 디스크 공간을 절약하고 확보하는 데 유용합니다. 이에 대한 자세한 내용은 [캐시 관리](./manage-cache#clean-cache-from-the-terminal) 가이드에서 확인할 수 있습니다. -## huggingface-cli env [[huggingface-cli-env]] +## hf env [[hf-env]] -`huggingface-cli env` 명령어는 사용자의 컴퓨터 설정에 대한 상세한 정보를 보여줍니다. 이는 [GitHub](https://github.com/huggingface/huggingface_hub)에서 문제를 제출할 때, 관리자가 문제를 파악하고 해결하는 데 도움이 됩니다. +`hf env` 명령어는 사용자의 컴퓨터 설정에 대한 상세한 정보를 보여줍니다. 이는 [GitHub](https://github.com/huggingface/huggingface_hub)에서 문제를 제출할 때, 관리자가 문제를 파악하고 해결하는 데 도움이 됩니다. ```bash ->>> huggingface-cli env +>>> hf env Copy-and-paste the text below in your GitHub issue. diff --git a/docs/source/ko/guides/download.md b/docs/source/ko/guides/download.md index ac658dde4e..b5023de9e0 100644 --- a/docs/source/ko/guides/download.md +++ b/docs/source/ko/guides/download.md @@ -135,25 +135,25 @@ Hub에서 파일을 다운로드하는 가장 좋은 (그리고 기본적인) ## CLI에서 파일 다운로드하기[[download-from-the-cli]] -터미널에서 `huggingface-cli download` 명령어를 사용하면 Hub에서 파일을 바로 다운로드할 수 있습니다. +터미널에서 `hf download` 명령어를 사용하면 Hub에서 파일을 바로 다운로드할 수 있습니다. 이 명령어는 내부적으로 앞서 설명한 [`hf_hub_download`]과 [`snapshot_download`] 함수를 사용하고, 다운로드한 파일의 로컬 경로를 터미널에 출력합니다: ```bash ->>> huggingface-cli download gpt2 config.json +>>> hf download gpt2 config.json /home/wauplin/.cache/huggingface/hub/models--gpt2/snapshots/11c5a3d5811f50298f278a704980280950aedb10/config.json ``` -기본적으로 (`huggingface-cli login` 명령으로) 로컬에 저장된 토큰을 사용합니다. 직접 인증하고 싶다면, `--token` 옵션을 사용하세요: +기본적으로 (`hf auth login` 명령으로) 로컬에 저장된 토큰을 사용합니다. 직접 인증하고 싶다면, `--token` 옵션을 사용하세요: ```bash ->>> huggingface-cli download gpt2 config.json --token=hf_**** +>>> hf download gpt2 config.json --token=hf_**** /home/wauplin/.cache/huggingface/hub/models--gpt2/snapshots/11c5a3d5811f50298f278a704980280950aedb10/config.json ``` 여러 파일을 한 번에 다운로드하면 진행률 표시줄이 보이고, 파일이 있는 스냅샷 경로가 반환됩니다: ```bash ->>> huggingface-cli download gpt2 config.json model.safetensors +>>> hf download gpt2 config.json model.safetensors Fetching 2 files: 100%|████████████████████████████████████████████| 2/2 [00:00<00:00, 23831.27it/s] /home/wauplin/.cache/huggingface/hub/models--gpt2/snapshots/11c5a3d5811f50298f278a704980280950aedb10 ``` @@ -161,14 +161,14 @@ Fetching 2 files: 100%|███████████████████ 진행률 표시줄이나 잠재적 경고가 필요 없다면 `--quiet` 옵션을 사용하세요. 이 옵션은 스크립트에서 다른 명령어로 출력을 넘겨주려는 경우에 유용할 수 있습니다. ```bash ->>> huggingface-cli download gpt2 config.json model.safetensors +>>> hf download gpt2 config.json model.safetensors /home/wauplin/.cache/huggingface/hub/models--gpt2/snapshots/11c5a3d5811f50298f278a704980280950aedb10 ``` 기본적으로 파일은 `HF_HOME` 환경 변수에 정의된 캐시 디렉터리(또는 지정하지 않은 경우 `~/.cache/huggingface/hub`)에 다운로드됩니다. 캐시 디렉터리는 `--cache-dir` 옵션으로 변경할 수 있습니다: ```bash ->>> huggingface-cli download gpt2 config.json --cache-dir=./cache +>>> hf download gpt2 config.json --cache-dir=./cache ./cache/models--gpt2/snapshots/11c5a3d5811f50298f278a704980280950aedb10/config.json ``` @@ -177,7 +177,7 @@ Fetching 2 files: 100%|███████████████████ ```bash ->>> huggingface-cli download gpt2 config.json --local-dir=./models/gpt2 +>>> hf download gpt2 config.json --local-dir=./models/gpt2 ./models/gpt2/config.json ``` @@ -185,7 +185,7 @@ Fetching 2 files: 100%|███████████████████ 다른 리포지토리 유형이나 버전에서 파일을 다운로드하거나 glob 패턴을 사용하여 다운로드할 파일을 선택하거나 제외하도록 지정할 수 있는 인수들이 더 있습니다: ```bash ->>> huggingface-cli download bigcode/the-stack --repo-type=dataset --revision=v1.2 --include="data/python/*" --exclu +>>> hf download bigcode/the-stack --repo-type=dataset --revision=v1.2 --include="data/python/*" --exclu de="*.json" --exclude="*.zip" Fetching 206 files: 100%|████████████████████████████████████████████| 206/206 [02:31<2:31, ?it/s] /home/wauplin/.cache/huggingface/hub/datasets--bigcode--the-stack/snapshots/9ca8fa6acdbc8ce920a0cb58adcdafc495818ae7 @@ -194,5 +194,5 @@ Fetching 206 files: 100%|█████████████████ 인수들의 전체 목록을 보려면 다음 명령어를 실행하세요: ```bash -huggingface-cli download --help +hf download --help ``` diff --git a/docs/source/ko/guides/manage-cache.md b/docs/source/ko/guides/manage-cache.md index 7b54c7eaab..a1a8045d94 100644 --- a/docs/source/ko/guides/manage-cache.md +++ b/docs/source/ko/guides/manage-cache.md @@ -185,16 +185,16 @@ something_path = assets_path / "something.json" # 자산 폴더에서 원하는 ## 캐시 스캔하기[[scan-your-cache]] -현재 캐시된 파일은 로컬 디렉토리에서 삭제되지 않습니다. 브랜치의 새로운 수정 버전을 다운로드할 때 이전 파일은 다시 필요할 경우를 대비하여 보관됩니다. 따라서 디스크 공간을 많이 차지하는 리포지토리와 수정 버전을 파악하기 위해 캐시 디렉토리를 스캔하는 것이 유용할 수 있습니다. `huggingface_hub`은 이를 수행할 수 있는 헬퍼를 제공하며, `huggingface-cli`를 통해 또는 Python 스크립트에서 사용할 수 있습니다. +현재 캐시된 파일은 로컬 디렉토리에서 삭제되지 않습니다. 브랜치의 새로운 수정 버전을 다운로드할 때 이전 파일은 다시 필요할 경우를 대비하여 보관됩니다. 따라서 디스크 공간을 많이 차지하는 리포지토리와 수정 버전을 파악하기 위해 캐시 디렉토리를 스캔하는 것이 유용할 수 있습니다. `huggingface_hub`은 이를 수행할 수 있는 헬퍼를 제공하며, `hf`를 통해 또는 Python 스크립트에서 사용할 수 있습니다. ### 터미널에서 캐시 스캔하기[[scan-cache-from-the-terminal]] -HF 캐시 시스템을 스캔하는 가장 쉬운 방법은 `huggingface-cli` 도구의 `scan-cache` 명령을 사용하는 것입니다. 이 명령은 캐시를 스캔하고 리포지토리 ID, 리포지토리 유형, 디스크 사용량, 참조 및 전체 로컬 경로와 같은 정보가 포함된 보고서를 출력합니다. +HF 캐시 시스템을 스캔하는 가장 쉬운 방법은 `hf` 도구의 `cache scan` 명령을 사용하는 것입니다. 이 명령은 캐시를 스캔하고 리포지토리 ID, 리포지토리 유형, 디스크 사용량, 참조 및 전체 로컬 경로와 같은 정보가 포함된 보고서를 출력합니다. 아래 코드 조각은 4개의 모델과 2개의 데이터셋이 캐시된 폴더에서의 스캔 보고서를 보여줍니다. ```text -➜ huggingface-cli scan-cache +➜ hf cache scan REPO ID REPO TYPE SIZE ON DISK NB FILES LAST_ACCESSED LAST_MODIFIED REFS LOCAL PATH --------------------------- --------- ------------ -------- ------------- ------------- ------------------- ------------------------------------------------------------------------- glue dataset 116.3K 15 4 days ago 4 days ago 2.4.0, main, 1.17.0 /home/wauplin/.cache/huggingface/hub/datasets--glue @@ -210,7 +210,7 @@ Got 1 warning(s) while scanning. Use -vvv to print details. 더 자세한 보고서를 얻으려면 `--verbose` 옵션을 사용하세요. 각 리포지토리에 대해 다운로드된 모든 수정 버전의 목록을 얻게 됩니다. 위에서 설명한대로, 2개의 수정 버전 사이에 변경되지 않는 파일들은 심볼릭 링크를 통해 공유됩니다. 이는 디스크 상의 리포지토리 크기가 각 수정 버전의 크기의 합보다 작을 것으로 예상됨을 의미합니다. 예를 들어, 여기서 `bert-base-cased`는 1.4G와 1.5G의 두 가지 수정 버전이 있지만 총 디스크 사용량은 단 1.9G입니다. ```text -➜ huggingface-cli scan-cache -v +➜ hf cache scan -v REPO ID REPO TYPE REVISION SIZE ON DISK NB FILES LAST_MODIFIED REFS LOCAL PATH --------------------------- --------- ---------------------------------------- ------------ -------- ------------- ----------- ---------------------------------------------------------------------------------------------------------------------------- glue dataset 9338f7b671827df886678df2bdd7cc7b4f36dffd 97.7K 14 4 days ago main, 2.4.0 /home/wauplin/.cache/huggingface/hub/datasets--glue/snapshots/9338f7b671827df886678df2bdd7cc7b4f36dffd @@ -234,7 +234,7 @@ Got 1 warning(s) while scanning. Use -vvv to print details. 출력이 테이블 형식으로 되어 있기 때문에 `grep`과 유사한 도구를 사용하여 항목을 필터링할 수 있습니다. 여기에는 Unix 기반 머신에서 "t5-small" 모델의 수정 버전만 필터링하는 예제가 있습니다. ```text -➜ eval "huggingface-cli scan-cache -v" | grep "t5-small" +➜ eval "hf cache scan -v" | grep "t5-small" t5-small model 98ffebbb27340ec1b1abd7c45da12c253ee1882a 726.2M 6 1 week ago refs/pr/1 /home/wauplin/.cache/huggingface/hub/models--t5-small/snapshots/98ffebbb27340ec1b1abd7c45da12c253ee1882a t5-small model d0a119eedb3718e34c648e594394474cf95e0617 485.8M 6 4 weeks ago /home/wauplin/.cache/huggingface/hub/models--t5-small/snapshots/d0a119eedb3718e34c648e594394474cf95e0617 t5-small model d78aea13fa7ecd06c29e3e46195d6341255065d5 970.7M 9 1 week ago main /home/wauplin/.cache/huggingface/hub/models--t5-small/snapshots/d78aea13fa7ecd06c29e3e46195d6341255065d5 @@ -329,7 +329,7 @@ HFCacheInfo( ### 터미널에서 캐시 정리하기[[clean-cache-from-the-terminal]] -HF 캐시 시스템에서 일부 수정 버전을 삭제하는 가장 쉬운 방법은 `huggingface-cli` 도구의 `delete-cache` 명령을 사용하는 것입니다. 이 명령에는 두 가지 모드가 있습니다. 기본적으로 사용자에게 삭제할 수정 버전을 선택하도록 TUI(터미널 사용자 인터페이스)가 표시됩니다. 이 TUI는 현재 베타 버전으로, 모든 플랫폼에서 테스트되지 않았습니다. 만약 TUI가 작동하지 않는다면 `--disable-tui` 플래그를 사용하여 비활성화할 수 있습니다. +HF 캐시 시스템에서 일부 수정 버전을 삭제하는 가장 쉬운 방법은 `hf` 도구의 `cache delete` 명령을 사용하는 것입니다. 이 명령에는 두 가지 모드가 있습니다. 기본적으로 사용자에게 삭제할 수정 버전을 선택하도록 TUI(터미널 사용자 인터페이스)가 표시됩니다. 이 TUI는 현재 베타 버전으로, 모든 플랫폼에서 테스트되지 않았습니다. 만약 TUI가 작동하지 않는다면 `--disable-tui` 플래그를 사용하여 비활성화할 수 있습니다. #### TUI 사용하기[[using-the-tui]] @@ -342,7 +342,7 @@ pip install huggingface_hub["cli"] 그러고 명령어를 실행합니다: ``` -huggingface-cli delete-cache +hf cache delete ``` 이제 선택/해제할 수 있는 수정 버전 목록이 표시됩니다: @@ -359,7 +359,7 @@ huggingface-cli delete-cache 삭제할 수정 버전을 선택하고 `enter` 를 누르면 마지막 확인 메시지가 표시됩니다. 다시 `enter` 를 누르면 삭제됩니다. 취소하려면 `n` 을 입력하세요. ```txt -✗ huggingface-cli delete-cache --dir ~/.cache/huggingface/hub +✗ hf cache delete --dir ~/.cache/huggingface/hub ? Select revisions to delete: 2 revision(s) selected. ? 2 revisions selected counting for 3.1G. Confirm deletion ? Yes Start deletion. @@ -375,7 +375,7 @@ Done. Deleted 1 repo(s) and 0 revision(s) for a total of 3.1G. 이 파일에는 헤더에 필요한 모든 사용방법이 포함되어 있습니다. 텍스트 편집기에서 이 파일을 열어 `#`으로 주석 처리/해제하면 수정 버전을 쉽게 선택/해제 할 수 있습니다. 검토를 완료하고 파일 편집이 완료되었다면 터미널로 돌아가 ``를 눌러 파일을 저장하세요. 기본적으로 업데이트된 수정 버전 목록으로 확보될 공간의 양을 계산합니다. 파일을 계속 편집할 수도 있고, `"y"`를 눌러 변경 사항을 확정할 수 있습니다. ```sh -huggingface-cli delete-cache --disable-tui +hf cache delete --disable-tui ``` Example of command file: @@ -383,7 +383,7 @@ Example of command file: ```txt # INSTRUCTIONS # ------------ -# This is a temporary file created by running `huggingface-cli delete-cache` with the +# This is a temporary file created by running `hf cache delete` with the # `--disable-tui` option. It contains a set of revisions that can be deleted from your # local cache directory. # diff --git a/docs/source/ko/guides/model-cards.md b/docs/source/ko/guides/model-cards.md index 41bc4f1317..6d9b1f1396 100644 --- a/docs/source/ko/guides/model-cards.md +++ b/docs/source/ko/guides/model-cards.md @@ -188,7 +188,7 @@ print(card) ## 모델 카드 공유하기[[share-model-cards]] -Hugging Face Hub로 인증받은 경우(`huggingface-cli login` 또는 [`login`] 사용) 간단히 [`ModelCard.push_to_hub`]를 호출하여 카드를 Hub에 푸시할 수 있습니다. 이를 수행하는 방법을 살펴보겠습니다. +Hugging Face Hub로 인증받은 경우(`hf auth login` 또는 [`login`] 사용) 간단히 [`ModelCard.push_to_hub`]를 호출하여 카드를 Hub에 푸시할 수 있습니다. 이를 수행하는 방법을 살펴보겠습니다. 먼저 인증된 사용자의 네임스페이스 아래에 'hf-hub-modelcards-pr-test'라는 새로운 레포지토리를 만듭니다: diff --git a/docs/source/ko/guides/upload.md b/docs/source/ko/guides/upload.md index fd704d0ba6..4f61f81faf 100644 --- a/docs/source/ko/guides/upload.md +++ b/docs/source/ko/guides/upload.md @@ -82,16 +82,16 @@ Hub에 파일을 업로드 하려면 허깅페이스 계정으로 로그인해 ## CLI에서 업로드[[upload-from-the-cli]] -터미널에서 `huggingface-cli upload` 명령어를 사용하여 Hub에 파일을 직접 업로드할 수 있습니다. 내부적으로는 위에서 설명한 것과 동일한 [`upload_file`] 와 [`upload_folder`] 함수를 사용합니다. +터미널에서 `hf upload` 명령어를 사용하여 Hub에 파일을 직접 업로드할 수 있습니다. 내부적으로는 위에서 설명한 것과 동일한 [`upload_file`] 와 [`upload_folder`] 함수를 사용합니다. 다음과 같이 단일 파일 또는 전체 폴더를 업로드할 수 있습니다: ```bash -# 사용례: huggingface-cli upload [repo_id] [local_path] [path_in_repo] ->>> huggingface-cli upload Wauplin/my-cool-model ./models/model.safetensors model.safetensors +# 사용례: hf upload [repo_id] [local_path] [path_in_repo] +>>> hf upload Wauplin/my-cool-model ./models/model.safetensors model.safetensors https://huggingface.co/Wauplin/my-cool-model/blob/main/model.safetensors ->>> huggingface-cli upload Wauplin/my-cool-model ./models . +>>> hf upload Wauplin/my-cool-model ./models . https://huggingface.co/Wauplin/my-cool-model/tree/main ``` @@ -100,7 +100,7 @@ https://huggingface.co/Wauplin/my-cool-model/tree/main 같은 이름의 폴더나 파일을 찾지 못한다면 사용자에게 `local_path`를 명시하도록 요청하는 예외 처리가 발생합니다. 어떤 경우든 `path_in_repo`가 설정되어 있지 않으면 파일이 리포지토리의 루트 디렉터리에 업로드됩니다. -CLI 업로드 명령어에 대한 자세한 내용은 [CLI 가이드](./cli#huggingface-cli-upload)를 참조하세요. +CLI 업로드 명령어에 대한 자세한 내용은 [CLI 가이드](./cli#hf-upload)를 참조하세요. ## 고급 기능[[advanced-features]] @@ -451,7 +451,7 @@ Hub에서 리포지토리를 구성하는 방법에 대한 모범 사례는 [리 Git LFS는 10MB보다 큰 파일을 자동으로 처리합니다. 하지만 매우 큰 파일(5GB 이상)의 경우, Git LFS용 사용자 지정 전송 에이전트를 설치해야 합니다: ```bash -huggingface-cli lfs-enable-largefiles +hf lfs-enable-largefiles ``` 매우 큰 파일이 있는 각 리포지토리에 대해 이 옵션을 설치해야 합니다. diff --git a/docs/source/ko/package_reference/environment_variables.md b/docs/source/ko/package_reference/environment_variables.md index a4d26123e4..124ca04d75 100644 --- a/docs/source/ko/package_reference/environment_variables.md +++ b/docs/source/ko/package_reference/environment_variables.md @@ -145,7 +145,7 @@ Hugging Face 생태계의 모든 환경 변수를 표준화하기 위해 일부 ### NO_COLOR[[nocolor]] -불리언 값입니다. 이 값을 설정하면 `huggingface-cli` 도구는 ANSI 색상을 출력하지 않습니다. [no-color.org](https://no-color.org/)를 참조하세요. +불리언 값입니다. 이 값을 설정하면 `hf` 도구는 ANSI 색상을 출력하지 않습니다. [no-color.org](https://no-color.org/)를 참조하세요. ### XDG_CACHE_HOME[[xdgcachehome]] diff --git a/docs/source/ko/package_reference/hf_api.md b/docs/source/ko/package_reference/hf_api.md index f3363fce61..673bc21e8b 100644 --- a/docs/source/ko/package_reference/hf_api.md +++ b/docs/source/ko/package_reference/hf_api.md @@ -10,7 +10,7 @@ rendered properly in your Markdown viewer. 루트 메서드를 사용하는 것이 더 간단하지만 [`HfApi`] 클래스를 사용하면 더 유연하게 사용할 수 있습니다. 특히 모든 HTTP 호출에서 재사용할 토큰을 전달할 수 있습니다. -이 방식은 토큰이 머신에 유지되지 않기 때문에 `huggingface-cli login` 또는 [`login`]를 사용하는 방식과는 다르며, +이 방식은 토큰이 머신에 유지되지 않기 때문에 `hf auth login` 또는 [`login`]를 사용하는 방식과는 다르며, 다른 엔드포인트를 제공하거나 사용자정의 에이전트를 구성할 수도 있습니다. ```python diff --git a/docs/source/ko/quick-start.md b/docs/source/ko/quick-start.md index 30e07331e3..55c8f1ed6a 100644 --- a/docs/source/ko/quick-start.md +++ b/docs/source/ko/quick-start.md @@ -49,9 +49,9 @@ Hub의 리포지토리는 git으로 버전 관리되며, 사용자는 단일 파 사용자 액세스 토큰을 받으면 터미널에서 다음 명령을 실행하세요: ```bash -huggingface-cli login +hf auth login # or using an environment variable -huggingface-cli login --token $HUGGINGFACE_TOKEN +hf auth login --token $HUGGINGFACE_TOKEN ``` 또는 주피터 노트북이나 스크립트에서 [`login`]로 프로그래밍 방식으로 로그인할 수도 있습니다: @@ -63,7 +63,7 @@ huggingface-cli login --token $HUGGINGFACE_TOKEN `login(token="hf_xxx")`과 같이 토큰을 [`login`]에 직접 전달하여 토큰을 입력하라는 메시지를 표시하지 않고 프로그래밍 방식으로 로그인할 수도 있습니다. 이렇게 한다면 소스 코드를 공유할 때 주의하세요. 토큰을 소스코드에 명시적으로 저장하는 대신에 보안 저장소에서 토큰을 가져오는 것이 가장 좋습니다. -한 번에 하나의 계정에만 로그인할 수 있습니다. 새 계정으로 로그인하면 이전 계정에서 로그아웃됩니다. 항상 `huggingface-cli whoami` 명령으로 어떤 계정을 사용 중인지 확인하세요. +한 번에 하나의 계정에만 로그인할 수 있습니다. 새 계정으로 로그인하면 이전 계정에서 로그아웃됩니다. 항상 `hf auth whoami` 명령으로 어떤 계정을 사용 중인지 확인하세요. 동일한 스크립트에서 여러 계정을 처리하려면 각 메서드를 호출할 때 토큰을 제공하면 됩니다. 이 방법은 머신에 토큰을 저장하지 않으려는 경우에도 유용합니다. diff --git a/i18n/README_cn.md b/i18n/README_cn.md index 4d657011ea..c019b21383 100644 --- a/i18n/README_cn.md +++ b/i18n/README_cn.md @@ -97,9 +97,9 @@ snapshot_download("stabilityai/stable-diffusion-2-1") Hugging Face Hub 使用令牌对应用进行身份验证(请参阅[文档](https://huggingface.co/docs/hub/security-tokens)). 要登录您的机器,请运行以下命令行: ```bash -huggingface-cli login +hf auth login # or using an environment variable -huggingface-cli login --token $HUGGINGFACE_TOKEN +hf auth login --token $HUGGINGFACE_TOKEN ``` ### 创建一个存储库 diff --git a/i18n/README_de.md b/i18n/README_de.md index 6259581008..85f70b872a 100644 --- a/i18n/README_de.md +++ b/i18n/README_de.md @@ -96,9 +96,9 @@ Dateien werden in einen lokalen Cache-Ordner heruntergeladen. Weitere Details fi Der Hugging Face Hub verwendet Tokens zur Authentifizierung von Anwendungen (siehe [Dokumentation](https://huggingface.co/docs/hub/security-tokens)). Um sich an Ihrem Computer anzumelden, führen Sie das folgende Kommando in der Befehlszeile aus: ```bash -huggingface-cli login +hf auth login # oder mit einer Umgebungsvariablen -huggingface-cli login --token $HUGGINGFACE_TOKEN +hf auth login --token $HUGGINGFACE_TOKEN ``` ### Eine Repository erstellen diff --git a/i18n/README_hi.md b/i18n/README_hi.md index 2d33fa0816..6b68a91818 100644 --- a/i18n/README_hi.md +++ b/i18n/README_hi.md @@ -97,9 +97,9 @@ snapshot_download("stabilityai/stable-diffusion-2-1") Hugging Face Hub एप्लिकेशन को प्रमाणित करने के लिए टोकन का उपयोग करता है (देखें [docs](https://huggingface.co/docs/hub/security-tokens))। अपनी मशीन में लॉगिन करने के लिए, निम्नलिखित सीएलआई चलाएँ: ```bash -huggingface-cli login +hf auth login # या कृपया इसे एक पर्यावरण चर के रूप में निर्दिष्ट करें। -huggingface-cli login --token $HUGGINGFACE_TOKEN +hf auth login --token $HUGGINGFACE_TOKEN ``` ### एक रिपॉजिटरी बनाएं diff --git a/i18n/README_ko.md b/i18n/README_ko.md index bf2fe8fed5..c55158dda6 100644 --- a/i18n/README_ko.md +++ b/i18n/README_ko.md @@ -97,9 +97,9 @@ snapshot_download("stabilityai/stable-diffusion-2-1") Hugging Face Hub는 토큰을 사용하여 애플리케이션을 인증합니다([문서](https://huggingface.co/docs/hub/security-tokens) 참조). 컴퓨터에서 로그인하려면 CLI를 사용하세요: ```bash -huggingface-cli login +hf auth login # 또는 환경 변수로 지정해주세요 -huggingface-cli login --token $HUGGINGFACE_TOKEN +hf auth login --token $HUGGINGFACE_TOKEN ``` ### 레포지토리 생성 diff --git a/src/huggingface_hub/README.md b/src/huggingface_hub/README.md index 33237e3148..6ce8b5a41c 100644 --- a/src/huggingface_hub/README.md +++ b/src/huggingface_hub/README.md @@ -89,13 +89,13 @@ single file to a repo or listing models from the Hub, you'll find helpers in * `delete_file()` * `delete_folder()` -Those API utilities are also exposed through the `huggingface-cli` CLI: +Those API utilities are also exposed through the `hf` CLI: ```bash -huggingface-cli login -huggingface-cli logout -huggingface-cli whoami -huggingface-cli repo create +hf auth login +hf auth logout +hf auth whoami +hr repo create ``` With the `HfApi` class there are methods to query models, datasets, and Spaces by specific tags (e.g. if you want to list models compatible with your library): @@ -143,21 +143,17 @@ will clone that repository: >>> repo = Repository("w2v2", clone_from="facebook/wav2vec2-large-960h-lv60") ``` -If the repository you're cloning is one of yours or one of your organisation's, -then having the ability to commit and push to that repository is important. In -order to do that, you should make sure to be logged-in using `huggingface-cli -login`, and to have the `token` parameter set to `True` (the default) -when instantiating the `Repository` object: +If the repository you're cloning is one of yours or one of your organisation's, then having the ability to commit and push to that repository is important. In order to do that, you should make sure to be logged-in using `hf auth login`,: ```python ->>> repo = Repository("my-model", clone_from="/", token=True) +>>> repo = Repository("my-model", clone_from="/") ``` This works for models, datasets and spaces repositories; but you will need to explicitely specify the type for the last two options: ```python ->>> repo = Repository("my-dataset", clone_from="/", token=True, repo_type="dataset") +>>> repo = Repository("my-dataset", clone_from="/", repo_type="dataset") ``` You can also change between branches: @@ -183,7 +179,6 @@ who will be the author of the commits: >>> repo = Repository( ... "my-dataset", ... clone_from="/", -... token=True, ... repo_type="dataset", ... git_user="MyName", ... git_email="me@cool.mail" @@ -226,7 +221,7 @@ These two methods also have support for the `blocking` parameter. Examples using the `commit` context manager: ```python ->>> with Repository("text-files", clone_from="/text-files", token=True).commit("My first file :)"): +>>> with Repository("text-files", clone_from="/text-files").commit("My first file :)"): ... with open("file.txt", "w+") as f: ... f.write(json.dumps({"hey": 8})) ``` @@ -234,7 +229,7 @@ Examples using the `commit` context manager: ```python >>> import torch >>> model = torch.nn.Transformer() ->>> with Repository("torch-model", clone_from="/torch-model", token=True).commit("My cool model :)"): +>>> with Repository("torch-model", clone_from="/torch-model").commit("My cool model :)"): ... torch.save(model.state_dict(), "model.pt") ``` @@ -287,14 +282,14 @@ for git-lfs, bundled in this package. To install, just run: ```bash -$ huggingface-cli lfs-enable-largefiles +$ hf lfs-enable-largefiles . ``` This should be executed once for each model repo that contains a model file >5GB. If you just try to push a file bigger than 5GB without running that command, you will get an error with a message reminding you to run it. -Finally, there's a `huggingface-cli lfs-multipart-upload` command but that one +Finally, there's a `hf lfs-multipart-upload` command but that one is internal (called by lfs directly) and is not meant to be called by the user.
diff --git a/src/huggingface_hub/_login.py b/src/huggingface_hub/_login.py index b14702201d..303cd2b35d 100644 --- a/src/huggingface_hub/_login.py +++ b/src/huggingface_hub/_login.py @@ -75,7 +75,7 @@ def login( components. If `token` is not provided, it will be prompted to the user either with a widget (in a notebook) or via the terminal. - To log in from outside of a script, one can also use `huggingface-cli login` which is + To log in from outside of a script, one can also use `hf auth login` which is a cli command that wraps [`login`]. @@ -120,7 +120,7 @@ def login( logger.info( "The token has not been saved to the git credentials helper. Pass " "`add_to_git_credential=True` in this function directly or " - "`--add-to-git-credential` if using via `huggingface-cli` if " + "`--add-to-git-credential` if using via `hf`CLI if " "you want to set the git credential as well." ) _login(token, add_to_git_credential=add_to_git_credential) @@ -233,7 +233,7 @@ def auth_list() -> None: ) elif current_token_name is None: logger.warning( - "\nNote: No active token is set and no environment variable `HF_TOKEN` is found. Use `huggingface-cli login` to log in." + "\nNote: No active token is set and no environment variable `HF_TOKEN` is found. Use `hf auth login` to log in." ) @@ -273,8 +273,8 @@ def interpreter_login(*, new_session: bool = True, write_permission: bool = Fals print(_HF_LOGO_ASCII) if get_token() is not None: logger.info( - " A token is already saved on your machine. Run `huggingface-cli" - " whoami` to get more information or `huggingface-cli logout` if you want" + " A token is already saved on your machine. Run `hf auth whoami`" + " to get more information or `hf auth logout` if you want" " to log out." ) logger.info(" Setting a new token will erase the existing one.") diff --git a/src/huggingface_hub/_oauth.py b/src/huggingface_hub/_oauth.py index aaf7e826c7..30642a43dd 100644 --- a/src/huggingface_hub/_oauth.py +++ b/src/huggingface_hub/_oauth.py @@ -415,7 +415,7 @@ def _get_mocked_oauth_info() -> Dict: if token is None: raise ValueError( "Your machine must be logged in to HF to debug an OAuth app locally. Please" - " run `huggingface-cli login` or set `HF_TOKEN` as environment variable " + " run `hf auth login` or set `HF_TOKEN` as environment variable " "with one of your access token. You can generate a new token in your " "settings page (https://huggingface.co/settings/tokens)." ) diff --git a/src/huggingface_hub/cli/repo.py b/src/huggingface_hub/cli/repo.py index 64c80617aa..2f532b3015 100644 --- a/src/huggingface_hub/cli/repo.py +++ b/src/huggingface_hub/cli/repo.py @@ -81,20 +81,6 @@ def register_subcommand(parser: _SubParsersAction): type=str, help="Resource group in which to create the repo. Resource groups is only available for Enterprise Hub organizations.", ) - repo_create_parser.add_argument( - "--type", - type=str, - help="[Deprecated]: use --repo-type instead.", - ) - repo_create_parser.add_argument( - "-y", - "--yes", - action="store_true", - help="[Deprecated] no effect.", - ) - repo_create_parser.add_argument( - "--organization", type=str, help="[Deprecated] Pass the organization namespace directly in the repo_id." - ) repo_create_parser.set_defaults(func=lambda args: RepoCreateCommand(args)) # TAG SUBCOMMANDS diff --git a/src/huggingface_hub/hf_api.py b/src/huggingface_hub/hf_api.py index e2a32dd14a..61b73dea46 100644 --- a/src/huggingface_hub/hf_api.py +++ b/src/huggingface_hub/hf_api.py @@ -1781,10 +1781,10 @@ def whoami(self, token: Union[bool, str, None] = None) -> Dict: elif effective_token == _get_token_from_environment(): error_message += ( " The token from HF_TOKEN environment variable is invalid. " - "Note that HF_TOKEN takes precedence over `huggingface-cli login`." + "Note that HF_TOKEN takes precedence over `hf auth login`." ) elif effective_token == _get_token_from_file(): - error_message += " The token stored is invalid. Please run `huggingface-cli login` to update it." + error_message += " The token stored is invalid. Please run `hf auth login` to update it." raise HTTPError(error_message, request=e.request, response=e.response) from e return r.json() @@ -9643,7 +9643,7 @@ def _prepare_upload_folder_additions( log( "It seems you are trying to upload a large folder at once. This might take some time and then fail if " "the folder is too large. For such cases, it is recommended to upload in smaller batches or to use " - "`HfApi().upload_large_folder(...)`/`huggingface-cli upload-large-folder` instead. For more details, " + "`HfApi().upload_large_folder(...)`/`hf upload-large-folder` instead. For more details, " "check out https://huggingface.co/docs/huggingface_hub/main/en/guides/upload#upload-a-large-folder." ) diff --git a/src/huggingface_hub/hub_mixin.py b/src/huggingface_hub/hub_mixin.py index ca1bb1b888..e095dfc865 100644 --- a/src/huggingface_hub/hub_mixin.py +++ b/src/huggingface_hub/hub_mixin.py @@ -491,7 +491,7 @@ def from_pretrained( 'http://hostname': 'foo.bar:4012'}`. The proxies are used on every request. token (`str` or `bool`, *optional*): The token to use as HTTP bearer authorization for remote files. By default, it will use the token - cached when running `huggingface-cli login`. + cached when running `hf auth login`. cache_dir (`str`, `Path`, *optional*): Path to the folder where cached files are stored. local_files_only (`bool`, *optional*, defaults to `False`): @@ -619,7 +619,7 @@ def _from_pretrained( 'http://hostname': 'foo.bar:4012'}`). token (`str` or `bool`, *optional*): The token to use as HTTP bearer authorization for remote files. By default, it will use the token - cached when running `huggingface-cli login`. + cached when running `hf auth login`. cache_dir (`str`, `Path`, *optional*): Path to the folder where cached files are stored. local_files_only (`bool`, *optional*, defaults to `False`): @@ -664,7 +664,7 @@ def push_to_hub( If `None` (default), the repo will be public unless the organization's default is private. token (`str`, *optional*): The token to use as HTTP bearer authorization for remote files. By default, it will use the token - cached when running `huggingface-cli login`. + cached when running `hf auth login`. branch (`str`, *optional*): The git branch on which to push the model. This defaults to `"main"`. create_pr (`boolean`, *optional*): diff --git a/src/huggingface_hub/inference/_providers/_common.py b/src/huggingface_hub/inference/_providers/_common.py index 7618dcf42d..3d41a7c1be 100644 --- a/src/huggingface_hub/inference/_providers/_common.py +++ b/src/huggingface_hub/inference/_providers/_common.py @@ -131,7 +131,7 @@ def _prepare_api_key(self, api_key: Optional[str]) -> str: api_key = get_token() if api_key is None: raise ValueError( - f"You must provide an api_key to work with {self.provider} API or log in with `huggingface-cli login`." + f"You must provide an api_key to work with {self.provider} API or log in with `hf auth login`." ) return api_key diff --git a/src/huggingface_hub/keras_mixin.py b/src/huggingface_hub/keras_mixin.py index e1c7ad503e..45d0eaf8a7 100644 --- a/src/huggingface_hub/keras_mixin.py +++ b/src/huggingface_hub/keras_mixin.py @@ -45,7 +45,7 @@ def _inner(model, *args, **kwargs): if not hasattr(model, "history"): # hacky way to check if model is Keras 2.x raise NotImplementedError( f"Cannot use '{fn.__name__}': Keras 3.x is not supported." - " Please save models manually and upload them using `upload_folder` or `huggingface-cli upload`." + " Please save models manually and upload them using `upload_folder` or `hf upload`." ) return fn(model, *args, **kwargs) @@ -338,7 +338,7 @@ def push_to_hub_keras( token (`str`, *optional*): The token to use as HTTP bearer authorization for remote files. If not set, will use the token set when logging in with - `huggingface-cli login` (stored in `~/.huggingface`). + `hf auth login` (stored in `~/.huggingface`). branch (`str`, *optional*): The git branch on which to push the model. This defaults to the default branch as specified in your repository, which diff --git a/src/huggingface_hub/repository.py b/src/huggingface_hub/repository.py index af1ab72fb4..d4a904f458 100644 --- a/src/huggingface_hub/repository.py +++ b/src/huggingface_hub/repository.py @@ -487,7 +487,7 @@ def __init__( To set when cloning a repo from a repo_id. Default is model. token (`bool` or `str`, *optional*): A valid authentication token (see https://huggingface.co/settings/token). - If `None` or `True` and machine is logged in (through `huggingface-cli login` + If `None` or `True` and machine is logged in (through `hf auth login` or [`~huggingface_hub.login`]), token will be retrieved from the cache. If `False`, token is not sent in the request header. git_user (`str`, *optional*): @@ -878,7 +878,7 @@ def lfs_enable_largefiles(self): """ try: lfs_config = "git config lfs.customtransfer.multipart" - run_subprocess(f"{lfs_config}.path huggingface-cli", self.local_dir) + run_subprocess(f"{lfs_config}.path hf", self.local_dir) run_subprocess( f"{lfs_config}.args {LFS_MULTIPART_UPLOAD_COMMAND}", self.local_dir, diff --git a/src/huggingface_hub/utils/_auth.py b/src/huggingface_hub/utils/_auth.py index c70280aec4..72be4dedbd 100644 --- a/src/huggingface_hub/utils/_auth.py +++ b/src/huggingface_hub/utils/_auth.py @@ -41,7 +41,7 @@ def get_token() -> Optional[str]: Token is retrieved in priority from the `HF_TOKEN` environment variable. Otherwise, we read the token file located in the Hugging Face home folder. Returns None if user is not logged in. To log in, use [`login`] or - `huggingface-cli login`. + `hf auth login`. Returns: `str` or `None`: The token, `None` if it doesn't exist. diff --git a/src/huggingface_hub/utils/_cache_manager.py b/src/huggingface_hub/utils/_cache_manager.py index 21469c97af..311e164a4f 100644 --- a/src/huggingface_hub/utils/_cache_manager.py +++ b/src/huggingface_hub/utils/_cache_manager.py @@ -632,9 +632,9 @@ def scan_cache_dir(cache_dir: Optional[Union[str, Path]] = None) -> HFCacheInfo: ) ``` - You can also print a detailed report directly from the `huggingface-cli` using: + You can also print a detailed report directly from the `hf` command line using: ```text - > huggingface-cli scan-cache + > hf cache scan REPO ID REPO TYPE SIZE ON DISK NB FILES REFS LOCAL PATH --------------------------- --------- ------------ -------- ------------------- ------------------------------------------------------------------------- glue dataset 116.3K 15 1.17.0, main, 2.4.0 /Users/lucain/.cache/huggingface/hub/datasets--glue diff --git a/src/huggingface_hub/utils/_headers.py b/src/huggingface_hub/utils/_headers.py index f0e1ddd87a..053a92a398 100644 --- a/src/huggingface_hub/utils/_headers.py +++ b/src/huggingface_hub/utils/_headers.py @@ -159,7 +159,7 @@ def get_token_to_send(token: Optional[Union[bool, str]]) -> Optional[str]: raise LocalTokenNotFoundError( "Token is required (`token=True`), but no token found. You" " need to provide a token or be logged in to Hugging Face with" - " `huggingface-cli login` or `huggingface_hub.login`. See" + " `hf auth login` or `huggingface_hub.login`. See" " https://huggingface.co/settings/tokens." ) return cached_token diff --git a/tests/test_cli.py b/tests/test_cli.py index 21ea90b409..6aae4390aa 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -7,12 +7,11 @@ from typing import Generator from unittest.mock import Mock, patch -from huggingface_hub.commands.delete_cache import DeleteCacheCommand -from huggingface_hub.commands.download import DownloadCommand -from huggingface_hub.commands.repo_files import DeleteFilesSubCommand, RepoFilesCommand -from huggingface_hub.commands.scan_cache import ScanCacheCommand -from huggingface_hub.commands.tag import TagCommands -from huggingface_hub.commands.upload import UploadCommand +from huggingface_hub.cli.cache import CacheCommand +from huggingface_hub.cli.download import DownloadCommand +from huggingface_hub.cli.repo import RepoCommands +from huggingface_hub.cli.repo_files import DeleteFilesSubCommand, RepoFilesCommand +from huggingface_hub.cli.upload import UploadCommand from huggingface_hub.errors import RevisionNotFoundError from huggingface_hub.utils import SoftTemporaryDirectory, capture_output @@ -22,74 +21,78 @@ class TestCacheCommand(unittest.TestCase): def setUp(self) -> None: """ - Set up scan-cache/delete-cache commands as in `src/huggingface_hub/commands/huggingface_cli.py`. + Set up cache scan/delete commands as in `src/huggingface_hub/cli/hf.py`. """ - self.parser = ArgumentParser("huggingface-cli", usage="huggingface-cli []") + self.parser = ArgumentParser("hf", usage="hf []") commands_parser = self.parser.add_subparsers() - ScanCacheCommand.register_subcommand(commands_parser) - DeleteCacheCommand.register_subcommand(commands_parser) + CacheCommand.register_subcommand(commands_parser) def test_scan_cache_basic(self) -> None: - """Test `huggingface-cli scan-cache`.""" - args = self.parser.parse_args(["scan-cache"]) - self.assertEqual(args.dir, None) - self.assertEqual(args.verbose, 0) - self.assertEqual(args.func, ScanCacheCommand) + """Test `hf cache scan`.""" + args = self.parser.parse_args(["cache", "scan"]) + assert args.dir is None + assert args.verbose == 0 + assert args.func == CacheCommand + assert args.cache_command == "scan" def test_scan_cache_verbose(self) -> None: - """Test `huggingface-cli scan-cache -v`.""" - args = self.parser.parse_args(["scan-cache", "-v"]) - self.assertEqual(args.dir, None) - self.assertEqual(args.verbose, 1) - self.assertEqual(args.func, ScanCacheCommand) + """Test `hf cache scan -v`.""" + args = self.parser.parse_args(["cache", "scan", "-v"]) + assert args.dir is None + assert args.verbose == 1 + assert args.func == CacheCommand + assert args.cache_command == "scan" def test_scan_cache_with_dir(self) -> None: - """Test `huggingface-cli scan-cache --dir something`.""" - args = self.parser.parse_args(["scan-cache", "--dir", "something"]) - self.assertEqual(args.dir, "something") - self.assertEqual(args.verbose, 0) - self.assertEqual(args.func, ScanCacheCommand) + """Test `hf cache scan --dir something`.""" + args = self.parser.parse_args(["cache", "scan", "--dir", "something"]) + assert args.dir == "something" + assert args.verbose == 0 + assert args.func == CacheCommand + assert args.cache_command == "scan" def test_scan_cache_ultra_verbose(self) -> None: - """Test `huggingface-cli scan-cache -vvv`.""" - args = self.parser.parse_args(["scan-cache", "-vvv"]) - self.assertEqual(args.dir, None) - self.assertEqual(args.verbose, 3) - self.assertEqual(args.func, ScanCacheCommand) + """Test `hf cache scan -vvv`.""" + args = self.parser.parse_args(["cache", "scan", "-vvv"]) + assert args.dir is None + assert args.verbose == 3 + assert args.func == CacheCommand + assert args.cache_command == "scan" def test_delete_cache_with_dir(self) -> None: - """Test `huggingface-cli delete-cache --dir something`.""" - args = self.parser.parse_args(["delete-cache", "--dir", "something"]) - self.assertEqual(args.dir, "something") - self.assertEqual(args.func, DeleteCacheCommand) + """Test `hf cache delete --dir something`.""" + args = self.parser.parse_args(["cache", "delete", "--dir", "something"]) + assert args.dir == "something" + assert args.func == CacheCommand + assert args.cache_command == "delete" class TestUploadCommand(unittest.TestCase): def setUp(self) -> None: """ - Set up CLI as in `src/huggingface_hub/commands/huggingface_cli.py`. + Set up CLI as in `src/huggingface_hub/cli/hf.py`. """ - self.parser = ArgumentParser("huggingface-cli", usage="huggingface-cli []") + self.parser = ArgumentParser("hf", usage="hf []") commands_parser = self.parser.add_subparsers() UploadCommand.register_subcommand(commands_parser) def test_upload_basic(self) -> None: - """Test `huggingface-cli upload my-folder to dummy-repo`.""" + """Test `hf upload my-folder to dummy-repo`.""" cmd = UploadCommand(self.parser.parse_args(["upload", DUMMY_MODEL_ID, "my-folder"])) - self.assertEqual(cmd.repo_id, DUMMY_MODEL_ID) - self.assertEqual(cmd.local_path, "my-folder") - self.assertEqual(cmd.path_in_repo, ".") # implicit - self.assertEqual(cmd.repo_type, "model") - self.assertEqual(cmd.revision, None) - self.assertEqual(cmd.include, None) - self.assertEqual(cmd.exclude, None) - self.assertEqual(cmd.delete, None) - self.assertEqual(cmd.commit_message, None) - self.assertEqual(cmd.commit_description, None) - self.assertEqual(cmd.create_pr, False) - self.assertEqual(cmd.every, None) - self.assertEqual(cmd.api.token, None) - self.assertEqual(cmd.quiet, False) + assert cmd.repo_id == DUMMY_MODEL_ID + assert cmd.local_path == "my-folder" + assert cmd.path_in_repo == "." # implicit + assert cmd.repo_type == "model" + assert cmd.revision is None + assert cmd.include is None + assert cmd.exclude is None + assert cmd.delete is None + assert cmd.commit_message is None + assert cmd.commit_description is None + assert cmd.create_pr is False + assert cmd.every is None + assert cmd.api.token is None + assert cmd.quiet is False def test_upload_with_wildcard(self) -> None: """Test uploading files using wildcard patterns.""" @@ -102,10 +105,10 @@ def test_upload_with_wildcard(self) -> None: # Test basic wildcard pattern cmd = UploadCommand(self.parser.parse_args(["upload", DUMMY_MODEL_ID, "*.safetensors"])) - self.assertEqual(cmd.local_path, ".") - self.assertEqual(cmd.include, "*.safetensors") - self.assertEqual(cmd.path_in_repo, ".") - self.assertEqual(cmd.repo_id, DUMMY_MODEL_ID) + assert cmd.local_path == "." + assert cmd.include == "*.safetensors" + assert cmd.path_in_repo == "." + assert cmd.repo_id == DUMMY_MODEL_ID # Test wildcard pattern with specific directory subdir = Path(cache_dir) / "subdir" @@ -113,9 +116,9 @@ def test_upload_with_wildcard(self) -> None: (subdir / "special.safetensors").touch() cmd = UploadCommand(self.parser.parse_args(["upload", DUMMY_MODEL_ID, "subdir/*.safetensors"])) - self.assertEqual(cmd.local_path, ".") - self.assertEqual(cmd.include, "subdir/*.safetensors") - self.assertEqual(cmd.path_in_repo, ".") + assert cmd.local_path == "." + assert cmd.include == "subdir/*.safetensors" + assert cmd.path_in_repo == "." # Test error when using wildcard with --include with self.assertRaises(ValueError): @@ -128,7 +131,7 @@ def test_upload_with_wildcard(self) -> None: UploadCommand(self.parser.parse_args(["upload", DUMMY_MODEL_ID, "*.safetensors", "models/"])) def test_upload_with_all_options(self) -> None: - """Test `huggingface-cli upload my-file to dummy-repo with all options selected`.""" + """Test `hf upload my-file to dummy-repo with all options selected`.""" cmd = UploadCommand( self.parser.parse_args( [ @@ -162,20 +165,20 @@ def test_upload_with_all_options(self) -> None: ] ) ) - self.assertEqual(cmd.repo_id, DUMMY_MODEL_ID) - self.assertEqual(cmd.local_path, "my-file") - self.assertEqual(cmd.path_in_repo, "data/") - self.assertEqual(cmd.repo_type, "dataset") - self.assertEqual(cmd.revision, "v1.0.0") - self.assertEqual(cmd.include, ["*.json", "*.yaml"]) - self.assertEqual(cmd.exclude, ["*.log", "*.txt"]) - self.assertEqual(cmd.delete, ["*.config", "*.secret"]) - self.assertEqual(cmd.commit_message, "My commit message") - self.assertEqual(cmd.commit_description, "My commit description") - self.assertEqual(cmd.create_pr, True) - self.assertEqual(cmd.every, 5) - self.assertEqual(cmd.api.token, "my-token") - self.assertEqual(cmd.quiet, True) + assert cmd.repo_id == DUMMY_MODEL_ID + assert cmd.local_path == "my-file" + assert cmd.path_in_repo == "data/" + assert cmd.repo_type == "dataset" + assert cmd.revision == "v1.0.0" + assert cmd.include == ["*.json", "*.yaml"] + assert cmd.exclude == ["*.log", "*.txt"] + assert cmd.delete == ["*.config", "*.secret"] + assert cmd.commit_message == "My commit message" + assert cmd.commit_description == "My commit description" + assert cmd.create_pr is True + assert cmd.every == 5 + assert cmd.api.token == "my-token" + assert cmd.quiet is True def test_upload_implicit_local_path_when_folder_exists(self) -> None: with tmp_current_directory() as cache_dir: @@ -184,8 +187,8 @@ def test_upload_implicit_local_path_when_folder_exists(self) -> None: cmd = UploadCommand(self.parser.parse_args(["upload", "my-cool-model"])) # A folder with the same name as the repo exists => upload it at the root of the repo - self.assertEqual(cmd.local_path, "my-cool-model") - self.assertEqual(cmd.path_in_repo, ".") + assert cmd.local_path == "my-cool-model" + assert cmd.path_in_repo == "." def test_upload_implicit_local_path_when_file_exists(self) -> None: with tmp_current_directory() as cache_dir: @@ -194,8 +197,8 @@ def test_upload_implicit_local_path_when_file_exists(self) -> None: cmd = UploadCommand(self.parser.parse_args(["upload", "my-cool-model"])) # A file with the same name as the repo exists => upload it at the root of the repo - self.assertEqual(cmd.local_path, "my-cool-model") - self.assertEqual(cmd.path_in_repo, "my-cool-model") + assert cmd.local_path == "my-cool-model" + assert cmd.path_in_repo == "my-cool-model" def test_upload_implicit_local_path_when_org_repo(self) -> None: with tmp_current_directory() as cache_dir: @@ -204,8 +207,8 @@ def test_upload_implicit_local_path_when_org_repo(self) -> None: cmd = UploadCommand(self.parser.parse_args(["upload", "my-cool-org/my-cool-model"])) # A folder with the same name as the repo exists => upload it at the root of the repo - self.assertEqual(cmd.local_path, "my-cool-model") - self.assertEqual(cmd.path_in_repo, ".") + assert cmd.local_path == "my-cool-model" + assert cmd.path_in_repo == "." def test_upload_implicit_local_path_otherwise(self) -> None: # No folder or file has the same name as the repo => raise exception @@ -218,8 +221,8 @@ def test_upload_explicit_local_path_to_folder_implicit_path_in_repo(self) -> Non folder_path = Path(cache_dir) / "path" / "to" / "folder" folder_path.mkdir(parents=True, exist_ok=True) cmd = UploadCommand(self.parser.parse_args(["upload", "my-repo", "./path/to/folder"])) - self.assertEqual(cmd.local_path, "./path/to/folder") - self.assertEqual(cmd.path_in_repo, ".") # Always upload the folder at the root of the repo + assert cmd.local_path == "./path/to/folder" + assert cmd.path_in_repo == "." # Always upload the folder at the root of the repo def test_upload_explicit_local_path_to_file_implicit_path_in_repo(self) -> None: with tmp_current_directory() as cache_dir: @@ -227,13 +230,13 @@ def test_upload_explicit_local_path_to_file_implicit_path_in_repo(self) -> None: file_path.parent.mkdir(parents=True, exist_ok=True) file_path.touch() cmd = UploadCommand(self.parser.parse_args(["upload", "my-repo", "./path/to/file.txt"])) - self.assertEqual(cmd.local_path, "./path/to/file.txt") - self.assertEqual(cmd.path_in_repo, "file.txt") # If a file, upload it at the root of the repo and keep name + assert cmd.local_path == "./path/to/file.txt" + assert cmd.path_in_repo == "file.txt" # If a file, upload it at the root of the repo and keep name def test_upload_explicit_paths(self) -> None: cmd = UploadCommand(self.parser.parse_args(["upload", "my-repo", "./path/to/folder", "data/"])) - self.assertEqual(cmd.local_path, "./path/to/folder") - self.assertEqual(cmd.path_in_repo, "data/") + assert cmd.local_path == "./path/to/folder" + assert cmd.path_in_repo == "data/" def test_every_must_be_positive(self) -> None: with self.assertRaises(ValueError): @@ -244,15 +247,15 @@ def test_every_must_be_positive(self) -> None: def test_every_as_int(self) -> None: cmd = UploadCommand(self.parser.parse_args(["upload", DUMMY_MODEL_ID, ".", "--every", "10"])) - self.assertEqual(cmd.every, 10) + assert cmd.every == 10 def test_every_as_float(self) -> None: cmd = UploadCommand(self.parser.parse_args(["upload", DUMMY_MODEL_ID, ".", "--every", "0.5"])) - self.assertEqual(cmd.every, 0.5) + assert cmd.every == 0.5 - @patch("huggingface_hub.commands.upload.HfApi.repo_info") - @patch("huggingface_hub.commands.upload.HfApi.upload_folder") - @patch("huggingface_hub.commands.upload.HfApi.create_repo") + @patch("huggingface_hub.cli.upload.HfApi.repo_info") + @patch("huggingface_hub.cli.upload.HfApi.upload_folder") + @patch("huggingface_hub.cli.upload.HfApi.create_repo") def test_upload_folder_mock(self, create_mock: Mock, upload_mock: Mock, repo_info_mock: Mock) -> None: with SoftTemporaryDirectory() as cache_dir: cache_path = cache_dir.absolute().as_posix() @@ -280,9 +283,9 @@ def test_upload_folder_mock(self, create_mock: Mock, upload_mock: Mock, repo_inf delete_patterns=["*.json"], ) - @patch("huggingface_hub.commands.upload.HfApi.repo_info") - @patch("huggingface_hub.commands.upload.HfApi.upload_file") - @patch("huggingface_hub.commands.upload.HfApi.create_repo") + @patch("huggingface_hub.cli.upload.HfApi.repo_info") + @patch("huggingface_hub.cli.upload.HfApi.upload_file") + @patch("huggingface_hub.cli.upload.HfApi.create_repo") def test_upload_file_mock(self, create_mock: Mock, upload_mock: Mock, repo_info_mock: Mock) -> None: with SoftTemporaryDirectory() as cache_dir: file_path = Path(cache_dir) / "file.txt" @@ -308,9 +311,9 @@ def test_upload_file_mock(self, create_mock: Mock, upload_mock: Mock, repo_info_ create_pr=True, ) - @patch("huggingface_hub.commands.upload.HfApi.repo_info") - @patch("huggingface_hub.commands.upload.HfApi.upload_file") - @patch("huggingface_hub.commands.upload.HfApi.create_repo") + @patch("huggingface_hub.cli.upload.HfApi.repo_info") + @patch("huggingface_hub.cli.upload.HfApi.upload_file") + @patch("huggingface_hub.cli.upload.HfApi.create_repo") def test_upload_file_no_revision_mock(self, create_mock: Mock, upload_mock: Mock, repo_info_mock: Mock) -> None: with SoftTemporaryDirectory() as cache_dir: file_path = Path(cache_dir) / "file.txt" @@ -320,10 +323,10 @@ def test_upload_file_no_revision_mock(self, create_mock: Mock, upload_mock: Mock # Revision not specified => no need to check repo_info_mock.assert_not_called() - @patch("huggingface_hub.commands.upload.HfApi.create_branch") - @patch("huggingface_hub.commands.upload.HfApi.repo_info") - @patch("huggingface_hub.commands.upload.HfApi.upload_file") - @patch("huggingface_hub.commands.upload.HfApi.create_repo") + @patch("huggingface_hub.cli.upload.HfApi.create_branch") + @patch("huggingface_hub.cli.upload.HfApi.repo_info") + @patch("huggingface_hub.cli.upload.HfApi.upload_file") + @patch("huggingface_hub.cli.upload.HfApi.create_repo") def test_upload_file_with_revision_mock( self, create_mock: Mock, upload_mock: Mock, repo_info_mock: Mock, create_branch_mock: Mock ) -> None: @@ -349,9 +352,9 @@ def test_upload_file_with_revision_mock( repo_id=create_mock.return_value.repo_id, repo_type="model", branch="my-branch", exist_ok=True ) - @patch("huggingface_hub.commands.upload.HfApi.repo_info") - @patch("huggingface_hub.commands.upload.HfApi.upload_file") - @patch("huggingface_hub.commands.upload.HfApi.create_repo") + @patch("huggingface_hub.cli.upload.HfApi.repo_info") + @patch("huggingface_hub.cli.upload.HfApi.upload_file") + @patch("huggingface_hub.cli.upload.HfApi.create_repo") def test_upload_file_revision_and_create_pr_mock( self, create_mock: Mock, upload_mock: Mock, repo_info_mock: Mock ) -> None: @@ -367,7 +370,7 @@ def test_upload_file_revision_and_create_pr_mock( # Revision specified but --create-pr => no need to check repo_info_mock.assert_not_called() - @patch("huggingface_hub.commands.upload.HfApi.create_repo") + @patch("huggingface_hub.cli.upload.HfApi.create_repo") def test_upload_missing_path(self, create_mock: Mock) -> None: cmd = UploadCommand(self.parser.parse_args(["upload", "my-model", "/path/to/missing_file", "logs/file.txt"])) with self.assertRaises(FileNotFoundError): @@ -380,31 +383,31 @@ def test_upload_missing_path(self, create_mock: Mock) -> None: class TestDownloadCommand(unittest.TestCase): def setUp(self) -> None: """ - Set up CLI as in `src/huggingface_hub/commands/huggingface_cli.py`. + Set up CLI as in `src/huggingface_hub/cli/hf.py`. """ - self.parser = ArgumentParser("huggingface-cli", usage="huggingface-cli []") + self.parser = ArgumentParser("hf", usage="hf []") commands_parser = self.parser.add_subparsers() DownloadCommand.register_subcommand(commands_parser) def test_download_basic(self) -> None: - """Test `huggingface-cli download dummy-repo`.""" + """Test `hf download dummy-repo`.""" args = self.parser.parse_args(["download", DUMMY_MODEL_ID]) - self.assertEqual(args.repo_id, DUMMY_MODEL_ID) - self.assertEqual(len(args.filenames), 0) - self.assertEqual(args.repo_type, "model") - self.assertIsNone(args.revision) - self.assertIsNone(args.include) - self.assertIsNone(args.exclude) - self.assertIsNone(args.cache_dir) - self.assertIsNone(args.local_dir) - self.assertFalse(args.force_download) - self.assertFalse(args.resume_download) - self.assertIsNone(args.token) - self.assertFalse(args.quiet) - self.assertEqual(args.func, DownloadCommand) + assert args.repo_id == DUMMY_MODEL_ID + assert len(args.filenames) == 0 + assert args.repo_type == "model" + assert args.revision is None + assert args.include is None + assert args.exclude is None + assert args.cache_dir is None + assert args.local_dir is None + assert args.force_download is False + assert args.resume_download is False + assert args.token is None + assert args.quiet is False + assert args.func == DownloadCommand def test_download_with_all_options(self) -> None: - """Test `huggingface-cli download dummy-repo` with all options selected.""" + """Test `hf download dummy-repo` with all options selected.""" args = self.parser.parse_args( [ "download", @@ -432,21 +435,21 @@ def test_download_with_all_options(self) -> None: "4", ] ) - self.assertEqual(args.repo_id, DUMMY_MODEL_ID) - self.assertEqual(args.repo_type, "dataset") - self.assertEqual(args.revision, "v1.0.0") - self.assertEqual(args.include, ["*.json", "*.yaml"]) - self.assertEqual(args.exclude, ["*.log", "*.txt"]) - self.assertTrue(args.force_download) - self.assertEqual(args.cache_dir, "/tmp") - self.assertEqual(args.local_dir, ".") - self.assertTrue(args.resume_download) - self.assertEqual(args.token, "my-token") - self.assertTrue(args.quiet) - self.assertEqual(args.max_workers, 4) - self.assertEqual(args.func, DownloadCommand) - - @patch("huggingface_hub.commands.download.hf_hub_download") + assert args.repo_id == DUMMY_MODEL_ID + assert args.repo_type == "dataset" + assert args.revision == "v1.0.0" + assert args.include == ["*.json", "*.yaml"] + assert args.exclude == ["*.log", "*.txt"] + assert args.force_download is True + assert args.cache_dir == "/tmp" + assert args.local_dir == "." + assert args.resume_download is True + assert args.token == "my-token" + assert args.quiet is True + assert args.max_workers == 4 + assert args.func == DownloadCommand + + @patch("huggingface_hub.cli.download.hf_hub_download") def test_download_file_from_revision(self, mock: Mock) -> None: args = Namespace( token="hf_****", @@ -480,10 +483,10 @@ def test_download_file_from_revision(self, mock: Mock) -> None: force_download=False, token="hf_****", local_dir=".", - library_name="huggingface-cli", + library_name="hf", ) - @patch("huggingface_hub.commands.download.snapshot_download") + @patch("huggingface_hub.cli.download.snapshot_download") def test_download_multiple_files(self, mock: Mock) -> None: args = Namespace( token="hf_****", @@ -515,11 +518,11 @@ def test_download_multiple_files(self, mock: Mock) -> None: cache_dir=None, token="hf_****", local_dir="/path/to/dir", - library_name="huggingface-cli", + library_name="hf", max_workers=8, ) - @patch("huggingface_hub.commands.download.snapshot_download") + @patch("huggingface_hub.cli.download.snapshot_download") def test_download_with_patterns(self, mock: Mock) -> None: args = Namespace( token=None, @@ -551,11 +554,11 @@ def test_download_with_patterns(self, mock: Mock) -> None: cache_dir=None, local_dir=None, token=None, - library_name="huggingface-cli", + library_name="hf", max_workers=8, ) - @patch("huggingface_hub.commands.download.snapshot_download") + @patch("huggingface_hub.cli.download.snapshot_download") def test_download_with_ignored_patterns(self, mock: Mock) -> None: args = Namespace( token=None, @@ -589,7 +592,7 @@ def test_download_with_ignored_patterns(self, mock: Mock) -> None: cache_dir=None, token=None, local_dir=None, - library_name="huggingface-cli", + library_name="hf", max_workers=8, ) @@ -604,26 +607,27 @@ def test_download_with_ignored_patterns(self, mock: Mock) -> None: class TestTagCommands(unittest.TestCase): def setUp(self) -> None: """ - Set up CLI as in `src/huggingface_hub/commands/huggingface_cli.py`. + Set up CLI as in `src/huggingface_hub/cli/hf.py`. """ - self.parser = ArgumentParser("huggingface-cli", usage="huggingface-cli []") + self.parser = ArgumentParser("hf", usage="hf []") commands_parser = self.parser.add_subparsers() - TagCommands.register_subcommand(commands_parser) + RepoCommands.register_subcommand(commands_parser) def test_tag_create_basic(self) -> None: - args = self.parser.parse_args(["tag", DUMMY_MODEL_ID, "1.0", "-m", "My tag message"]) - self.assertEqual(args.repo_id, DUMMY_MODEL_ID) - self.assertEqual(args.tag, "1.0") - self.assertIsNotNone(args.message) - self.assertIsNone(args.revision) - self.assertIsNone(args.token) - self.assertEqual(args.repo_type, "model") - self.assertFalse(args.yes) + args = self.parser.parse_args(["repo", "tag", "create", DUMMY_MODEL_ID, "1.0", "-m", "My tag message"]) + assert args.repo_id == DUMMY_MODEL_ID + assert args.tag == "1.0" + assert args.message is not None + assert args.revision is None + assert args.token is None + assert args.repo_type == "model" def test_tag_create_with_all_options(self) -> None: args = self.parser.parse_args( [ + "repo", "tag", + "create", DUMMY_MODEL_ID, "1.0", "--message", @@ -634,30 +638,28 @@ def test_tag_create_with_all_options(self) -> None: "my-token", "--repo-type", "dataset", - "--yes", ] ) - self.assertEqual(args.repo_id, DUMMY_MODEL_ID) - self.assertEqual(args.tag, "1.0") - self.assertEqual(args.message, "My tag message") - self.assertEqual(args.revision, "v1.0.0") - self.assertEqual(args.token, "my-token") - self.assertEqual(args.repo_type, "dataset") - self.assertTrue(args.yes) + assert args.repo_id == DUMMY_MODEL_ID + assert args.tag == "1.0" + assert args.message == "My tag message" + assert args.revision == "v1.0.0" + assert args.token == "my-token" + assert args.repo_type == "dataset" def test_tag_list_basic(self) -> None: - args = self.parser.parse_args(["tag", "--list", DUMMY_MODEL_ID]) - self.assertEqual(args.repo_id, DUMMY_MODEL_ID) - self.assertIsNone(args.token) - self.assertEqual(args.repo_type, "model") + args = self.parser.parse_args(["repo", "tag", "list", DUMMY_MODEL_ID]) + assert args.repo_id == DUMMY_MODEL_ID + assert args.token is None + assert args.repo_type == "model" def test_tag_delete_basic(self) -> None: - args = self.parser.parse_args(["tag", "--delete", DUMMY_MODEL_ID, "1.0"]) - self.assertEqual(args.repo_id, DUMMY_MODEL_ID) - self.assertEqual(args.tag, "1.0") - self.assertIsNone(args.token) - self.assertEqual(args.repo_type, "model") - self.assertFalse(args.yes) + args = self.parser.parse_args(["repo", "tag", "delete", DUMMY_MODEL_ID, "1.0"]) + assert args.repo_id == DUMMY_MODEL_ID + assert args.tag == "1.0" + assert args.token is None + assert args.repo_type == "model" + assert args.yes is False @contextmanager @@ -677,20 +679,20 @@ def tmp_current_directory() -> Generator[str, None, None]: class TestRepoFilesCommand(unittest.TestCase): def setUp(self) -> None: """ - Set up CLI as in `src/huggingface_hub/commands/huggingface_cli.py`. + Set up CLI as in `src/huggingface_hub/cli/hf.py`. """ - self.parser = ArgumentParser("huggingface-cli", usage="huggingface-cli []") + self.parser = ArgumentParser("hf", usage="hf []") commands_parser = self.parser.add_subparsers() RepoFilesCommand.register_subcommand(commands_parser) - @patch("huggingface_hub.commands.repo_files.HfApi.delete_files") + @patch("huggingface_hub.cli.repo_files.HfApi.delete_files") def test_delete(self, delete_files_mock: Mock) -> None: fixtures = [ { "input_args": [ "repo-files", - DUMMY_MODEL_ID, "delete", + DUMMY_MODEL_ID, "*", ], "delete_files_args": { @@ -708,8 +710,8 @@ def test_delete(self, delete_files_mock: Mock) -> None: { "input_args": [ "repo-files", - DUMMY_MODEL_ID, "delete", + DUMMY_MODEL_ID, "file.txt", ], "delete_files_args": { @@ -727,8 +729,8 @@ def test_delete(self, delete_files_mock: Mock) -> None: { "input_args": [ "repo-files", - DUMMY_MODEL_ID, "delete", + DUMMY_MODEL_ID, "folder/", ], "delete_files_args": { @@ -746,8 +748,8 @@ def test_delete(self, delete_files_mock: Mock) -> None: { "input_args": [ "repo-files", - DUMMY_MODEL_ID, "delete", + DUMMY_MODEL_ID, "file1.txt", "folder/", "file2.txt", @@ -769,8 +771,8 @@ def test_delete(self, delete_files_mock: Mock) -> None: { "input_args": [ "repo-files", - DUMMY_MODEL_ID, "delete", + DUMMY_MODEL_ID, "file.txt *", "*.json", "folder/*.parquet", @@ -792,8 +794,8 @@ def test_delete(self, delete_files_mock: Mock) -> None: { "input_args": [ "repo-files", - DUMMY_MODEL_ID, "delete", + DUMMY_MODEL_ID, "file.txt *", "--revision", "test_revision", diff --git a/tests/test_hf_api.py b/tests/test_hf_api.py index 7bc0cf23f1..87fb77c6d2 100644 --- a/tests/test_hf_api.py +++ b/tests/test_hf_api.py @@ -181,7 +181,7 @@ def test_whoami_with_passing_token(self): @patch("huggingface_hub.utils._headers.get_token", return_value=TOKEN) def test_whoami_with_implicit_token_from_login(self, mock_get_token: Mock) -> None: - """Test using `whoami` after a `huggingface-cli login`.""" + """Test using `whoami` after a `hf auth login`.""" with patch.object(self._api, "token", None): # no default token info = self._api.whoami() self.assertEqual(info["name"], USER) @@ -2786,7 +2786,7 @@ def test_end_to_end_thresh_6M(self): self.assertEqual(failed_process.returncode, 1) self.assertIn("cli lfs-enable-largefiles", failed_process.stderr.decode()) # ^ Instructions on how to fix this are included in the error message. - subprocess.run(["huggingface-cli", "lfs-enable-largefiles", self.cache_dir], check=True) + subprocess.run(["hf", "lfs-enable-largefiles", self.cache_dir], check=True) start_time = time.time() subprocess.run(["git", "push"], check=True, cwd=self.cache_dir) @@ -2823,7 +2823,7 @@ def test_end_to_end_thresh_16M(self): ) subprocess.run(["git", "add", "*"], check=True, cwd=self.cache_dir) subprocess.run(["git", "commit", "-m", "both files in same commit"], check=True, cwd=self.cache_dir) - subprocess.run(["huggingface-cli", "lfs-enable-largefiles", self.cache_dir], check=True) + subprocess.run(["hf", "lfs-enable-largefiles", self.cache_dir], check=True) start_time = time.time() subprocess.run(["git", "push"], check=True, cwd=self.cache_dir) From 83e177750ea3eba1318f8c9525accc520bc6c5b9 Mon Sep 17 00:00:00 2001 From: Lucain Pouget Date: Wed, 23 Jul 2025 14:54:10 +0200 Subject: [PATCH 6/7] Remove deprecated options --- src/huggingface_hub/cli/download.py | 19 ------------------ src/huggingface_hub/cli/repo.py | 30 +---------------------------- 2 files changed, 1 insertion(+), 48 deletions(-) diff --git a/src/huggingface_hub/cli/download.py b/src/huggingface_hub/cli/download.py index 5c22fce372..3e59233da1 100644 --- a/src/huggingface_hub/cli/download.py +++ b/src/huggingface_hub/cli/download.py @@ -89,21 +89,11 @@ def register_subcommand(parser: _SubParsersAction): " details." ), ) - download_parser.add_argument( - "--local-dir-use-symlinks", - choices=["auto", "True", "False"], - help=("Deprecated and ignored. Downloading to a local directory does not use symlinks anymore."), - ) download_parser.add_argument( "--force-download", action="store_true", help="If True, the files will be downloaded even if they are already cached.", ) - download_parser.add_argument( - "--resume-download", - action="store_true", - help="Deprecated and ignored. Downloading a file to local dir always attempts to resume previously interrupted downloads (unless hf-transfer is enabled).", - ) download_parser.add_argument( "--token", type=str, help="A User Access Token generated from https://huggingface.co/settings/tokens" ) @@ -131,16 +121,9 @@ def __init__(self, args: Namespace) -> None: self.cache_dir: Optional[str] = args.cache_dir self.local_dir: Optional[str] = args.local_dir self.force_download: bool = args.force_download - self.resume_download: Optional[bool] = args.resume_download or None self.quiet: bool = args.quiet self.max_workers: int = args.max_workers - if args.local_dir_use_symlinks is not None: - warnings.warn( - "Ignoring --local-dir-use-symlinks. Downloading to a local directory does not use symlinks anymore.", - FutureWarning, - ) - def run(self) -> None: if self.quiet: disable_progress_bars() @@ -169,7 +152,6 @@ def _download(self) -> str: revision=self.revision, filename=self.filenames[0], cache_dir=self.cache_dir, - resume_download=self.resume_download, force_download=self.force_download, token=self.token, local_dir=self.local_dir, @@ -190,7 +172,6 @@ def _download(self) -> str: revision=self.revision, allow_patterns=allow_patterns, ignore_patterns=ignore_patterns, - resume_download=self.resume_download, force_download=self.force_download, cache_dir=self.cache_dir, token=self.token, diff --git a/src/huggingface_hub/cli/repo.py b/src/huggingface_hub/cli/repo.py index 2f532b3015..df7888c546 100644 --- a/src/huggingface_hub/cli/repo.py +++ b/src/huggingface_hub/cli/repo.py @@ -144,43 +144,15 @@ def register_subcommand(parser: _SubParsersAction): class RepoCreateCommand: def __init__(self, args: argparse.Namespace): self.repo_id: str = args.repo_id - self.repo_type: Optional[str] = args.repo_type or args.type + self.repo_type: Optional[str] = args.repo_type self.space_sdk: Optional[str] = args.space_sdk - self.organization: Optional[str] = args.organization - self.yes: bool = args.yes self.private: bool = args.private self.token: Optional[str] = args.token self.exist_ok: bool = args.exist_ok self.resource_group_id: Optional[str] = args.resource_group_id - - if args.type is not None: - print( - ANSI.yellow( - "The --type argument is deprecated and will be removed in a future version. Use --repo-type instead." - ) - ) - if self.organization is not None: - print( - ANSI.yellow( - "The --organization argument is deprecated and will be removed in a future version. Pass the organization namespace directly in the repo_id." - ) - ) - if self.yes: - print( - ANSI.yellow( - "The --yes argument is deprecated and will be removed in a future version. It does not have any effect." - ) - ) - self._api = HfApi() def run(self): - if self.organization is not None: - if "/" in self.repo_id: - print(ANSI.red("You cannot pass both --organization and a repo_id with a namespace.")) - exit(1) - self.repo_id = f"{self.organization}/{self.repo_id}" - repo_url = self._api.create_repo( repo_id=self.repo_id, repo_type=self.repo_type, From 55857b162ea298fda4fc94ba6b3089f34d0b68c1 Mon Sep 17 00:00:00 2001 From: Lucain Pouget Date: Wed, 23 Jul 2025 15:10:56 +0200 Subject: [PATCH 7/7] fix tests --- tests/test_cli.py | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/tests/test_cli.py b/tests/test_cli.py index 6aae4390aa..50fd567efe 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -401,7 +401,6 @@ def test_download_basic(self) -> None: assert args.cache_dir is None assert args.local_dir is None assert args.force_download is False - assert args.resume_download is False assert args.token is None assert args.quiet is False assert args.func == DownloadCommand @@ -425,7 +424,6 @@ def test_download_with_all_options(self) -> None: "--force-download", "--cache-dir", "/tmp", - "--resume-download", "--token", "my-token", "--quiet", @@ -443,7 +441,6 @@ def test_download_with_all_options(self) -> None: assert args.force_download is True assert args.cache_dir == "/tmp" assert args.local_dir == "." - assert args.resume_download is True assert args.token == "my-token" assert args.quiet is True assert args.max_workers == 4 @@ -460,10 +457,8 @@ def test_download_file_from_revision(self, mock: Mock) -> None: include=None, exclude=None, force_download=False, - resume_download=False, cache_dir=None, local_dir=".", - local_dir_use_symlinks=None, quiet=False, max_workers=8, ) @@ -479,7 +474,6 @@ def test_download_file_from_revision(self, mock: Mock) -> None: revision="refs/pr/1", filename="README.md", cache_dir=None, - resume_download=None, force_download=False, token="hf_****", local_dir=".", @@ -497,10 +491,8 @@ def test_download_multiple_files(self, mock: Mock) -> None: include=None, exclude=None, force_download=True, - resume_download=True, cache_dir=None, local_dir="/path/to/dir", - local_dir_use_symlinks=None, quiet=False, max_workers=8, ) @@ -513,7 +505,6 @@ def test_download_multiple_files(self, mock: Mock) -> None: revision=None, allow_patterns=["README.md", "config.json"], ignore_patterns=None, - resume_download=True, force_download=True, cache_dir=None, token="hf_****", @@ -533,11 +524,9 @@ def test_download_with_patterns(self, mock: Mock) -> None: include=["*.json"], exclude=["data/*"], force_download=True, - resume_download=True, cache_dir=None, quiet=False, local_dir=None, - local_dir_use_symlinks=None, max_workers=8, ) DownloadCommand(args).run() @@ -549,7 +538,6 @@ def test_download_with_patterns(self, mock: Mock) -> None: revision=None, allow_patterns=["*.json"], ignore_patterns=["data/*"], - resume_download=True, force_download=True, cache_dir=None, local_dir=None, @@ -573,7 +561,6 @@ def test_download_with_ignored_patterns(self, mock: Mock) -> None: cache_dir=None, quiet=False, local_dir=None, - local_dir_use_symlinks=None, max_workers=8, ) @@ -587,7 +574,6 @@ def test_download_with_ignored_patterns(self, mock: Mock) -> None: revision=None, allow_patterns=["README.md", "config.json"], # `filenames` has priority over the patterns ignore_patterns=None, # cleaned up - resume_download=True, force_download=True, cache_dir=None, token=None,