hfutils 0.3.8__tar.gz → 0.4.0__tar.gz

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 (74) hide show
  1. {hfutils-0.3.8/hfutils.egg-info → hfutils-0.4.0}/PKG-INFO +1 -1
  2. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/config/meta.py +1 -1
  3. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/entry/cli.py +2 -0
  4. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/entry/download.py +6 -2
  5. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/entry/ls.py +8 -61
  6. hfutils-0.4.0/hfutils/entry/tree.py +239 -0
  7. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/operate/download.py +114 -61
  8. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/utils/__init__.py +3 -0
  9. hfutils-0.4.0/hfutils/utils/archive.py +157 -0
  10. hfutils-0.4.0/hfutils/utils/data.py +87 -0
  11. hfutils-0.4.0/hfutils/utils/model.py +140 -0
  12. hfutils-0.4.0/hfutils/utils/type_.py +121 -0
  13. {hfutils-0.3.8 → hfutils-0.4.0/hfutils.egg-info}/PKG-INFO +1 -1
  14. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils.egg-info/SOURCES.txt +5 -0
  15. {hfutils-0.3.8 → hfutils-0.4.0}/LICENSE +0 -0
  16. {hfutils-0.3.8 → hfutils-0.4.0}/MANIFEST.in +0 -0
  17. {hfutils-0.3.8 → hfutils-0.4.0}/README.md +0 -0
  18. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/__init__.py +0 -0
  19. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/__main__.py +0 -0
  20. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/archive/__init__.py +0 -0
  21. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/archive/base.py +0 -0
  22. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/archive/rar.py +0 -0
  23. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/archive/sevenz.py +0 -0
  24. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/archive/tar.py +0 -0
  25. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/archive/zip.py +0 -0
  26. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/cache/__init__.py +0 -0
  27. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/cache/delete.py +0 -0
  28. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/config/__init__.py +0 -0
  29. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/entry/__init__.py +0 -0
  30. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/entry/base.py +0 -0
  31. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/entry/clone.py +0 -0
  32. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/entry/dispatch.py +0 -0
  33. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/entry/index.py +0 -0
  34. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/entry/ls_repo.py +0 -0
  35. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/entry/rollback.py +0 -0
  36. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/entry/upload.py +0 -0
  37. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/entry/whoami.py +0 -0
  38. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/index/__init__.py +0 -0
  39. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/index/fetch.py +0 -0
  40. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/index/hash.py +0 -0
  41. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/index/local_fetch.py +0 -0
  42. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/index/make.py +0 -0
  43. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/index/validate.py +0 -0
  44. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/operate/__init__.py +0 -0
  45. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/operate/base.py +0 -0
  46. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/operate/upload.py +0 -0
  47. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/operate/validate.py +0 -0
  48. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/repository/__init__.py +0 -0
  49. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/repository/base.py +0 -0
  50. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/repository/clone.py +0 -0
  51. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/repository/rollback.py +0 -0
  52. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/utils/binary.py +0 -0
  53. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/utils/download.py +0 -0
  54. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/utils/irregular_repo.json +0 -0
  55. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/utils/logging.py +0 -0
  56. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/utils/number.py +0 -0
  57. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/utils/path.py +0 -0
  58. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/utils/session.py +0 -0
  59. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/utils/temp.py +0 -0
  60. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/utils/tqdm_.py +0 -0
  61. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/utils/walk.py +0 -0
  62. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils.egg-info/dependency_links.txt +0 -0
  63. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils.egg-info/entry_points.txt +0 -0
  64. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils.egg-info/requires.txt +0 -0
  65. {hfutils-0.3.8 → hfutils-0.4.0}/hfutils.egg-info/top_level.txt +0 -0
  66. {hfutils-0.3.8 → hfutils-0.4.0}/requirements-7z.txt +0 -0
  67. {hfutils-0.3.8 → hfutils-0.4.0}/requirements-build.txt +0 -0
  68. {hfutils-0.3.8 → hfutils-0.4.0}/requirements-doc.txt +0 -0
  69. {hfutils-0.3.8 → hfutils-0.4.0}/requirements-rar.txt +0 -0
  70. {hfutils-0.3.8 → hfutils-0.4.0}/requirements-test.txt +0 -0
  71. {hfutils-0.3.8 → hfutils-0.4.0}/requirements-transfer.txt +0 -0
  72. {hfutils-0.3.8 → hfutils-0.4.0}/requirements.txt +0 -0
  73. {hfutils-0.3.8 → hfutils-0.4.0}/setup.cfg +0 -0
  74. {hfutils-0.3.8 → hfutils-0.4.0}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: hfutils
