huggingface-hub 0.12.0rc0__py3-none-any.whl → 0.13.0rc0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. huggingface_hub/__init__.py +166 -126
  2. huggingface_hub/_commit_api.py +25 -51
  3. huggingface_hub/_login.py +4 -13
  4. huggingface_hub/_snapshot_download.py +45 -23
  5. huggingface_hub/_space_api.py +7 -0
  6. huggingface_hub/commands/delete_cache.py +13 -39
  7. huggingface_hub/commands/env.py +1 -3
  8. huggingface_hub/commands/huggingface_cli.py +1 -3
  9. huggingface_hub/commands/lfs.py +4 -8
  10. huggingface_hub/commands/scan_cache.py +5 -16
  11. huggingface_hub/commands/user.py +27 -45
  12. huggingface_hub/community.py +4 -4
  13. huggingface_hub/constants.py +22 -19
  14. huggingface_hub/fastai_utils.py +14 -23
  15. huggingface_hub/file_download.py +210 -121
  16. huggingface_hub/hf_api.py +500 -255
  17. huggingface_hub/hub_mixin.py +181 -176
  18. huggingface_hub/inference_api.py +4 -10
  19. huggingface_hub/keras_mixin.py +39 -71
  20. huggingface_hub/lfs.py +8 -24
  21. huggingface_hub/repocard.py +33 -48
  22. huggingface_hub/repocard_data.py +141 -30
  23. huggingface_hub/repository.py +41 -112
  24. huggingface_hub/templates/modelcard_template.md +39 -34
  25. huggingface_hub/utils/__init__.py +1 -0
  26. huggingface_hub/utils/_cache_assets.py +1 -4
  27. huggingface_hub/utils/_cache_manager.py +17 -39
  28. huggingface_hub/utils/_deprecation.py +8 -12
  29. huggingface_hub/utils/_errors.py +10 -57
  30. huggingface_hub/utils/_fixes.py +2 -6
  31. huggingface_hub/utils/_git_credential.py +5 -16
  32. huggingface_hub/utils/_headers.py +22 -11
  33. huggingface_hub/utils/_http.py +1 -4
  34. huggingface_hub/utils/_paths.py +5 -12
  35. huggingface_hub/utils/_runtime.py +2 -1
  36. huggingface_hub/utils/_telemetry.py +120 -0
  37. huggingface_hub/utils/_validators.py +5 -13
  38. huggingface_hub/utils/endpoint_helpers.py +1 -3
  39. huggingface_hub/utils/logging.py +10 -8
  40. {huggingface_hub-0.12.0rc0.dist-info → huggingface_hub-0.13.0rc0.dist-info}/METADATA +7 -14
  41. huggingface_hub-0.13.0rc0.dist-info/RECORD +56 -0
  42. huggingface_hub/py.typed +0 -0
  43. huggingface_hub-0.12.0rc0.dist-info/RECORD +0 -56
  44. {huggingface_hub-0.12.0rc0.dist-info → huggingface_hub-0.13.0rc0.dist-info}/LICENSE +0 -0
  45. {huggingface_hub-0.12.0rc0.dist-info → huggingface_hub-0.13.0rc0.dist-info}/WHEEL +0 -0
  46. {huggingface_hub-0.12.0rc0.dist-info → huggingface_hub-0.13.0rc0.dist-info}/entry_points.txt +0 -0
  47. {huggingface_hub-0.12.0rc0.dist-info → huggingface_hub-0.13.0rc0.dist-info}/top_level.txt +0 -0
@@ -13,9 +13,9 @@ from .constants import (
13
13
  )
14
14
  from .file_download import REGEX_COMMIT_HASH, hf_hub_download, repo_folder_name
15
15
  from .hf_api import HfApi
16
- from .utils import filter_repo_objects, logging
16
+ from .utils import filter_repo_objects, logging, validate_hf_hub_args
17
17
  from .utils import tqdm as hf_tqdm
18
- from .utils import validate_hf_hub_args
18
+ from .utils._typing import Literal
19
19
 
20
20
 
21
21
  logger = logging.get_logger(__name__)
