huggingface-hub 0.29.0rc2__py3-none-any.whl → 1.1.3__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 (153) hide show
  1. huggingface_hub/__init__.py +160 -46
  2. huggingface_hub/_commit_api.py +277 -71
  3. huggingface_hub/_commit_scheduler.py +15 -15
  4. huggingface_hub/_inference_endpoints.py +33 -22
  5. huggingface_hub/_jobs_api.py +301 -0
  6. huggingface_hub/_local_folder.py +18 -3
  7. huggingface_hub/_login.py +31 -63
  8. huggingface_hub/_oauth.py +460 -0
  9. huggingface_hub/_snapshot_download.py +241 -81
  10. huggingface_hub/_space_api.py +18 -10
  11. huggingface_hub/_tensorboard_logger.py +15 -19
  12. huggingface_hub/_upload_large_folder.py +196 -76
  13. huggingface_hub/_webhooks_payload.py +3 -3
  14. huggingface_hub/_webhooks_server.py +15 -25
  15. huggingface_hub/{commands → cli}/__init__.py +1 -15
  16. huggingface_hub/cli/_cli_utils.py +173 -0
  17. huggingface_hub/cli/auth.py +147 -0
  18. huggingface_hub/cli/cache.py +841 -0
  19. huggingface_hub/cli/download.py +189 -0
  20. huggingface_hub/cli/hf.py +60 -0
  21. huggingface_hub/cli/inference_endpoints.py +377 -0
  22. huggingface_hub/cli/jobs.py +772 -0
  23. huggingface_hub/cli/lfs.py +175 -0
  24. huggingface_hub/cli/repo.py +315 -0
  25. huggingface_hub/cli/repo_files.py +94 -0
  26. huggingface_hub/{commands/env.py → cli/system.py} +10 -13
  27. huggingface_hub/cli/upload.py +294 -0
  28. huggingface_hub/cli/upload_large_folder.py +117 -0
  29. huggingface_hub/community.py +20 -12
  30. huggingface_hub/constants.py +83 -59
  31. huggingface_hub/dataclasses.py +609 -0
  32. huggingface_hub/errors.py +99 -30
  33. huggingface_hub/fastai_utils.py +30 -41
  34. huggingface_hub/file_download.py +606 -346
  35. huggingface_hub/hf_api.py +2445 -1132
  36. huggingface_hub/hf_file_system.py +269 -152
  37. huggingface_hub/hub_mixin.py +61 -66
  38. huggingface_hub/inference/_client.py +501 -630
  39. huggingface_hub/inference/_common.py +133 -121
  40. huggingface_hub/inference/_generated/_async_client.py +536 -722
  41. huggingface_hub/inference/_generated/types/__init__.py +6 -1
  42. huggingface_hub/inference/_generated/types/automatic_speech_recognition.py +5 -6
  43. huggingface_hub/inference/_generated/types/base.py +10 -7
  44. huggingface_hub/inference/_generated/types/chat_completion.py +77 -31
  45. huggingface_hub/inference/_generated/types/depth_estimation.py +2 -2
  46. huggingface_hub/inference/_generated/types/document_question_answering.py +2 -2
  47. huggingface_hub/inference/_generated/types/feature_extraction.py +2 -2
  48. huggingface_hub/inference/_generated/types/fill_mask.py +2 -2
  49. huggingface_hub/inference/_generated/types/image_to_image.py +8 -2
  50. huggingface_hub/inference/_generated/types/image_to_text.py +2 -3
  51. huggingface_hub/inference/_generated/types/image_to_video.py +60 -0
  52. huggingface_hub/inference/_generated/types/sentence_similarity.py +3 -3
  53. huggingface_hub/inference/_generated/types/summarization.py +2 -2
  54. huggingface_hub/inference/_generated/types/table_question_answering.py +5 -5
  55. huggingface_hub/inference/_generated/types/text2text_generation.py +2 -2
  56. huggingface_hub/inference/_generated/types/text_generation.py +11 -11
  57. huggingface_hub/inference/_generated/types/text_to_audio.py +1 -2
  58. huggingface_hub/inference/_generated/types/text_to_speech.py +1 -2
  59. huggingface_hub/inference/_generated/types/text_to_video.py +2 -2
  60. huggingface_hub/inference/_generated/types/token_classification.py +2 -2
  61. huggingface_hub/inference/_generated/types/translation.py +2 -2
  62. huggingface_hub/inference/_generated/types/zero_shot_classification.py +2 -2
  63. huggingface_hub/inference/_generated/types/zero_shot_image_classification.py +2 -2
  64. huggingface_hub/inference/_generated/types/zero_shot_object_detection.py +1 -3
  65. huggingface_hub/inference/_mcp/__init__.py +0 -0
  66. huggingface_hub/inference/_mcp/_cli_hacks.py +88 -0
  67. huggingface_hub/inference/_mcp/agent.py +100 -0
  68. huggingface_hub/inference/_mcp/cli.py +247 -0
  69. huggingface_hub/inference/_mcp/constants.py +81 -0
  70. huggingface_hub/inference/_mcp/mcp_client.py +395 -0
  71. huggingface_hub/inference/_mcp/types.py +45 -0
  72. huggingface_hub/inference/_mcp/utils.py +128 -0
  73. huggingface_hub/inference/_providers/__init__.py +149 -20
  74. huggingface_hub/inference/_providers/_common.py +160 -37
  75. huggingface_hub/inference/_providers/black_forest_labs.py +12 -9
  76. huggingface_hub/inference/_providers/cerebras.py +6 -0
  77. huggingface_hub/inference/_providers/clarifai.py +13 -0
  78. huggingface_hub/inference/_providers/cohere.py +32 -0
  79. huggingface_hub/inference/_providers/fal_ai.py +231 -22
  80. huggingface_hub/inference/_providers/featherless_ai.py +38 -0
  81. huggingface_hub/inference/_providers/fireworks_ai.py +22 -1
  82. huggingface_hub/inference/_providers/groq.py +9 -0
  83. huggingface_hub/inference/_providers/hf_inference.py +143 -33
  84. huggingface_hub/inference/_providers/hyperbolic.py +9 -5
  85. huggingface_hub/inference/_providers/nebius.py +47 -5
  86. huggingface_hub/inference/_providers/novita.py +48 -5
  87. huggingface_hub/inference/_providers/nscale.py +44 -0
  88. huggingface_hub/inference/_providers/openai.py +25 -0
  89. huggingface_hub/inference/_providers/publicai.py +6 -0
  90. huggingface_hub/inference/_providers/replicate.py +46 -9
  91. huggingface_hub/inference/_providers/sambanova.py +37 -1
  92. huggingface_hub/inference/_providers/scaleway.py +28 -0
  93. huggingface_hub/inference/_providers/together.py +34 -5
  94. huggingface_hub/inference/_providers/wavespeed.py +138 -0
  95. huggingface_hub/inference/_providers/zai_org.py +17 -0
  96. huggingface_hub/lfs.py +33 -100
  97. huggingface_hub/repocard.py +34 -38
  98. huggingface_hub/repocard_data.py +79 -59
  99. huggingface_hub/serialization/__init__.py +0 -1
  100. huggingface_hub/serialization/_base.py +12 -15
  101. huggingface_hub/serialization/_dduf.py +8 -8
  102. huggingface_hub/serialization/_torch.py +69 -69
  103. huggingface_hub/utils/__init__.py +27 -8
  104. huggingface_hub/utils/_auth.py +7 -7
  105. huggingface_hub/utils/_cache_manager.py +92 -147
  106. huggingface_hub/utils/_chunk_utils.py +2 -3
  107. huggingface_hub/utils/_deprecation.py +1 -1
  108. huggingface_hub/utils/_dotenv.py +55 -0
  109. huggingface_hub/utils/_experimental.py +7 -5
  110. huggingface_hub/utils/_fixes.py +0 -10
  111. huggingface_hub/utils/_git_credential.py +5 -5
  112. huggingface_hub/utils/_headers.py +8 -30
  113. huggingface_hub/utils/_http.py +399 -237
  114. huggingface_hub/utils/_pagination.py +6 -6
  115. huggingface_hub/utils/_parsing.py +98 -0
  116. huggingface_hub/utils/_paths.py +5 -5
  117. huggingface_hub/utils/_runtime.py +74 -22
  118. huggingface_hub/utils/_safetensors.py +21 -21
  119. huggingface_hub/utils/_subprocess.py +13 -11
  120. huggingface_hub/utils/_telemetry.py +4 -4
  121. huggingface_hub/{commands/_cli_utils.py → utils/_terminal.py} +4 -4
  122. huggingface_hub/utils/_typing.py +25 -5
  123. huggingface_hub/utils/_validators.py +55 -74
  124. huggingface_hub/utils/_verification.py +167 -0
  125. huggingface_hub/utils/_xet.py +235 -0
  126. huggingface_hub/utils/_xet_progress_reporting.py +162 -0
  127. huggingface_hub/utils/insecure_hashlib.py +3 -5
  128. huggingface_hub/utils/logging.py +8 -11
  129. huggingface_hub/utils/tqdm.py +33 -4
  130. {huggingface_hub-0.29.0rc2.dist-info → huggingface_hub-1.1.3.dist-info}/METADATA +94 -82
  131. huggingface_hub-1.1.3.dist-info/RECORD +155 -0
  132. {huggingface_hub-0.29.0rc2.dist-info → huggingface_hub-1.1.3.dist-info}/WHEEL +1 -1
  133. huggingface_hub-1.1.3.dist-info/entry_points.txt +6 -0
  134. huggingface_hub/commands/delete_cache.py +0 -428
  135. huggingface_hub/commands/download.py +0 -200
  136. huggingface_hub/commands/huggingface_cli.py +0 -61
  137. huggingface_hub/commands/lfs.py +0 -200
  138. huggingface_hub/commands/repo_files.py +0 -128
  139. huggingface_hub/commands/scan_cache.py +0 -181
  140. huggingface_hub/commands/tag.py +0 -159
  141. huggingface_hub/commands/upload.py +0 -299
  142. huggingface_hub/commands/upload_large_folder.py +0 -129
  143. huggingface_hub/commands/user.py +0 -304
  144. huggingface_hub/commands/version.py +0 -37
  145. huggingface_hub/inference_api.py +0 -217
  146. huggingface_hub/keras_mixin.py +0 -500
  147. huggingface_hub/repository.py +0 -1477
  148. huggingface_hub/serialization/_tensorflow.py +0 -95
  149. huggingface_hub/utils/_hf_folder.py +0 -68
  150. huggingface_hub-0.29.0rc2.dist-info/RECORD +0 -131
  151. huggingface_hub-0.29.0rc2.dist-info/entry_points.txt +0 -6
  152. {huggingface_hub-0.29.0rc2.dist-info → huggingface_hub-1.1.3.dist-info/licenses}/LICENSE +0 -0
  153. {huggingface_hub-0.29.0rc2.dist-info → huggingface_hub-1.1.3.dist-info}/top_level.txt +0 -0