3
- Version: 0.3.8
3
+ Version: 0.4.0
4
4
  Summary: Useful utilities for huggingface
5
5
  Home-page: https://github.com/deepghs/hfutils
6
6
  Author: narugo1992
@@ -7,7 +7,7 @@ Overview:
7
7
  __TITLE__ = 'hfutils'
8
8
 
9
9
  #: Version of this project.
10
- __VERSION__ = '0.3.8'
10
+ __VERSION__ = '0.4.0'
11
11
 
12
12
  #: Short description of the project, will be included in ``setup.py``.
13
13
  __DESCRIPTION__ = 'Useful utilities for huggingface'
@@ -5,6 +5,7 @@ from .index import _add_index_subcommand
5
5
  from .ls import _add_ls_subcommand
6
6
  from .ls_repo import _add_ls_repo_subcommand
7
7
  from .rollback import _add_rollback_subcommand
8
+ from .tree import _add_tree_subcommand
8
9
  from .upload import _add_upload_subcommand
9
10
  from .whoami import _add_whoami_subcommand
10
11
 
@@ -17,6 +18,7 @@ _DECORATORS = [
17
18
  _add_index_subcommand,
18
19
  _add_rollback_subcommand,
19
20
  _add_clone_subcommand,
21
+ _add_tree_subcommand,
20
22
  ]
21
23
 
22
24
  cli = hfutilcli
@@ -7,7 +7,7 @@ from huggingface_hub import configure_http_backend
7
7
 
8
8
  from .base import CONTEXT_SETTINGS, command_wrap, ClickErrorException
9
9
  from ..operate import download_file_to_file, download_archive_as_directory, download_directory_as_directory
10
- from ..operate.base import REPO_TYPES, RepoTypeTyping
10
+ from ..operate.base import REPO_TYPES, RepoTypeTyping, _IGNORE_PATTERN_UNSET
11
11
  from ..utils import get_requests_session
12
12
 
13
13
 
@@ -55,12 +55,15 @@ def _add_download_subcommand(cli: click.Group) -> click.Group:
55
55
  help='Just check the file size when validating the downloaded files.', show_default=True)
56
56
  @click.option('--tmpdir', 'tmpdir', type=str, default=None,
57
57
  help='Use custom temporary Directory.', show_default=True)
58
+ @click.option('--all', 'show_all', is_flag=True, type=bool, default=False,
59
+ help='Show all files, including hidden files.', show_default=True)
58
60
  @command_wrap()
59
61
  def download(
60
62
  repo_id: str, repo_type: RepoTypeTyping,
61
63
  file_in_repo: Optional[str], archive_in_repo: Optional[str], dir_in_repo: Optional[str],
62
64
  output_path: str, revision: str, max_workers: int,
63
- password: Optional[str], wildcard: Optional[str], soft_mode_when_check: bool, tmpdir: Optional[str]
65
+ password: Optional[str], wildcard: Optional[str], soft_mode_when_check: bool, tmpdir: Optional[str],
66
+ show_all: bool = False,
64
67
  ):