@@ -28,6 +28,8 @@ def snapshot_download(
28
28
  revision: Optional[str] = None,
29
29
  repo_type: Optional[str] = None,
30
30
  cache_dir: Union[str, Path, None] = None,
31
+ local_dir: Union[str, Path, None] = None,
32
+ local_dir_use_symlinks: Union[bool, Literal["auto"]] = "auto",
31
33
  library_name: Optional[str] = None,
32
34
  library_version: Optional[str] = None,
33
35
  user_agent: Optional[Union[Dict, str]] = None,
@@ -41,15 +43,30 @@ def snapshot_download(
41
43
  max_workers: int = 8,
42
44
  tqdm_class: Optional[base_tqdm] = None,
43
45
  ) -> str:
44
- """Download all files of a repo.
45
-
46
- Downloads a whole snapshot of a repo's files at the specified revision. This
47
- is useful when you want all files from a repo, because you don't know which
48
- ones you will need a priori. All files are nested inside a folder in order
49
- to keep their actual filename relative to that folder.
50
-
51
- An alternative would be to just clone a repo but this would require that the
52
- user always has git and git-lfs installed, and properly configured.
46
+ """Download repo files.
47
+
48
+ Download a whole snapshot of a repo's files at the specified revision. This is useful when you want all files from
49
+ a repo, because you don't know which ones you will need a priori. All files are nested inside a folder in order
50
+ to keep their actual filename relative to that folder. You can also filter which files to download using
51
+ `allow_patterns` and `ignore_patterns`.
52
+
53
+ If `local_dir` is provided, the file structure from the repo will be replicated in this location. You can configure
54
+ how you want to move those files:
55
+ - If `local_dir_use_symlinks="auto"` (default), files are downloaded and stored in the cache directory as blob
56
+ files. Small files (<5MB) are duplicated in `local_dir` while a symlink is created for bigger files. The goal
57
+ is to be able to manually edit and save small files without corrupting the cache while saving disk space for
58
+ binary files. The 5MB threshold can be configured with the `HF_HUB_LOCAL_DIR_AUTO_SYMLINK_THRESHOLD`
59
+ environment variable.
60
+ - If `local_dir_use_symlinks=True`, files are downloaded, stored in the cache directory and symlinked in `local_dir`.
61
+ This is optimal in term of disk usage but files must not be manually edited.
62
+ - If `local_dir_use_symlinks=False` and the blob files exist in the cache directory, they are duplicated in the
63
+ local dir. This means disk usage is not optimized.
64
+ - Finally, if `local_dir_use_symlinks=False` and the blob files do not exist in the cache directory, then the
65
+ files are downloaded and directly placed under `local_dir`. This means if you need to download them again later,
66
+ they will be re-downloaded entirely.
67
+
68
+ An alternative would be to clone the repo but this requires git and git-lfs to be installed and properly
69
+ configured. It is also not possible to filter which files to download when cloning a repository using git.
53
70
 
54
71
  Args:
55
72
  repo_id (`str`):
@@ -58,10 +75,18 @@ def snapshot_download(
58
75
  An optional Git revision id which can be a branch name, a tag, or a
59
76
  commit hash.
60
77
  repo_type (`str`, *optional*):
61
- Set to `"dataset"` or `"space"` if uploading to a dataset or space,
62
- `None` or `"model"` if uploading to a model. Default is `None`.
78
+ Set to `"dataset"` or `"space"` if downloading from a dataset or space,
79
+ `None` or `"model"` if downloading from a model. Default is `None`.
63
80
  cache_dir (`str`, `Path`, *optional*):
64
81
  Path to the folder where cached files are stored.
82
+ local_dir (`str` or `Path`, *optional*:
83
+ If provided, the downloaded files will be placed under this directory, either as symlinks (default) or
84
+ regular files (see description for more details).
85
+ local_dir_use_symlinks (`"auto"` or `bool`, defaults to `"auto"`):
86
+ To be used with `local_dir`. If set to "auto", the cache directory will be used and the file will be either
87
+ duplicated or symlinked to the local directory depending on its size. It set to `True`, a symlink will be
88
+ created, no matter the file size. If set to `False`, the file will either be duplicated from cache (if
89
+ already exists) or downloaded from the Hub and not cached. See description for more details.
65
90
  library_name (`str`, *optional*):
66
91
  The name of the library to which the object corresponds.
67
92
  library_version (`str`, *optional*):
@@ -124,14 +149,9 @@ def snapshot_download(
124
149
  if repo_type is None:
125
150
  repo_type = "model"
126
151
  if repo_type not in REPO_TYPES:
127
- raise ValueError(
128
- f"Invalid repo type: {repo_type}. Accepted repo types are:"
129
- f" {str(REPO_TYPES)}"
130
- )
152
+ raise ValueError(f"Invalid repo type: {repo_type}. Accepted repo types are: {str(REPO_TYPES)}")
131
153
 
132
- storage_folder = os.path.join(
133
- cache_dir, repo_folder_name(repo_id=repo_id, repo_type=repo_type)
134
- )
154
+ storage_folder = os.path.join(cache_dir, repo_folder_name(repo_id=repo_id, repo_type=repo_type))
135
155
 
136
156
  # if we have no internet connection we will look for an
137
157
  # appropriate folder in the cache
@@ -166,9 +186,7 @@ def snapshot_download(
166
186
  revision=revision,
167
187
  token=token,
168
188
  )
169
- assert (
170
- repo_info.sha is not None
171
- ), "Repo info returned from server must have a revision sha."
189
+ assert repo_info.sha is not None, "Repo info returned from server must have a revision sha."
172
190
  filtered_repo_files = list(
173
191
  filter_repo_objects(
174
192
  items=[f.rfilename for f in repo_info.siblings],
@@ -197,6 +215,8 @@ def snapshot_download(
197
215
  repo_type=repo_type,
198
216
  revision=commit_hash,
199
217
  cache_dir=cache_dir,
218
+ local_dir=local_dir,
219
+ local_dir_use_symlinks=local_dir_use_symlinks,
200
220
  library_name=library_name,
201
221
  library_version=library_version,
202
222
  user_agent=user_agent,
@@ -221,4 +241,6 @@ def snapshot_download(
221
241
  tqdm_class=tqdm_class or hf_tqdm,
222
242
  )
223
243
 
244
+ if local_dir is not None:
245
+ return str(os.path.realpath(local_dir))
224
246
  return snapshot_folder
@@ -39,6 +39,7 @@ class SpaceStage(str, Enum):
39
39
  RUNTIME_ERROR = "RUNTIME_ERROR"
40
40
  DELETING = "DELETING"
41
41
  STOPPED = "STOPPED"
42
+ PAUSED = "PAUSED"
42
43
 
43
44
 
44
45
  class SpaceHardware(str, Enum):
@@ -86,3 +87,9 @@ class SpaceRuntime:
86
87
  hardware: Optional[SpaceHardware]
87
88
  requested_hardware: Optional[SpaceHardware]
88
89
  raw: Dict
90
+
91
+ def __init__(self, data: Dict) -> None:
92
+ self.stage = data["stage"]
93
+ self.hardware = data["hardware"]["current"]
94
+ self.requested_hardware = data["hardware"]["requested"]
95
+ self.raw = data
@@ -78,6 +78,7 @@ except ImportError:
78
78
 
79
79
  def require_inquirer_py(fn: Callable) -> Callable:
80
80
  """Decorator to flag methods that require `InquirerPy`."""
81
+
81
82
  # TODO: refactor this + imports in a unified pattern across codebase
82
83
  @wraps(fn)
83
84
  def _inner(*args, **kwargs):
@@ -100,17 +101,13 @@ _CANCEL_DELETION_STR = "CANCEL_DELETION"
100
101
  class DeleteCacheCommand(BaseHuggingfaceCLICommand):
101
102
  @staticmethod
102
103
  def register_subcommand(parser: _SubParsersAction):
103
- delete_cache_parser = parser.add_parser(
104
- "delete-cache", help="Delete revisions from the cache directory."
105
- )
104
+ delete_cache_parser = parser.add_parser("delete-cache", help="Delete revisions from the cache directory.")
106
105
 
107
106
  delete_cache_parser.add_argument(
108
107
  "--dir",
109
108
  type=str,
110
109
  default=None,
111
- help=(
112
- "cache directory (optional). Default to the default HuggingFace cache."
113
- ),
110
+ help="cache directory (optional). Default to the default HuggingFace cache.",
114
111
  )
115
112
 
116
113
  delete_cache_parser.add_argument(
@@ -141,10 +138,7 @@ class DeleteCacheCommand(BaseHuggingfaceCLICommand):
141
138
 
142
139
  # If deletion is not cancelled
143
140
  if len(selected_hashes) > 0 and _CANCEL_DELETION_STR not in selected_hashes:
144
- confirm_message = (
145
- _get_expectations_str(hf_cache_info, selected_hashes)
146
- + " Confirm deletion ?"
147
- )
141
+ confirm_message = _get_expectations_str(hf_cache_info, selected_hashes) + " Confirm deletion ?"
148
142
 
149
143
  # Confirm deletion
150
144
  if self.disable_tui:
@@ -175,9 +169,7 @@ def _manual_review_tui(hf_cache_info: HFCacheInfo, preselected: List[str]) -> Li
175
169
  Displays a multi-select menu in the terminal (TUI).
176
170
  """
177
171
  # Define multiselect list
178
- choices = _get_tui_choices_from_scan(
179
- repos=hf_cache_info.repos, preselected=preselected
180
- )
172
+ choices = _get_tui_choices_from_scan(repos=hf_cache_info.repos, preselected=preselected)
181
173
  checkbox = inquirer.checkbox(
182
174
  message="Select revisions to delete:",
183
175
  choices=choices, # List of revisions with some pre-selection
@@ -187,15 +179,10 @@ def _manual_review_tui(hf_cache_info: HFCacheInfo, preselected: List[str]) -> Li
187
179
  # deletion.
188
180
  instruction=_get_expectations_str(
189
181
  hf_cache_info,
190
- selected_hashes=[
191
- c.value for c in choices if isinstance(c, Choice) and c.enabled
192
- ],
182
+ selected_hashes=[c.value for c in choices if isinstance(c, Choice) and c.enabled],
193
183
  ),
194
184
  # We use the long instruction to should keybindings instructions to the user
195
- long_instruction=(
196
- "Press <space> to select, <enter> to validate and <ctrl+c> to quit"
197
- " without modification."
198
- ),
185
+ long_instruction="Press <space> to select, <enter> to validate and <ctrl+c> to quit without modification.",
199
186
  # Message that is displayed once the user validates its selection.
200
187
  transformer=lambda result: f"{len(result)} revision(s) selected.",
201
188
  )
@@ -207,11 +194,7 @@ def _manual_review_tui(hf_cache_info: HFCacheInfo, preselected: List[str]) -> Li
207
194
  # a revision hash is selected/unselected.
208
195
  checkbox._instruction = _get_expectations_str(
209
196
  hf_cache_info,
210
- selected_hashes=[
211
- choice["value"]
212
- for choice in checkbox.content_control.choices
213
- if choice["enabled"]
214
- ],
197
+ selected_hashes=[choice["value"] for choice in checkbox.content_control.choices if choice["enabled"]],
215
198
  )
216
199
 
217
200
  checkbox.kb_func_lookup["toggle"].append({"func": _update_expectations})
@@ -229,9 +212,7 @@ def _ask_for_confirmation_tui(message: str, default: bool = True) -> bool:
229
212
  return inquirer.confirm(message, default=default).execute()
230
213
 
231
214
 
232
- def _get_tui_choices_from_scan(
233
- repos: Iterable[CachedRepoInfo], preselected: List[str]
234
- ) -> List:
215
+ def _get_tui_choices_from_scan(repos: Iterable[CachedRepoInfo], preselected: List[str]) -> List:
235
216
  """Build a list of choices from the scanned repos.
236
217
 
237
218
  Args:
@@ -282,9 +263,7 @@ def _get_tui_choices_from_scan(
282
263
  return choices
283
264
 
284
265
 
285
- def _manual_review_no_tui(
286
- hf_cache_info: HFCacheInfo, preselected: List[str]
287
- ) -> List[str]:
266
+ def _manual_review_no_tui(hf_cache_info: HFCacheInfo, preselected: List[str]) -> List[str]:
288
267
  """Ask the user for a manual review of the revisions to delete.
289
268
 
290
269
  Used when TUI is disabled. Manual review happens in a separate tmp file that the
@@ -317,7 +296,7 @@ def _manual_review_no_tui(
317
296
 
318
297
  # 2. Prompt instructions to user.
319
298
  instructions = f"""
320
- TUI is disabled. In other to select which revisions you want to delete, please edit
299
+ TUI is disabled. In order to select which revisions you want to delete, please edit
321
300
  the following file using the text editor of your choice. Instructions for manual
322
301
  editing are located at the beginning of the file. Edit the file, save it and confirm
323
302
  to continue.
@@ -357,9 +336,7 @@ def _ask_for_confirmation_no_tui(message: str, default: bool = True) -> bool:
357
336
  print(f"Invalid input. Must be one of {ALL}")
358
337
 
359
338
 
360
- def _get_expectations_str(
361
- hf_cache_info: HFCacheInfo, selected_hashes: List[str]
362
- ) -> str:
339
+ def _get_expectations_str(hf_cache_info: HFCacheInfo, selected_hashes: List[str]) -> str:
363
340
  """Format a string to display to the user how much space would be saved.
364
341
 
365
342
  Example:
@@ -371,10 +348,7 @@ def _get_expectations_str(
371
348
  if _CANCEL_DELETION_STR in selected_hashes:
372
349
  return "Nothing will be deleted."
373
350
  strategy = hf_cache_info.delete_revisions(*selected_hashes)
374
- return (
375
- f"{len(selected_hashes)} revisions selected counting for"
376
- f" {strategy.expected_freed_size_str}."
377
- )
351
+ return f"{len(selected_hashes)} revisions selected counting for {strategy.expected_freed_size_str}."
378
352
 
379
353
 
380
354
  def _read_manual_review_tmp_file(tmp_path: str) -> List[str]:
@@ -28,9 +28,7 @@ class EnvironmentCommand(BaseHuggingfaceCLICommand):
28
28
 
29
29
  @staticmethod
30
30
  def register_subcommand(parser: _SubParsersAction):
31
- env_parser = parser.add_parser(
32
- "env", help="Print information about the environment."
33
- )
31
+ env_parser = parser.add_parser("env", help="Print information about the environment.")
34
32
  env_parser.set_defaults(func=EnvironmentCommand)
35
33
 
36
34
  def run(self) -> None:
@@ -23,9 +23,7 @@ from huggingface_hub.commands.user import UserCommands
23
23
 
24
24
 
25
25
  def main():
26
- parser = ArgumentParser(
27
- "huggingface-cli", usage="huggingface-cli <command> [<args>]"
28
- )
26
+ parser = ArgumentParser("huggingface-cli", usage="huggingface-cli <command> [<args>]")
29
27
  commands_parser = parser.add_subparsers(help="huggingface-cli command helpers")
30
28
 
31
29
  # Register commands
@@ -24,6 +24,7 @@ from argparse import _SubParsersAction
24
24
  from typing import Dict, List, Optional
25
25
 
26
26
  import requests
27
+
27
28
  from huggingface_hub.commands import BaseHuggingfaceCLICommand
28
29
  from huggingface_hub.lfs import LFS_MULTIPART_UPLOAD_COMMAND, SliceFileObj
29
30
 
@@ -60,9 +61,7 @@ class LfsCommands(BaseHuggingfaceCLICommand):
60
61
  "lfs-enable-largefiles",
61
62
  help="Configure your repository to enable upload of files > 5GB.",
62
63
  )
63
- enable_parser.add_argument(
64
- "path", type=str, help="Local path to repository you want to configure."
65
- )
64
+ enable_parser.add_argument("path", type=str, help="Local path to repository you want to configure.")
66
65
  enable_parser.set_defaults(func=lambda args: LfsEnableCommand(args))
67
66
 
68
67
  upload_parser = parser.add_parser(
@@ -87,8 +86,7 @@ class LfsEnableCommand:
87
86
  cwd=local_path,
88
87
  )
89
88
  subprocess.run(
90
- "git config lfs.customtransfer.multipart.args"
91
- f" {LFS_MULTIPART_UPLOAD_COMMAND}".split(),
89
+ f"git config lfs.customtransfer.multipart.args {LFS_MULTIPART_UPLOAD_COMMAND}".split(),
92
90
  check=True,
93
91
  cwd=local_path,
94
92
  )
@@ -126,9 +124,7 @@ class LfsUploadCommand:
126
124
  # sends initiation data to the process over stdin.
127
125
  # This tells the process useful information about the configuration.
128
126
  init_msg = json.loads(sys.stdin.readline().strip())
129
- if not (
130
- init_msg.get("event") == "init" and init_msg.get("operation") == "upload"
131
- ):
127
+ if not (init_msg.get("event") == "init" and init_msg.get("operation") == "upload"):
132
128
  write_msg({"error": {"code": 32, "message": "Wrong lfs init operation"}})
133
129
  sys.exit(1)
134
130
 
@@ -32,18 +32,13 @@ from ._cli_utils import ANSI, tabulate
32
32
  class ScanCacheCommand(BaseHuggingfaceCLICommand):
33
33
  @staticmethod
34
34
  def register_subcommand(parser: _SubParsersAction):
35
- scan_cache_parser = parser.add_parser(
36
- "scan-cache", help="Scan cache directory."
37
- )
35
+ scan_cache_parser = parser.add_parser("scan-cache", help="Scan cache directory.")
38
36
 
39
37
  scan_cache_parser.add_argument(
40
38
  "--dir",
41
39
  type=str,
42
40
  default=None,
43
- help=(
44
- "cache directory to scan (optional). Default to the"
45
- " default HuggingFace cache."
46
- ),
41
+ help="cache directory to scan (optional). Default to the default HuggingFace cache.",
47
42
  )
48
43
  scan_cache_parser.add_argument(
49
44
  "-v",
@@ -98,9 +93,7 @@ class ScanCacheCommand(BaseHuggingfaceCLICommand):
98
93
  ", ".join(sorted(repo.refs)),
99
94
  str(repo.repo_path),
100
95
  ]
101
- for repo in sorted(
102
- hf_cache_info.repos, key=lambda repo: repo.repo_path
103
- )
96
+ for repo in sorted(hf_cache_info.repos, key=lambda repo: repo.repo_path)
104
97
  ],
105
98
  headers=[
106
99
  "REPO ID",
@@ -128,12 +121,8 @@ class ScanCacheCommand(BaseHuggingfaceCLICommand):
128
121
  ", ".join(sorted(revision.refs)),
129
122
  str(revision.snapshot_path),
130
123
  ]
131
- for repo in sorted(
132
- hf_cache_info.repos, key=lambda repo: repo.repo_path
133
- )
134
- for revision in sorted(
135
- repo.revisions, key=lambda revision: revision.commit_hash
136
- )
124
+ for repo in sorted(hf_cache_info.repos, key=lambda repo: repo.repo_path)
125
+ for revision in sorted(repo.revisions, key=lambda revision: revision.commit_hash)
137
126
  ],
138
127
  headers=[
139
128
  "REPO ID",
@@ -14,6 +14,8 @@
14
14
  import subprocess
15
15
  from argparse import _SubParsersAction
16
16
 
17
+ from requests.exceptions import HTTPError
18
+
17
19
  from huggingface_hub.commands import BaseHuggingfaceCLICommand
18
20
  from huggingface_hub.constants import (
19
21
  ENDPOINT,
@@ -22,21 +24,18 @@ from huggingface_hub.constants import (
22
24
  SPACES_SDK_TYPES,
23
25
  )
24
26
  from huggingface_hub.hf_api import HfApi
25
- from requests.exceptions import HTTPError
26
27
 
27
- from .._login import ( # noqa: F401 # for backward compatibility
28
+ from .._login import ( # noqa: F401 # for backward compatibility # noqa: F401 # for backward compatibility
28
29
  NOTEBOOK_LOGIN_PASSWORD_HTML,
29
30
  NOTEBOOK_LOGIN_TOKEN_HTML_END,
30
31
  NOTEBOOK_LOGIN_TOKEN_HTML_START,
31
- )
32
- from .._login import ( # noqa: F401 # for backward compatibility
33
- _currently_setup_credential_helpers as currently_setup_credential_helpers,
34
- )
35
- from .._login import ( # noqa: F401 # for backward compatibility
36
32
  login,
37
33
  logout,
38
34
  notebook_login,
39
35
  )
36
+ from .._login import (
37
+ _currently_setup_credential_helpers as currently_setup_credential_helpers, # noqa: F401 # for backward compatibility
38
+ )
40
39
  from ..utils import HfFolder
41
40
  from ._cli_utils import ANSI
42
41
 
@@ -44,13 +43,19 @@ from ._cli_utils import ANSI
44
43
  class UserCommands(BaseHuggingfaceCLICommand):
45
44
  @staticmethod
46
45
  def register_subcommand(parser: _SubParsersAction):
47
- login_parser = parser.add_parser(
48
- "login", help="Log in using a token from huggingface.co/settings/tokens"
46
+ login_parser = parser.add_parser("login", help="Log in using a token from huggingface.co/settings/tokens")
47
+ login_parser.add_argument(
48
+ "--token",
49
+ type=str,
50
+ help="Token generated from https://huggingface.co/settings/tokens",
49
51
  )
50
- login_parser.set_defaults(func=lambda args: LoginCommand(args))
51
- whoami_parser = parser.add_parser(
52
- "whoami", help="Find out which huggingface.co account you are logged in as."
52
+ login_parser.add_argument(
53
+ "--add-to-git-credential",
54
+ action="store_true",
55
+ help="Optional: Save token to git credential helper.",
53
56
  )
57
+ login_parser.set_defaults(func=lambda args: LoginCommand(args))
58
+ whoami_parser = parser.add_parser("whoami", help="Find out which huggingface.co account you are logged in as.")
54
59
  whoami_parser.set_defaults(func=lambda args: WhoamiCommand(args))
55
60
  logout_parser = parser.add_parser("logout", help="Log out")
56
61
  logout_parser.set_defaults(func=lambda args: LogoutCommand(args))
@@ -58,43 +63,25 @@ class UserCommands(BaseHuggingfaceCLICommand):
58
63
  # new system: git-based repo system
59
64
  repo_parser = parser.add_parser(
60
65
  "repo",
61
- help=(
62
- "{create, ls-files} Commands to interact with your huggingface.co"
63
- " repos."
64
- ),
65
- )
66
- repo_subparsers = repo_parser.add_subparsers(
67
- help="huggingface.co repos related commands"
68
- )
69
- repo_create_parser = repo_subparsers.add_parser(
70
- "create", help="Create a new repo on huggingface.co"
66
+ help="{create, ls-files} Commands to interact with your huggingface.co repos.",
71
67
  )
68
+ repo_subparsers = repo_parser.add_subparsers(help="huggingface.co repos related commands")
69
+ repo_create_parser = repo_subparsers.add_parser("create", help="Create a new repo on huggingface.co")
72
70
  repo_create_parser.add_argument(
73
71
  "name",
74
72
  type=str,
75
- help=(
76
- "Name for your repo. Will be namespaced under your username to build"
77
- " the repo id."
78
- ),
73
+ help="Name for your repo. Will be namespaced under your username to build the repo id.",
79
74
  )
80
75
  repo_create_parser.add_argument(
81
76
  "--type",
82
77
  type=str,
83
- help=(
84
- 'Optional: repo_type: set to "dataset" or "space" if creating a dataset'
85
- " or space, default is model."
86
- ),
87
- )
88
- repo_create_parser.add_argument(
89
- "--organization", type=str, help="Optional: organization namespace."
78
+ help='Optional: repo_type: set to "dataset" or "space" if creating a dataset or space, default is model.',
90
79
  )
80
+ repo_create_parser.add_argument("--organization", type=str, help="Optional: organization namespace.")
91
81
  repo_create_parser.add_argument(
92
82
  "--space_sdk",
93
83
  type=str,
94
- help=(
95
- "Optional: Hugging Face Spaces SDK type. Required when --type is set to"
96
- ' "space".'
97
- ),
84
+ help='Optional: Hugging Face Spaces SDK type. Required when --type is set to "space".',
98
85
  choices=SPACES_SDK_TYPES,
99
86
  )
100
87
  repo_create_parser.add_argument(
@@ -114,7 +101,7 @@ class BaseUserCommand:
114
101
 
115
102
  class LoginCommand(BaseUserCommand):
116
103
  def run(self):
117
- login()
104
+ login(token=self.args.token, add_to_git_credential=self.args.add_to_git_credential)
118
105
 
119
106
 
120
107
  class LogoutCommand(BaseUserCommand):
@@ -169,9 +156,7 @@ class RepoCreateCommand(BaseUserCommand):
169
156
  print("")
170
157
 
171
158
  user = self._api.whoami(token)["name"]
172
- namespace = (
173
- self.args.organization if self.args.organization is not None else user
174
- )
159
+ namespace = self.args.organization if self.args.organization is not None else user
175
160
 
176
161
  repo_id = f"{namespace}/{self.args.name}"
177
162
 
@@ -204,9 +189,6 @@ class RepoCreateCommand(BaseUserCommand):
204
189
  exit(1)
205
190
  print("\nYour repo now lives at:")
206
191
  print(f" {ANSI.bold(url)}")
207
- print(
208
- "\nYou can clone it locally with the command below,"
209
- " and commit/push as usual."
210
- )
192
+ print("\nYou can clone it locally with the command below, and commit/push as usual.")
211
193
  print(f"\n git clone {url}")
212
194
  print("")
@@ -44,7 +44,7 @@ class Discussion:
44
44
  The username of the Discussion / Pull Request author.
45
45
  Can be `"deleted"` if the user has been deleted since.
46
46
  is_pull_request (`bool`):
47
- Wether or not this is a Pull Request.
47
+ Whether or not this is a Pull Request.
48
48
  created_at (`datetime`):
49
49
  The `datetime` of creation of the Discussion / Pull Request.
50
50
  """
@@ -96,7 +96,7 @@ class DiscussionWithDetails(Discussion):
96
96
  The username of the Discussion / Pull Request author.
97
97
  Can be `"deleted"` if the user has been deleted since.
98
98
  is_pull_request (`bool`):
99
- Wether or not this is a Pull Request.
99
+ Whether or not this is a Pull Request.
100
100
  created_at (`datetime`):
101
101
  The `datetime` of creation of the Discussion / Pull Request.
102
102
  events (`list` of [`DiscussionEvent`])
@@ -175,7 +175,7 @@ class DiscussionComment(DiscussionEvent):
175
175
  content (`str`):
176
176
  The raw markdown content of the comment. Mentions, links and images are not rendered.
177
177
  edited (`bool`):
178
- Wether or not this comment has been edited.
178
+ Whether or not this comment has been edited.
179
179
  hidden (`bool`):
180
180
  Whether or not this comment has been hidden.
181
181
  """
@@ -196,7 +196,7 @@ class DiscussionComment(DiscussionEvent):
196
196
 
197
197
  @property
198
198
  def last_edited_by(self) -> str:
199
- """The last edit tiem, as a `datetime` object."""
199
+ """The last edit time, as a `datetime` object."""
200
200
  return self._event["data"]["latest"].get("author", {}).get("name", "deleted")
201
201
 
202
202
  @property
@@ -15,10 +15,10 @@ def _is_true(value: Optional[str]) -> bool:
15
15
  return value.upper() in ENV_VARS_TRUE_VALUES
16
16
 
17
17
 
18
- def _is_true_or_auto(value: Optional[str]) -> bool:
18
+ def _as_int(value: Optional[str]) -> Optional[int]:
19
19
  if value is None:
20
- return False
21
- return value.upper() in ENV_VARS_TRUE_AND_AUTO_VALUES
20
+ return None
21
+ return int(value)
22
22
 
23
23
 
24
24
  # Constants for file downloads
@@ -39,15 +39,15 @@ HUGGINGFACE_CO_URL_HOME = "https://huggingface.co/"
39
39
 
40
40
  _staging_mode = _is_true(os.environ.get("HUGGINGFACE_CO_STAGING"))
41
41
 
42
- ENDPOINT = os.getenv("HF_ENDPOINT") or (
43
- "https://hub-ci.huggingface.co" if _staging_mode else "https://huggingface.co"
44
- )
42
+ ENDPOINT = os.getenv("HF_ENDPOINT") or ("https://hub-ci.huggingface.co" if _staging_mode else "https://huggingface.co")
45
43
 
46
44
  HUGGINGFACE_CO_URL_TEMPLATE = ENDPOINT + "/{repo_id}/resolve/{revision}/{filename}"
47
45
  HUGGINGFACE_HEADER_X_REPO_COMMIT = "X-Repo-Commit"
48
46
  HUGGINGFACE_HEADER_X_LINKED_ETAG = "X-Linked-Etag"
49
47
  HUGGINGFACE_HEADER_X_LINKED_SIZE = "X-Linked-Size"
50
48
 
49
+ INFERENCE_ENDPOINT = os.environ.get("HF_INFERENCE_ENDPOINT", "https://api-inference.huggingface.co")
50
+
51
51
  REPO_ID_SEPARATOR = "--"
52
52
  # ^ this substring is not allowed in repo_ids on hf.co
53
53
  # and is the canonical one we use for serialization of repo ids elsewhere.
@@ -83,11 +83,12 @@ default_cache_path = os.path.join(hf_cache_home, "hub")
83
83
  default_assets_cache_path = os.path.join(hf_cache_home, "assets")
84
84
 
85
85
  HUGGINGFACE_HUB_CACHE = os.getenv("HUGGINGFACE_HUB_CACHE", default_cache_path)
86
- HUGGINGFACE_ASSETS_CACHE = os.getenv(
87
- "HUGGINGFACE_ASSETS_CACHE", default_assets_cache_path
88
- )
86
+ HUGGINGFACE_ASSETS_CACHE = os.getenv("HUGGINGFACE_ASSETS_CACHE", default_assets_cache_path)
87
+
88
+ HF_HUB_OFFLINE = _is_true(os.environ.get("HF_HUB_OFFLINE") or os.environ.get("TRANSFORMERS_OFFLINE"))
89
89
 
90
- HF_HUB_OFFLINE = _is_true(os.environ.get("HF_HUB_OFFLINE"))
90
+ # Opt-out from telemetry requests
91
+ HF_HUB_DISABLE_TELEMETRY = _is_true(os.environ.get("HF_HUB_DISABLE_TELEMETRY") or os.environ.get("DISABLE_TELEMETRY"))
91
92
 
92
93
  # In the past, token was stored in a hardcoded location
93
94
  # `_OLD_HF_TOKEN_PATH` is deprecated and will be removed "at some point".
@@ -103,23 +104,25 @@ HF_TOKEN_PATH = os.path.join(hf_cache_home, "token")
103
104
  # TL;DR: env variable has priority over code
104
105
  __HF_HUB_DISABLE_PROGRESS_BARS = os.environ.get("HF_HUB_DISABLE_PROGRESS_BARS")
105
106
  HF_HUB_DISABLE_PROGRESS_BARS: Optional[bool] = (
106
- _is_true(__HF_HUB_DISABLE_PROGRESS_BARS)
107
- if __HF_HUB_DISABLE_PROGRESS_BARS is not None
108
- else None
107
+ _is_true(__HF_HUB_DISABLE_PROGRESS_BARS) if __HF_HUB_DISABLE_PROGRESS_BARS is not None else None
109
108
  )
110
109
 
111
110
  # Disable warning on machines that do not support symlinks (e.g. Windows non-developer)
112
- HF_HUB_DISABLE_SYMLINKS_WARNING: bool = _is_true(
113
- os.environ.get("HF_HUB_DISABLE_SYMLINKS_WARNING")
114
- )
111
+ HF_HUB_DISABLE_SYMLINKS_WARNING: bool = _is_true(os.environ.get("HF_HUB_DISABLE_SYMLINKS_WARNING"))
115
112
 
116
113
  # Disable sending the cached token by default is all HTTP requests to the Hub
117
- HF_HUB_DISABLE_IMPLICIT_TOKEN: bool = _is_true(
118
- os.environ.get("HF_HUB_DISABLE_IMPLICIT_TOKEN")
119
- )
114
+ HF_HUB_DISABLE_IMPLICIT_TOKEN: bool = _is_true(os.environ.get("HF_HUB_DISABLE_IMPLICIT_TOKEN"))
120
115
 
121
116
  # Enable fast-download using external dependency "hf_transfer"
122
117
  # See:
123
118
  # - https://pypi.org/project/hf-transfer/
124
119
  # - https://github.com/huggingface/hf_transfer (private)
125
120
  HF_HUB_ENABLE_HF_TRANSFER: bool = _is_true(os.environ.get("HF_HUB_ENABLE_HF_TRANSFER"))
121
+
122
+
123
+ # Used if download to `local_dir` and `local_dir_use_symlinks="auto"`
124
+ # Files smaller than 5MB are copy-pasted while bigger files are symlinked. The idea is to save disk-usage by symlinking
125
+ # huge files (i.e. LFS files most of the time) while allowing small files to be manually edited in local folder.
126
+ HF_HUB_LOCAL_DIR_AUTO_SYMLINK_THRESHOLD: int = (
127
+ _as_int(os.environ.get("HF_HUB_LOCAL_DIR_AUTO_SYMLINK_THRESHOLD")) or 5 * 1024 * 1024
128
+ )