@@ -14,17 +14,17 @@
14
14
  # limitations under the License.
15
15
  """Contains utilities to handle pagination on Huggingface Hub."""
16
16
 
17
- from typing import Dict, Iterable, Optional
17
+ from typing import Iterable, Optional
18
18
 
19
- import requests
19
+ import httpx
20
20
 
21
- from . import get_session, hf_raise_for_status, logging
21
+ from . import get_session, hf_raise_for_status, http_backoff, logging
22
22
 
23
23
 
24
24
  logger = logging.get_logger(__name__)
25
25
 
26
26
 
27
- def paginate(path: str, params: Dict, headers: Dict) -> Iterable:
27
+ def paginate(path: str, params: dict, headers: dict) -> Iterable:
28
28
  """Fetch a list of models/datasets/spaces and paginate through results.
29
29
 
30
30
  This is using the same "Link" header format as GitHub.
@@ -42,11 +42,11 @@ def paginate(path: str, params: Dict, headers: Dict) -> Iterable:
42
42
  next_page = _get_next_page(r)
43
43
  while next_page is not None:
44
44
  logger.debug(f"Pagination detected. Requesting next page: {next_page}")
45
- r = session.get(next_page, headers=headers)
45
+ r = http_backoff("GET", next_page, max_retries=20, retry_on_status_codes=429, headers=headers)
46
46
  hf_raise_for_status(r)
47
47
  yield from r.json()
48
48
  next_page = _get_next_page(r)
49
49
 
50
50
 
51
- def _get_next_page(response: requests.Response) -> Optional[str]:
51
+ def _get_next_page(response: httpx.Response) -> Optional[str]:
52
52
  return response.links.get("next", {}).get("url")
@@ -0,0 +1,98 @@
1
+ # coding=utf-8
2
+ # Copyright 2025-present, the HuggingFace Inc. team.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ """Parsing helpers shared across modules."""
16
+
17
+ import re
18
+ import time
19
+ from typing import Dict
20
+
21
+
22
+ RE_NUMBER_WITH_UNIT = re.compile(r"(\d+)([a-z]+)", re.IGNORECASE)
23
+
24
+ BYTE_UNITS: Dict[str, int] = {
25
+ "k": 1_000,
26
+ "m": 1_000_000,
27
+ "g": 1_000_000_000,
28
+ "t": 1_000_000_000_000,
29
+ "p": 1_000_000_000_000_000,
30
+ }
31
+
32
+ TIME_UNITS: Dict[str, int] = {
33
+ "s": 1,
34
+ "m": 60,
35
+ "h": 60 * 60,
36
+ "d": 24 * 60 * 60,
37
+ "w": 7 * 24 * 60 * 60,
38
+ "mo": 30 * 24 * 60 * 60,
39
+ "y": 365 * 24 * 60 * 60,
40
+ }
41
+
42
+
43
+ def parse_size(value: str) -> int:
44
+ """Parse a size expressed as a string with digits and unit (like `"10MB"`) to an integer (in bytes)."""
45
+ return _parse_with_unit(value, BYTE_UNITS)
46
+
47
+
48
+ def parse_duration(value: str) -> int:
49
+ """Parse a duration expressed as a string with digits and unit (like `"10s"`) to an integer (in seconds)."""
50
+ return _parse_with_unit(value, TIME_UNITS)
51
+
52
+
53
+ def _parse_with_unit(value: str, units: Dict[str, int]) -> int:
54
+ """Parse a numeric value with optional unit."""
55
+ stripped = value.strip()
56
+ if not stripped:
57
+ raise ValueError("Value cannot be empty.")
58
+ try:
59
+ return int(value)
60
+ except ValueError:
61
+ pass
62
+
63
+ match = RE_NUMBER_WITH_UNIT.fullmatch(stripped)
64
+ if not match:
65
+ raise ValueError(f"Invalid value '{value}'. Must match pattern '\\d+[a-z]+' or be a plain number.")
66
+
67
+ number = int(match.group(1))
68
+ unit = match.group(2).lower()
69
+
70
+ if unit not in units:
71
+ raise ValueError(f"Unknown unit '{unit}'. Must be one of {list(units.keys())}.")
72
+
73
+ return number * units[unit]
74
+
75
+
76
+ def format_timesince(ts: float) -> str:
77
+ """Format timestamp in seconds into a human-readable string, relative to now.
78
+
79
+ Vaguely inspired by Django's `timesince` formatter.
80
+ """
81
+ _TIMESINCE_CHUNKS = (
82
+ # Label, divider, max value
83
+ ("second", 1, 60),
84
+ ("minute", 60, 60),
85
+ ("hour", 60 * 60, 24),
86
+ ("day", 60 * 60 * 24, 6),
87
+ ("week", 60 * 60 * 24 * 7, 6),
88
+ ("month", 60 * 60 * 24 * 30, 11),
89
+ ("year", 60 * 60 * 24 * 365, None),
90
+ )
91
+ delta = time.time() - ts
92
+ if delta < 20:
93
+ return "a few seconds ago"
94
+ for label, divider, max_value in _TIMESINCE_CHUNKS: # noqa: B007
95
+ value = round(delta / divider)
96
+ if max_value is not None and value <= max_value:
97
+ break
98
+ return f"{value} {label}{'s' if value > 1 else ''} ago"
@@ -16,7 +16,7 @@
16
16
 
17
17
  from fnmatch import fnmatch
18
18
  from pathlib import Path
19
- from typing import Callable, Generator, Iterable, List, Optional, TypeVar, Union
19
+ from typing import Callable, Generator, Iterable, Optional, TypeVar, Union
20
20
 
21
21
 
22
22
  T = TypeVar("T")
@@ -39,8 +39,8 @@ FORBIDDEN_FOLDERS = [".git", ".cache"]
39
39
  def filter_repo_objects(
40
40
  items: Iterable[T],
41
41
  *,
42
- allow_patterns: Optional[Union[List[str], str]] = None,
43
- ignore_patterns: Optional[Union[List[str], str]] = None,
42
+ allow_patterns: Optional[Union[list[str], str]] = None,
43
+ ignore_patterns: Optional[Union[list[str], str]] = None,
44
44
  key: Optional[Callable[[T], str]] = None,
45
45
  ) -> Generator[T, None, None]:
46
46
  """Filter repo objects based on an allowlist and a denylist.