65
68
  """
66
69
  Download data from HuggingFace repositories.
@@ -135,6 +138,7 @@ def _add_download_subcommand(cli: click.Group) -> click.Group:
135
138
  silent=False,
136
139
  max_workers=max_workers,
137
140
  soft_mode_when_check=soft_mode_when_check,
141
+ ignore_patterns=_IGNORE_PATTERN_UNSET if not show_all else [],
138
142
  )
139
143
 
140
144
  else:
@@ -1,6 +1,5 @@
1
- import mimetypes
2
1
  import os.path
3
- from enum import Enum, unique
2
+ import os.path
4
3
  from typing import Union, List
5
4
 
6
5
  import click
@@ -10,46 +9,7 @@ from huggingface_hub.hf_api import RepoFolder, RepoFile
10
9
 
11
10
  from .base import CONTEXT_SETTINGS
12
11
  from ..operate.base import REPO_TYPES, get_hf_client
13
- from ..utils import get_requests_session
14
-
15
- mimetypes.add_type('image/webp', '.webp')
16
-
17
-
18
- @unique
19
- class ListItemType(Enum):
20
- """
21
- Enum class representing different types of list items.
22
- """
23
-
24
- FILE = 0x1
25
- FOLDER = 0x2
26
- IMAGE = 0x3
27
- ARCHIVE = 0x4
28
- MODEL = 0x5
29
- DATA = 0x6
30
-
31
- @property
32
- def render_color(self):
33
- """
34
- Get the render color based on the item type.
35
-
36
- :return: The render color for the item type.
37
- :rtype: str
38
- """
39
- if self == self.FILE:
40
- return None
41
- elif self == self.FOLDER:
42
- return 'blue'
43
- elif self == self.IMAGE:
44
- return 'magenta'
45
- elif self == self.ARCHIVE:
46
- return 'red'
47
- elif self == self.MODEL:
48
- return 'green'
49
- elif self == self.DATA:
50
- return 'yellow'
51
- else:
52
- raise ValueError(f'Unknown type - {self!r}') # pragma: no cover
12
+ from ..utils import get_requests_session, FileItemType, get_file_type
53
13
 
54
14
 
55
15
  class ListItem:
@@ -66,22 +26,9 @@ class ListItem:
66
26
  self.item = item
67
27
  self.base_dir = base_dir
68
28
  if isinstance(item, RepoFolder):
69
- self.type = ListItemType.FOLDER
29
+ self.type = FileItemType.FOLDER
70
30
  else:
71
- mimetype, _ = mimetypes.guess_type(item.path)
72
- _, ext = os.path.splitext(item.path)
73
- self.type = ListItemType.FILE
74
- if ext in {'.ckpt', '.pt', '.safetensors', '.onnx', '.model', '.h5', '.mlmodel',
75
- '.ftz', '.pb', '.pth', '.tflite'}:
76
- self.type = ListItemType.MODEL
77
- elif ext in {'.json', '.csv', '.tsv', '.arrow', '.bin', '.msgpack', '.npy', '.npz',
78
- '.parquet', '.pickle', '.pkl', '.wasm'}:
79
- self.type = ListItemType.DATA
80
- elif mimetype:
81
- if 'image' in mimetype:
82
- self.type = ListItemType.IMAGE
83
- elif mimetype.split('/', maxsplit=1)[-1] in {'zip', 'rar', 'x-tar', 'x-7z-compressed'}:
84
- self.type = ListItemType.ARCHIVE
31
+ self.type = get_file_type(item.path)
85
32
 
86
33
 
87
34
  def _add_ls_subcommand(cli: click.Group) -> click.Group:
@@ -154,7 +101,7 @@ def _add_ls_subcommand(cli: click.Group) -> click.Group:
154
101
  max_size_length = 0
155
102
  max_commit_info_length = 0
156
103
  for item in items:
157
- if item.type == ListItemType.FOLDER:
104
+ if item.type == FileItemType.FOLDER:
158
105
  size_text = '-'
159
106
  else:
160
107
  size_text = str(item.item.size)
@@ -164,8 +111,8 @@ def _add_ls_subcommand(cli: click.Group) -> click.Group:
164
111
  max_commit_info_length = max(max_commit_info_length, len(commit_text))
165
112
 
166
113
  for item in items:
167
- print('d' if item.type == ListItemType.FOLDER else '-', end='')
168
- print('L' if item.type != ListItemType.FOLDER and item.item.lfs else '-', end='')
114
+ print('d' if item.type == FileItemType.FOLDER else '-', end='')
115
+ print('L' if item.type != FileItemType.FOLDER and item.item.lfs else '-', end='')
169
116
 
170
117
  commit_text = (item.item.last_commit.title or '').splitlines(keepends=False)[0]
171
118
  print(
@@ -174,7 +121,7 @@ def _add_ls_subcommand(cli: click.Group) -> click.Group:
174
121
  end=''
175
122
  )
176
123
 
177
- if item.type == ListItemType.FOLDER:
124
+ if item.type == FileItemType.FOLDER:
178
125
  size_text = '-'
179
126
  else:
180
127
  size_text = str(item.item.size)
@@ -0,0 +1,239 @@
1
+ """
2
+ This module provides functionality for listing and displaying files from a HuggingFace repository in a tree-like structure.
3
+
4
+ It includes functions for parsing repository paths, retrieving file information, and formatting the output as a tree.
5
+ The module also defines a CLI command for easy interaction with the tree functionality.
6
+
7
+ Key components:
8
+
9
+ - TreeItem: A dataclass representing an item (file or folder) in the tree structure.
10
+ - _get_tree: Function to retrieve the tree structure of files in a HuggingFace repository.
11
+ - _add_tree_subcommand: Function to add the 'tree' subcommand to a Click CLI group.
12
+
13
+ Usage:
14
+ This module is typically used as part of a larger CLI application for interacting with HuggingFace repositories.
15
+ The 'tree' command can be used to visualize the structure of files in a repository.
16
+ """
17
+
18
+ import dataclasses
19
+ import os
20
+ import re
21
+ from typing import Optional, List, Union
22
+
23
+ import click
24
+ from hbutils.string import format_tree
25
+ from huggingface_hub import configure_http_backend
26
+ from huggingface_hub.hf_api import RepoFile
27
+ from natsort import natsorted
28
+
29
+ from .base import CONTEXT_SETTINGS
30
+ from ..operate.base import REPO_TYPES, list_files_in_repository, RepoTypeTyping, get_hf_client
31
+ from ..utils import get_requests_session, hf_normpath, get_file_type, hf_fs_path, FileItemType
32
+
33
+
34
+ @dataclasses.dataclass
35
+ class TreeItem:
36
+ """
37
+ Represents an item (file or folder) in the tree structure.
38
+
39
+ :param name: The name of the item.
40
+ :type name: str
41
+ :param type_: The type of the item (file or folder).
42
+ :type type_: FileItemType
43
+ :param children: List of child items if this is a folder.
44
+ :type children: Optional[List[TreeItem]]
45
+ :param exist: Whether the item exists in the repository.
46
+ :type exist: bool
47
+
48
+ :ivar name: The name of the item.
49
+ :ivar type_: The type of the item.
50
+ :ivar children: List of child items.
51
+ :ivar exist: Existence status of the item.
52
+ """
53
+
54
+ name: str
55
+ type_: FileItemType
56
+ children: Optional[List['TreeItem']]
57
+ exist: bool = True
58
+
59
+ def get_name(self):
60
+ """
61
+ Get the formatted name of the item for display.
62
+
63
+ :return: Formatted name string with color and strike-through if applicable.
64
+ :rtype: str
65
+ """
66
+ return click.style(
67
+ self.name,
68
+ fg=self.type_.render_color if self.exist else None,
69
+ strikethrough=not self.exist,
70
+ ) + ('' if self.exist else ' <NOT EXIST>')
71
+
72
+ def get_children(self):
73
+ """
74
+ Get the children of this item if it's a folder.
75
+
76
+ :return: List of child items if folder, empty list otherwise.
77
+ :rtype: List[TreeItem]
78
+ """
79
+ return self.children if self.type_ == FileItemType.FOLDER else []
80
+
81
+
82
+ def _get_tree(repo_id: str, repo_type: RepoTypeTyping, dir_in_repo: str,
83
+ revision: Optional[str] = None, show_all: bool = False) -> TreeItem:
84
+ """
85
+ Retrieve the tree structure of files in a HuggingFace repository.
86
+
87
+ :param repo_id: The ID of the repository.
88
+ :type repo_id: str
89
+ :param repo_type: The type of the repository.
90
+ :type repo_type: RepoTypeTyping
91
+ :param dir_in_repo: The directory in the repository to start from.
92
+ :type dir_in_repo: str
93
+ :param revision: The revision of the repository to use.
94
+ :type revision: Optional[str]
95
+ :param show_all: Whether to show hidden files.
96
+ :type show_all: bool
97
+
98
+ :return: The root TreeItem representing the directory structure.
99
+ :rtype: TreeItem
100
+ """
101
+ root = {}
102
+ for filepath in list_files_in_repository(
103
+ repo_id=repo_id,
104
+ repo_type=repo_type,
105
+ subdir=dir_in_repo,
106
+ revision=revision,
107
+ ignore_patterns=[],
108
+ ):
109
+ filename = hf_normpath(os.path.relpath(filepath, dir_in_repo))
110
+ segments = re.split(r'[\\/]+', filename)
111
+ if any(segment.startswith('.') and segment != '.' for segment in segments) and not show_all:
112
+ continue
113
+
114
+ current_node = root
115
+ for i, segment in enumerate(segments):
116
+ if segment not in current_node:
117
+ if i == (len(segments) - 1):
118
+ current_node[segment] = get_file_type(segment)
119
+ else:
120
+ current_node[segment] = {}
121
+ current_node = current_node[segment]
122
+
123
+ root_name = hf_fs_path(
124
+ repo_id=repo_id,
125
+ repo_type=repo_type,
126
+ filename=dir_in_repo,
127
+ revision=revision,
128
+ )
129
+
130
+ def _recursion(cur_node: Union[dict, FileItemType], parent_name: str, is_exist: bool = False):
131
+ if isinstance(cur_node, dict):
132
+ return TreeItem(
133
+ name=parent_name,
134
+ type_=FileItemType.FOLDER,
135
+ children=[
136
+ _recursion(cur_node=value, parent_name=name, is_exist=is_exist)
137
+ for name, value in natsorted(cur_node.items())
138
+ ],
139
+ exist=is_exist,
140
+ )
141
+ else:
142
+ return TreeItem(
143
+ name=parent_name,
144
+ type_=cur_node,
145
+ children=[],
146
+ exist=is_exist,
147
+ )
148
+
149
+ exist = True
150
+ if not root:
151
+ hf_client = get_hf_client()
152
+ paths = hf_client.get_paths_info(
153
+ repo_id=repo_id,
154
+ repo_type=repo_type,
155
+ revision=revision,
156
+ paths=[dir_in_repo],
157
+ )
158
+ if len(paths) == 0:
159
+ exist = False
160
+ elif len(paths) == 1:
161
+ pathobj = paths[0]
162
+ if isinstance(pathobj, RepoFile): # the subdir is a file
163
+ root = get_file_type(dir_in_repo)
164
+ else:
165
+ assert len(paths) == 1, \
166
+ f'Multiple path {dir_in_repo!r} found in repo {root_name!r}, ' \
167
+ f'this must be caused by HuggingFace API.' # pragma: no cover
168
+
169
+ return _recursion(
170
+ cur_node=root,
171
+ parent_name=root_name,
172
+ is_exist=exist,
173
+ )
174
+
175
+
176
+ def _add_tree_subcommand(cli: click.Group) -> click.Group:
177
+ """
178
+ Add the 'tree' subcommand to a Click CLI group.
179
+
180
+ This function defines a new 'tree' command that lists files from a HuggingFace repository
181
+ in a tree-like structure.
182
+
183
+ :param cli: The Click CLI group to add the command to.
184
+ :type cli: click.Group
185
+
186
+ :return: The modified CLI group with the 'tree' command added.
187
+ :rtype: click.Group
188
+
189
+ Usage:
190
+ This function is typically called when setting up a CLI application:
191
+
192
+ cli = click.Group()
193
+ cli = _add_tree_subcommand(cli)
194
+ """
195
+
196
+ @cli.command('tree', help='List files as a tree from HuggingFace repository.\n\n'
197
+ 'Set environment $HF_TOKEN to use your own access token.',
198
+ context_settings=CONTEXT_SETTINGS)
199
+ @click.option('-r', '--repository', 'repo_id', type=str, required=True,
200
+ help='Repository to download from.')
201
+ @click.option('-t', '--type', 'repo_type', type=click.Choice(REPO_TYPES), default='dataset',
202
+ help='Type of the HuggingFace repository.', show_default=True)
203
+ @click.option('-d', '--directory', 'dir_in_repo', type=str, default=None,
204
+ help='Directory in repository to download the full directory tree.')
205
+ @click.option('-R', '--revision', 'revision', type=str, default='main',
206
+ help='Revision of repository.', show_default=True)
207
+ @click.option('-a', '--all', 'show_all', is_flag=True, type=bool, default=False,
208
+ help='Show all files, including hidden files.', show_default=True)
209
+ def tree(repo_id: str, repo_type: RepoTypeTyping, dir_in_repo, revision: str, show_all: bool):
210
+ """
211
+ List files as a tree from a HuggingFace repository in a tree-like structure.
212
+
213
+ :param repo_id: The ID of the repository.
214
+ :type repo_id: str
215
+ :param repo_type: The type of the repository.
216
+ :type repo_type: RepoTypeTyping
217
+ :param dir_in_repo: The directory in the repository to start from.
218
+ :type dir_in_repo: str
219
+ :param revision: The revision of the repository to use.
220
+ :type revision: str
221
+ :param show_all: Whether to show hidden files.
222
+ :type show_all: bool
223
+ """
224
+ configure_http_backend(get_requests_session)
225
+
226
+ _tree = _get_tree(
227
+ repo_id=repo_id,
228
+ repo_type=repo_type,
229
+ dir_in_repo=dir_in_repo or '.',
230
+ revision=revision,
231
+ show_all=show_all,
232
+ )
233
+ print(format_tree(
234
+ _tree,
235
+ format_node=TreeItem.get_name,
236
+ get_children=TreeItem.get_children,
237
+ ))
238
+
239
+ return cli
@@ -1,3 +1,27 @@
1
+ """
2
+ This module provides functions for downloading files and directories from Hugging Face repositories.
3
+
4
+ It includes utilities for downloading individual files, archives, and entire directories,
5
+ with support for concurrent downloads, retries, and progress tracking.
6
+
7
+ The module interacts with the Hugging Face Hub API to fetch repository contents and
8
+ download files, handling various repository types and revisions.
9
+
10
+ Key features:
11
+
12
+ - Download individual files from Hugging Face repositories
13
+ - Download and extract archive files
14
+ - Download entire directories with pattern matching and ignore rules
15
+ - Concurrent downloads with configurable worker count
16
+ - Retry mechanism for failed downloads
17
+ - Progress tracking with tqdm
18
+ - Support for different repository types (dataset, model, space)
19
+ - Token-based authentication for accessing private repositories
20
+
21
+ This module is particularly useful for managing and synchronizing local copies of
22
+ Hugging Face repository contents, especially when dealing with large datasets or models.
23
+ """
24
+
1
25
  import logging
2
26
  import os.path
3
27
  import shutil
@@ -12,9 +36,50 @@ from ..archive import archive_unpack
12
36
  from ..utils import tqdm, TemporaryDirectory, hf_normpath
13
37
 
14
38
 
39
+ def _raw_download_file(td: str, local_file: str, repo_id: str, file_in_repo: str,
40
+ repo_type: RepoTypeTyping = 'dataset', revision: str = 'main',
41
+ hf_token: Optional[str] = None):
42
+ """
43
+ Download a file from a Hugging Face repository to a temporary directory and then move it to the final location.
44
+
45
+ This internal function handles the actual download process using the Hugging Face Hub client.
46
+
47
+ :param td: Temporary directory path.
48
+ :type td: str
49
+ :param local_file: The final local file path where the downloaded file will be moved.
50
+ :type local_file: str
51
+ :param repo_id: The identifier of the repository.
52
+ :type repo_id: str
53
+ :param file_in_repo: The file path within the repository.
54
+ :type file_in_repo: str
55
+ :param repo_type: The type of the repository ('dataset', 'model', 'space').
56
+ :type repo_type: RepoTypeTyping
57
+ :param revision: The revision of the repository (e.g., branch, tag, commit hash).
58
+ :type revision: str
59
+ :param hf_token: Hugging Face token for API client.
60
+ :type hf_token: str, optional
61
+ """
62
+ hf_client = get_hf_client(hf_token=hf_token)
63
+ relative_filename = os.path.join(*file_in_repo.split("/"))
64
+ temp_path = os.path.join(td, relative_filename)
65
+ try:
66
+ hf_client.hf_hub_download(
67
+ repo_id=repo_id,
68
+ repo_type=repo_type,
69
+ filename=hf_normpath(file_in_repo),
70
+ revision=revision,
71
+ local_dir=td,
72
+ )
73
+ finally:
74
+ if os.path.exists(temp_path):
75
+ if os.path.dirname(local_file):
76
+ os.makedirs(os.path.dirname(local_file), exist_ok=True)
77
+ shutil.move(temp_path, local_file)
78
+
79
+
15
80
  def download_file_to_file(local_file: str, repo_id: str, file_in_repo: str,
16
81
  repo_type: RepoTypeTyping = 'dataset', revision: str = 'main',
17
- resume_download: bool = True, hf_token: Optional[str] = None):
82
+ hf_token: Optional[str] = None):
18
83
  """
19
84
  Download a file from a Hugging Face repository and save it to a local file.
20
85
 
@@ -28,29 +93,19 @@ def download_file_to_file(local_file: str, repo_id: str, file_in_repo: str,
28
93
  :type repo_type: RepoTypeTyping
29
94
  :param revision: The revision of the repository (e.g., branch, tag, commit hash).
30
95
  :type revision: str
31
- :param resume_download: Resume the existing download.
32
- :type resume_download: bool
33
96
  :param hf_token: Huggingface token for API client, use ``HF_TOKEN`` variable if not assigned.
34
97
  :type hf_token: str, optional
35
98
  """
36
- hf_client = get_hf_client(hf_token)
37
- relative_filename = os.path.join(*file_in_repo.split("/"))
38
99
  with TemporaryDirectory() as td:
39
- temp_path = os.path.join(td, relative_filename)
40
- try:
41
- hf_client.hf_hub_download(
42
- repo_id=repo_id,
43
- repo_type=repo_type,
44
- filename=hf_normpath(file_in_repo),
45
- revision=revision,
46
- local_dir=td,
47
- resume_download=resume_download,
48
- )
49
- finally:
50
- if os.path.exists(temp_path):
51
- if os.path.dirname(local_file):
52
- os.makedirs(os.path.dirname(local_file), exist_ok=True)
53
- shutil.move(temp_path, local_file)
100
+ _raw_download_file(
101
+ td=td,
102
+ local_file=local_file,
103
+ repo_id=repo_id,
104
+ file_in_repo=file_in_repo,
105
+ repo_type=repo_type,
106
+ revision=revision,
107
+ hf_token=hf_token,
108
+ )
54
109
 
55
110
 
56
111
  def download_archive_as_directory(local_directory: str, repo_id: str, file_in_repo: str,
@@ -84,7 +139,7 @@ def download_directory_as_directory(
84
139
  local_directory: str, repo_id: str, dir_in_repo: str = '.', pattern: str = '**/*',
85
140
  repo_type: RepoTypeTyping = 'dataset', revision: str = 'main',
86
141
  silent: bool = False, ignore_patterns: List[str] = _IGNORE_PATTERN_UNSET,
87
- resume_download: bool = True, max_workers: int = 8, max_retries: int = 5,
142
+ max_workers: int = 8, max_retries: int = 5,
88
143
  soft_mode_when_check: bool = False, hf_token: Optional[str] = None
89
144
  ):
90
145
  """
@@ -110,8 +165,6 @@ def download_directory_as_directory(
110
165
  :type max_workers: int
111
166
  :param max_retries: Max retry times when downloading. Default is ``5``.
112
167
  :type max_retries: int
113
- :param resume_download: Resume the existing download.
114
- :type resume_download: bool
115
168
  :param soft_mode_when_check: Just check the size of the expected file when enabled. Default is False.
116
169
  :type soft_mode_when_check: bool
117
170
  :param hf_token: Huggingface token for API client, use ``HF_TOKEN`` variable if not assigned.
@@ -129,46 +182,46 @@ def download_directory_as_directory(
129
182
  progress = tqdm(files, silent=silent, desc=f'Downloading {dir_in_repo!r} ...')
130
183
 
131
184
  def _download_one_file(rel_file):
132
- current_resume_download = resume_download
133
- try:
134
- dst_file = os.path.join(local_directory, rel_file)
135
- file_in_repo = hf_normpath(f'{dir_in_repo}/{rel_file}')
136
- if os.path.exists(dst_file) and is_local_file_ready(
137
- repo_id=repo_id,
138
- repo_type=repo_type,
139
- local_file=dst_file,
140
- file_in_repo=file_in_repo,
141
- revision=revision,
142
- hf_token=hf_token,
143
- soft_mode=soft_mode_when_check,
144
- ):
145
- logging.info(f'Local file {rel_file} is ready, download skipped.')
146
- else:
147
- tries = 0
148
- while True:
149
- try:
150
- download_file_to_file(
151
- local_file=dst_file,
152
- repo_id=repo_id,
153
- file_in_repo=file_in_repo,
154
- repo_type=repo_type,
155
- revision=revision,
156
- resume_download=current_resume_download,
157
- hf_token=hf_token,
158
- )
159
- except requests.exceptions.RequestException as err:
160
- if tries < max_retries:
161
- tries += 1
162
- logging.warning(f'Download {rel_file!r} failed, retry ({tries}/{max_retries}) - {err!r}.')
163
- current_resume_download = True
185
+ with TemporaryDirectory() as td:
186
+ try:
187
+ dst_file = os.path.join(local_directory, rel_file)
188
+ file_in_repo = hf_normpath(f'{dir_in_repo}/{rel_file}')
189
+ if os.path.exists(dst_file) and is_local_file_ready(
190
+ repo_id=repo_id,
191
+ repo_type=repo_type,
192
+ local_file=dst_file,
193
+ file_in_repo=file_in_repo,
194
+ revision=revision,
195
+ hf_token=hf_token,
196
+ soft_mode=soft_mode_when_check,
197
+ ):
198
+ logging.info(f'Local file {rel_file} is ready, download skipped.')
199
+ else:
200
+ tries = 0
201
+ while True:
202
+ try:
203
+ _raw_download_file(
204
+ td=td,
205
+ local_file=dst_file,
206
+ repo_id=repo_id,
207
+ file_in_repo=file_in_repo,
208
+ repo_type=repo_type,
209
+ revision=revision,
210
+ hf_token=hf_token,
211
+ )
212
+ except requests.exceptions.RequestException as err:
213
+ if tries < max_retries:
214
+ tries += 1
215
+ logging.warning(
216
+ f'Download {rel_file!r} failed, retry ({tries}/{max_retries}) - {err!r}.')
217
+ else:
218
+ raise
164
219
  else:
165
- raise
166
- else:
167
- break
220
+ break
168
221
 
169
- progress.update()
170
- except Exception as err:
171
- logging.error(f'Unexpected error when downloading {rel_file!r} - {err!r}')
222
+ progress.update()
223
+ except Exception as err:
224
+ logging.exception(f'Unexpected error when downloading {rel_file!r} - {err!r}')
172
225
 
173
226
  tp = ThreadPoolExecutor(max_workers=max_workers)
174
227
  for file in files:
@@ -1,4 +1,6 @@
1
+ from .archive import is_archive_or_compressed
1
2
  from .binary import is_binary_file
3
+ from .data import is_data_file
2
4
  from .download import download_file
3
5
  from .logging import ColoredFormatter
4
6
  from .number import number_to_tag
@@ -6,4 +8,5 @@ from .path import hf_normpath, hf_fs_path, parse_hf_fs_path, HfFileSystemPath
6
8
  from .session import TimeoutHTTPAdapter, get_requests_session, get_random_ua
7
9
  from .temp import TemporaryDirectory
8
10
  from .tqdm_ import tqdm
11
+ from .type_ import FileItemType, get_file_type
9
12
  from .walk import walk_files