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.
- {hfutils-0.3.8/hfutils.egg-info → hfutils-0.4.0}/PKG-INFO +1 -1
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/config/meta.py +1 -1
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/entry/cli.py +2 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/entry/download.py +6 -2
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/entry/ls.py +8 -61
- hfutils-0.4.0/hfutils/entry/tree.py +239 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/operate/download.py +114 -61
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/utils/__init__.py +3 -0
- hfutils-0.4.0/hfutils/utils/archive.py +157 -0
- hfutils-0.4.0/hfutils/utils/data.py +87 -0
- hfutils-0.4.0/hfutils/utils/model.py +140 -0
- hfutils-0.4.0/hfutils/utils/type_.py +121 -0
- {hfutils-0.3.8 → hfutils-0.4.0/hfutils.egg-info}/PKG-INFO +1 -1
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils.egg-info/SOURCES.txt +5 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/LICENSE +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/MANIFEST.in +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/README.md +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/__init__.py +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/__main__.py +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/archive/__init__.py +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/archive/base.py +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/archive/rar.py +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/archive/sevenz.py +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/archive/tar.py +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/archive/zip.py +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/cache/__init__.py +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/cache/delete.py +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/config/__init__.py +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/entry/__init__.py +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/entry/base.py +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/entry/clone.py +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/entry/dispatch.py +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/entry/index.py +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/entry/ls_repo.py +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/entry/rollback.py +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/entry/upload.py +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/entry/whoami.py +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/index/__init__.py +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/index/fetch.py +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/index/hash.py +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/index/local_fetch.py +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/index/make.py +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/index/validate.py +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/operate/__init__.py +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/operate/base.py +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/operate/upload.py +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/operate/validate.py +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/repository/__init__.py +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/repository/base.py +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/repository/clone.py +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/repository/rollback.py +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/utils/binary.py +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/utils/download.py +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/utils/irregular_repo.json +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/utils/logging.py +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/utils/number.py +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/utils/path.py +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/utils/session.py +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/utils/temp.py +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/utils/tqdm_.py +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils/utils/walk.py +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils.egg-info/dependency_links.txt +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils.egg-info/entry_points.txt +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils.egg-info/requires.txt +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/hfutils.egg-info/top_level.txt +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/requirements-7z.txt +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/requirements-build.txt +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/requirements-doc.txt +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/requirements-rar.txt +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/requirements-test.txt +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/requirements-transfer.txt +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/requirements.txt +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/setup.cfg +0 -0
- {hfutils-0.3.8 → hfutils-0.4.0}/setup.py +0 -0
|
@@ -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
|
-
|
|
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 =
|
|
29
|
+
self.type = FileItemType.FOLDER
|
|
70
30
|
else:
|
|
71
|
-
|
|
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 ==
|
|
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 ==
|
|
168
|
-
print('L' if item.type !=
|
|
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 ==
|
|
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
|
-
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
-
|
|
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
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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
|
-
|
|
166
|
-
else:
|
|
167
|
-
break
|
|
220
|
+
break
|
|
168
221
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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
|