@@ -55,10 +55,10 @@ def filter_repo_objects(
55
55
  Args:
56
56
  items (`Iterable`):
57
57
  List of items to filter.
58
- allow_patterns (`str` or `List[str]`, *optional*):
58
+ allow_patterns (`str` or `list[str]`, *optional*):
59
59
  Patterns constituting the allowlist. If provided, item paths must match at
60
60
  least one pattern from the allowlist.
61
- ignore_patterns (`str` or `List[str]`, *optional*):
61
+ ignore_patterns (`str` or `list[str]`, *optional*):
62
62
  Patterns constituting the denylist. If provided, item paths must not match
63
63
  any patterns from the denylist.
64
64
  key (`Callable[[T], str]`, *optional*):
@@ -19,7 +19,8 @@ import os
19
19
  import platform
20
20
  import sys
21
21
  import warnings
22
- from typing import Any, Dict
22
+ from pathlib import Path
23
+ from typing import Any, Literal
23
24
 
24
25
  from .. import __version__, constants
25
26
 
@@ -35,8 +36,9 @@ _CANDIDATES = {
35
36
  "fastcore": {"fastcore"},
36
37
  "gradio": {"gradio"},
37
38
  "graphviz": {"graphviz"},
38
- "hf_transfer": {"hf_transfer"},
39
+ "hf_xet": {"hf_xet"},
39
40
  "jinja": {"Jinja2"},
41
+ "httpx": {"httpx"},
40
42
  "keras": {"keras"},
41
43
  "numpy": {"numpy"},
42
44
  "pillow": {"Pillow"},
@@ -142,13 +144,26 @@ def get_graphviz_version() -> str:
142
144
  return _get_version("graphviz")
143
145
 
144
146
 
145
- # hf_transfer
146
- def is_hf_transfer_available() -> bool:
147
- return is_package_available("hf_transfer")
147
+ # httpx
148
+ def is_httpx_available() -> bool:
149
+ return is_package_available("httpx")
148
150
 
149
151
 
150
- def get_hf_transfer_version() -> str:
151
- return _get_version("hf_transfer")
152
+ def get_httpx_version() -> str:
153
+ return _get_version("httpx")
154
+
155
+
156
+ # xet
157
+ def is_xet_available() -> bool:
158
+ # since hf_xet is automatically used if available, allow explicit disabling via environment variable
159
+ if constants.HF_HUB_DISABLE_XET:
160
+ return False
161
+
162
+ return is_package_available("hf_xet")
163
+
164
+
165
+ def get_xet_version() -> str:
166
+ return _get_version("hf_xet")
152
167
 
153
168
 
154
169
  # keras
@@ -298,7 +313,50 @@ def is_colab_enterprise() -> bool:
298
313
  return os.environ.get("VERTEX_PRODUCT") == "COLAB_ENTERPRISE"
299
314
 
300
315
 
301
- def dump_environment_info() -> Dict[str, Any]:
316
+ # Check how huggingface_hub has been installed
317
+
318
+
319
+ def installation_method() -> Literal["brew", "hf_installer", "unknown"]:
320
+ """Return the installation method of the current environment.
321
+
322
+ - "hf_installer" if installed via the official installer script
323
+ - "brew" if installed via Homebrew
324
+ - "unknown" otherwise
325
+ """
326
+ if _is_brew_installation():
327
+ return "brew"
328
+ elif _is_hf_installer_installation():
329
+ return "hf_installer"
330
+ else:
331
+ return "unknown"
332
+
333
+
334
+ def _is_brew_installation() -> bool:
335
+ """Check if running from a Homebrew installation.
336
+
337
+ Note: AI-generated by Claude.
338
+ """
339
+ exe_path = Path(sys.executable).resolve()
340
+ exe_str = str(exe_path)
341
+
342
+ # Check common Homebrew paths
343
+ # /opt/homebrew (Apple Silicon), /usr/local (Intel)
344
+ return "/Cellar/" in exe_str or "/opt/homebrew/" in exe_str or exe_str.startswith("/usr/local/Cellar/")
345
+
346
+
347
+ def _is_hf_installer_installation() -> bool:
348
+ """Return `True` if the current environment was set up via the official hf installer script.
349
+
350
+ i.e. using one of
351
+ curl -LsSf https://hf.co/cli/install.sh | bash
352
+ powershell -ExecutionPolicy ByPass -c "irm https://hf.co/cli/install.ps1 | iex"
353
+ """
354
+ venv = sys.prefix # points to venv root if active
355
+ marker = Path(venv) / ".hf_installer_marker"
356
+ return marker.exists()
357
+
358
+
359
+ def dump_environment_info() -> dict[str, Any]:
302
360
  """Dump information about the machine to help debugging issues.
303
361
 
304
362
  Similar helper exist in:
@@ -312,7 +370,7 @@ def dump_environment_info() -> Dict[str, Any]:
312
370
  token = get_token()
313
371
 
314
372
  # Generic machine info
315
- info: Dict[str, Any] = {
373
+ info: dict[str, Any] = {
316
374
  "huggingface_hub version": get_hf_hub_version(),
317
375
  "Platform": platform.platform(),
318
376
  "Python version": get_python_version(),
@@ -342,21 +400,14 @@ def dump_environment_info() -> Dict[str, Any]:
342
400
  except Exception:
343
401
  pass
344
402
 
403
+ # How huggingface_hub has been installed?
404
+ info["Installation method"] = installation_method()
405
+
345
406
  # Installed dependencies
346
- info["FastAI"] = get_fastai_version()
347
- info["Tensorflow"] = get_tf_version()
348
- info["Torch"] = get_torch_version()
349
- info["Jinja2"] = get_jinja_version()
350
- info["Graphviz"] = get_graphviz_version()
351
- info["keras"] = get_keras_version()
352
- info["Pydot"] = get_pydot_version()
353
- info["Pillow"] = get_pillow_version()
354
- info["hf_transfer"] = get_hf_transfer_version()
407
+ info["httpx"] = get_httpx_version()
408
+ info["hf_xet"] = get_xet_version()
355
409
  info["gradio"] = get_gradio_version()
356
410
  info["tensorboard"] = get_tensorboard_version()
357
- info["numpy"] = get_numpy_version()
358
- info["pydantic"] = get_pydantic_version()
359
- info["aiohttp"] = get_aiohttp_version()
360
411
 
361
412
  # Environment variables
362
413
  info["ENDPOINT"] = constants.ENDPOINT
@@ -370,9 +421,10 @@ def dump_environment_info() -> Dict[str, Any]:
370
421
  info["HF_HUB_DISABLE_SYMLINKS_WARNING"] = constants.HF_HUB_DISABLE_SYMLINKS_WARNING
371
422
  info["HF_HUB_DISABLE_EXPERIMENTAL_WARNING"] = constants.HF_HUB_DISABLE_EXPERIMENTAL_WARNING
372
423
  info["HF_HUB_DISABLE_IMPLICIT_TOKEN"] = constants.HF_HUB_DISABLE_IMPLICIT_TOKEN
373
- info["HF_HUB_ENABLE_HF_TRANSFER"] = constants.HF_HUB_ENABLE_HF_TRANSFER
424
+ info["HF_HUB_DISABLE_XET"] = constants.HF_HUB_DISABLE_XET
374
425
  info["HF_HUB_ETAG_TIMEOUT"] = constants.HF_HUB_ETAG_TIMEOUT
375
426
  info["HF_HUB_DOWNLOAD_TIMEOUT"] = constants.HF_HUB_DOWNLOAD_TIMEOUT
427
+ info["HF_XET_HIGH_PERFORMANCE"] = constants.HF_XET_HIGH_PERFORMANCE
376
428
 
377
429
  print("\nCopy-and-paste the text below in your GitHub issue.\n")
378
430
  print("\n".join([f"- {prop}: {val}" for prop, val in info.items()]) + "\n")
@@ -2,7 +2,7 @@ import functools
2
2
  import operator
3
3
  from collections import defaultdict
4
4
  from dataclasses import dataclass, field
5
- from typing import Dict, List, Literal, Optional, Tuple
5
+ from typing import Literal, Optional
6
6
 
7
7
 
8
8
  FILENAME_T = str
@@ -19,17 +19,17 @@ class TensorInfo:
19
19
  Attributes:
20
20
  dtype (`str`):
21
21
  The data type of the tensor ("F64", "F32", "F16", "BF16", "I64", "I32", "I16", "I8", "U8", "BOOL").
22
- shape (`List[int]`):
22
+ shape (`list[int]`):
23
23
  The shape of the tensor.
24
- data_offsets (`Tuple[int, int]`):
24
+ data_offsets (`tuple[int, int]`):
25
25
  The offsets of the data in the file as a tuple `[BEGIN, END]`.
26
26
  parameter_count (`int`):
27
27
  The number of parameters in the tensor.
28
28
  """
29
29
 
30
30
  dtype: DTYPE_T
31
- shape: List[int]
32
- data_offsets: Tuple[int, int]
31
+ shape: list[int]
32
+ data_offsets: tuple[int, int]
33
33
  parameter_count: int = field(init=False)
34
34
 
35
35
  def __post_init__(self) -> None:
@@ -49,22 +49,22 @@ class SafetensorsFileMetadata:
49
49
  For more details regarding the safetensors format, check out https://huggingface.co/docs/safetensors/index#format.
50
50
 
51
51
  Attributes:
52
- metadata (`Dict`):
52
+ metadata (`dict`):
53
53
  The metadata contained in the file.
54
- tensors (`Dict[str, TensorInfo]`):
54
+ tensors (`dict[str, TensorInfo]`):
55
55
  A map of all tensors. Keys are tensor names and values are information about the corresponding tensor, as a
56
56
  [`TensorInfo`] object.
57
- parameter_count (`Dict[str, int]`):
57
+ parameter_count (`dict[str, int]`):
58
58
  A map of the number of parameters per data type. Keys are data types and values are the number of parameters
59
59
  of that data type.
60
60
  """
61
61
 
62
- metadata: Dict[str, str]
63
- tensors: Dict[TENSOR_NAME_T, TensorInfo]
64
- parameter_count: Dict[DTYPE_T, int] = field(init=False)
62
+ metadata: dict[str, str]
63
+ tensors: dict[TENSOR_NAME_T, TensorInfo]
64
+ parameter_count: dict[DTYPE_T, int] = field(init=False)
65
65
 
66
66
  def __post_init__(self) -> None:
67
- parameter_count: Dict[DTYPE_T, int] = defaultdict(int)
67
+ parameter_count: dict[DTYPE_T, int] = defaultdict(int)
68
68
  for tensor in self.tensors.values():
69
69
  parameter_count[tensor.dtype] += tensor.parameter_count
70
70
  self.parameter_count = dict(parameter_count)
@@ -82,29 +82,29 @@ class SafetensorsRepoMetadata:
82
82
  For more details regarding the safetensors format, check out https://huggingface.co/docs/safetensors/index#format.
83
83
 
84
84
  Attributes:
85
- metadata (`Dict`, *optional*):
85
+ metadata (`dict`, *optional*):
86
86
  The metadata contained in the 'model.safetensors.index.json' file, if it exists. Only populated for sharded
87
87
  models.
88
88
  sharded (`bool`):
89
89
  Whether the repo contains a sharded model or not.
90
- weight_map (`Dict[str, str]`):
90
+ weight_map (`dict[str, str]`):
91
91
  A map of all weights. Keys are tensor names and values are filenames of the files containing the tensors.
92
- files_metadata (`Dict[str, SafetensorsFileMetadata]`):
92
+ files_metadata (`dict[str, SafetensorsFileMetadata]`):
93
93
  A map of all files metadata. Keys are filenames and values are the metadata of the corresponding file, as
94
94
  a [`SafetensorsFileMetadata`] object.
95
- parameter_count (`Dict[str, int]`):
95
+ parameter_count (`dict[str, int]`):
96
96
  A map of the number of parameters per data type. Keys are data types and values are the number of parameters
97
97
  of that data type.
98
98
  """
99
99
 
100
- metadata: Optional[Dict]
100
+ metadata: Optional[dict]
101
101
  sharded: bool
102
- weight_map: Dict[TENSOR_NAME_T, FILENAME_T] # tensor name -> filename
103
- files_metadata: Dict[FILENAME_T, SafetensorsFileMetadata] # filename -> metadata
104
- parameter_count: Dict[DTYPE_T, int] = field(init=False)
102
+ weight_map: dict[TENSOR_NAME_T, FILENAME_T] # tensor name -> filename
103
+ files_metadata: dict[FILENAME_T, SafetensorsFileMetadata] # filename -> metadata
104
+ parameter_count: dict[DTYPE_T, int] = field(init=False)
105
105
 
106
106
  def __post_init__(self) -> None:
107
- parameter_count: Dict[DTYPE_T, int] = defaultdict(int)
107
+ parameter_count: dict[DTYPE_T, int] = defaultdict(int)
108
108
  for file_metadata in self.files_metadata.values():
109
109
  for dtype, nb_parameters_ in file_metadata.parameter_count.items():
110
110
  parameter_count[dtype] += nb_parameters_
@@ -20,7 +20,7 @@ import sys
20
20
  from contextlib import contextmanager
21
21
  from io import StringIO
22
22
  from pathlib import Path
23
- from typing import IO, Generator, List, Optional, Tuple, Union
23
+ from typing import IO, Generator, Optional, Union
24
24
 
25
25
  from .logging import get_logger
26
26
 
@@ -44,12 +44,14 @@ def capture_output() -> Generator[StringIO, None, None]:
44
44
  output = StringIO()
45
45
  previous_output = sys.stdout
46
46
  sys.stdout = output
47
- yield output
48
- sys.stdout = previous_output
47
+ try:
48
+ yield output
49
+ finally:
50
+ sys.stdout = previous_output
49
51
 
50
52
 
51
53
  def run_subprocess(
52
- command: Union[str, List[str]],
54
+ command: Union[str, list[str]],
53
55
  folder: Optional[Union[str, Path]] = None,
54
56
  check=True,
55
57
  **kwargs,
@@ -60,7 +62,7 @@ def run_subprocess(
60
62
  be captured.
61
63
 
62
64
  Args:
63
- command (`str` or `List[str]`):
65
+ command (`str` or `list[str]`):
64
66
  The command to execute as a string or list of strings.
65
67
  folder (`str`, *optional*):
66
68
  The folder in which to run the command. Defaults to current working
@@ -68,7 +70,7 @@ def run_subprocess(
68
70
  check (`bool`, *optional*, defaults to `True`):
69
71
  Setting `check` to `True` will raise a `subprocess.CalledProcessError`
70
72
  when the subprocess has a non-zero exit code.
71
- kwargs (`Dict[str]`):
73
+ kwargs (`dict[str]`):
72
74
  Keyword arguments to be passed to the `subprocess.run` underlying command.
73
75
 
74
76
  Returns:
@@ -94,23 +96,23 @@ def run_subprocess(
94
96
 
95
97
  @contextmanager
96
98
  def run_interactive_subprocess(
97
- command: Union[str, List[str]],
99
+ command: Union[str, list[str]],
98
100
  folder: Optional[Union[str, Path]] = None,
99
101
  **kwargs,
100
- ) -> Generator[Tuple[IO[str], IO[str]], None, None]:
102
+ ) -> Generator[tuple[IO[str], IO[str]], None, None]:
101
103
  """Run a subprocess in an interactive mode in a context manager.
102
104
 
103
105
  Args:
104
- command (`str` or `List[str]`):
106
+ command (`str` or `list[str]`):
105
107
  The command to execute as a string or list of strings.
106
108
  folder (`str`, *optional*):
107
109
  The folder in which to run the command. Defaults to current working
108
110
  directory (from `os.getcwd()`).
109
- kwargs (`Dict[str]`):
111
+ kwargs (`dict[str]`):
110
112
  Keyword arguments to be passed to the `subprocess.run` underlying command.
111
113
 
112
114
  Returns:
113
- `Tuple[IO[str], IO[str]]`: A tuple with `stdin` and `stdout` to interact
115
+ `tuple[IO[str], IO[str]]`: A tuple with `stdin` and `stdout` to interact
114
116
  with the process (input and output are utf-8 encoded).
115
117
 
116
118
  Example:
@@ -1,6 +1,6 @@
1
1
  from queue import Queue
2
2
  from threading import Lock, Thread
3
- from typing import Dict, Optional, Union
3
+ from typing import Optional, Union
4
4
  from urllib.parse import quote
5
5
 
6
6
  from .. import constants, logging
@@ -22,10 +22,10 @@ def send_telemetry(
22
22
  *,
23
23
  library_name: Optional[str] = None,
24
24
  library_version: Optional[str] = None,
25
- user_agent: Union[Dict, str, None] = None,
25
+ user_agent: Union[dict, str, None] = None,
26
26
  ) -> None:
27
27
  """
28
- Sends telemetry that helps tracking usage of different HF libraries.
28
+ Sends telemetry that helps track usage of different HF libraries.
29
29
 
30
30
  This usage data helps us debug issues and prioritize new features. However, we understand that not everyone wants
31
31
  to share additional information, and we respect your privacy. You can disable telemetry collection by setting the
@@ -98,7 +98,7 @@ def _send_telemetry_in_thread(
98
98
  *,
99
99
  library_name: Optional[str] = None,
100
100
  library_version: Optional[str] = None,
101
- user_agent: Union[Dict, str, None] = None,
101
+ user_agent: Union[dict, str, None] = None,
102
102
  ) -> None:
103
103
  """Contains the actual data sending data to the Hub.
104
104
 
@@ -1,4 +1,4 @@
1
- # Copyright 2022 The HuggingFace Team. All rights reserved.
1
+ # Copyright 2025 The HuggingFace Team. All rights reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -11,10 +11,10 @@
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
- """Contains a utility for good-looking prints."""
14
+ """Contains utilities to print stuff to the terminal (styling, helpers)."""
15
15
 
16
16
  import os
17
- from typing import List, Union
17
+ from typing import Union
18
18
 
19
19
 
20
20
  class ANSI:
@@ -52,7 +52,7 @@ class ANSI:
52
52
  return f"{code}{s}{cls._reset}"
53
53
 
54
54
 
55
- def tabulate(rows: List[List[Union[str, int]]], headers: List[str]) -> str:
55
+ def tabulate(rows: list[list[Union[str, int]]], headers: list[str]) -> str:
56
56
  """
57
57
  Inspired by:
58
58
 
@@ -15,10 +15,10 @@
15
15
  """Handle typing imports based on system compatibility."""
16
16
 
17
17
  import sys
18
- from typing import Any, Callable, List, Literal, Type, TypeVar, Union, get_args, get_origin
18
+ from typing import Any, Callable, Literal, Optional, Type, TypeVar, Union, get_args, get_origin
19
19
 
20
20
 
21
- UNION_TYPES: List[Any] = [Union]
21
+ UNION_TYPES: list[Any] = [Union]
22
22
  if sys.version_info >= (3, 10):
23
23
  from types import UnionType
24
24
 
@@ -33,7 +33,7 @@ CallableT = TypeVar("CallableT", bound=Callable)
33
33
  _JSON_SERIALIZABLE_TYPES = (int, float, str, bool, type(None))
34
34
 
35
35
 
36
- def is_jsonable(obj: Any) -> bool:
36
+ def is_jsonable(obj: Any, _visited: Optional[set[int]] = None) -> bool:
37
37
  """Check if an object is JSON serializable.
38
38
 
39
39
  This is a weak check, as it does not check for the actual JSON serialization, but only for the types of the object.
@@ -43,19 +43,39 @@ def is_jsonable(obj: Any) -> bool:
43
43
  - it is an instance of int, float, str, bool, or NoneType
44
44
  - it is a list or tuple and all its items are json serializable
45
45
  - it is a dict and all its keys are strings and all its values are json serializable
46
+
47
+ Uses a visited set to avoid infinite recursion on circular references. If object has already been visited, it is
48
+ considered not json serializable.
46
49
  """
50
+ # Initialize visited set to track object ids and detect circular references
51
+ if _visited is None:
52
+ _visited = set()
53
+
54
+ # Detect circular reference
55
+ obj_id = id(obj)
56
+ if obj_id in _visited:
57
+ return False
58
+
59
+ # Add current object to visited before recursive checks
60
+ _visited.add(obj_id)
47
61
  try:
48
62
  if isinstance(obj, _JSON_SERIALIZABLE_TYPES):
49
63
  return True
50
64
  if isinstance(obj, (list, tuple)):
51
- return all(is_jsonable(item) for item in obj)
65
+ return all(is_jsonable(item, _visited) for item in obj)
52
66
  if isinstance(obj, dict):
53
- return all(isinstance(key, _JSON_SERIALIZABLE_TYPES) and is_jsonable(value) for key, value in obj.items())
67
+ return all(
68
+ isinstance(key, _JSON_SERIALIZABLE_TYPES) and is_jsonable(value, _visited)
69
+ for key, value in obj.items()
70
+ )
54
71
  if hasattr(obj, "__json__"):
55
72
  return True
56
73
  return False
57
74
  except RecursionError:
58
75
  return False
76
+ finally:
77
+ # Remove the object id from visited to avoid side‑effects for other branches
78
+ _visited.discard(obj_id)
59
79
 
60
80
 
61
81
  def is_simple_optional_type(type_: Type) -> bool: