hfutils 0.3.2__tar.gz → 0.3.4__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.2/hfutils.egg-info → hfutils-0.3.4}/PKG-INFO +2 -1
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils/config/meta.py +1 -1
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils/entry/download.py +11 -3
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils/operate/__init__.py +1 -1
- hfutils-0.3.4/hfutils/operate/base.py +267 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils/operate/download.py +18 -6
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils/operate/upload.py +8 -1
- {hfutils-0.3.2 → hfutils-0.3.4/hfutils.egg-info}/PKG-INFO +2 -1
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils.egg-info/requires.txt +1 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/requirements.txt +2 -1
- hfutils-0.3.2/hfutils/operate/base.py +0 -135
- {hfutils-0.3.2 → hfutils-0.3.4}/LICENSE +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/MANIFEST.in +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/README.md +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils/__init__.py +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils/__main__.py +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils/archive/__init__.py +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils/archive/base.py +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils/archive/rar.py +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils/archive/sevenz.py +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils/archive/tar.py +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils/archive/zip.py +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils/cache/__init__.py +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils/cache/delete.py +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils/config/__init__.py +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils/entry/__init__.py +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils/entry/base.py +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils/entry/cli.py +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils/entry/clone.py +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils/entry/dispatch.py +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils/entry/index.py +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils/entry/ls.py +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils/entry/ls_repo.py +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils/entry/rollback.py +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils/entry/upload.py +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils/entry/whoami.py +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils/index/__init__.py +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils/index/fetch.py +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils/index/hash.py +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils/index/make.py +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils/index/validate.py +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils/operate/validate.py +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils/repository/__init__.py +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils/repository/base.py +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils/repository/clone.py +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils/repository/rollback.py +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils/utils/__init__.py +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils/utils/binary.py +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils/utils/download.py +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils/utils/irregular_repo.json +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils/utils/logging.py +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils/utils/number.py +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils/utils/path.py +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils/utils/temp.py +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils/utils/tqdm_.py +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils/utils/walk.py +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils.egg-info/SOURCES.txt +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils.egg-info/dependency_links.txt +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils.egg-info/entry_points.txt +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/hfutils.egg-info/top_level.txt +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/requirements-7z.txt +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/requirements-build.txt +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/requirements-doc.txt +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/requirements-rar.txt +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/requirements-test.txt +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/requirements-transfer.txt +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/setup.cfg +0 -0
- {hfutils-0.3.2 → hfutils-0.3.4}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: hfutils
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.4
|
|
4
4
|
Summary: Useful utilities for huggingface
|
|
5
5
|
Home-page: https://github.com/deepghs/hfutils
|
|
6
6
|
Author: narugo1992
|
|
@@ -28,6 +28,7 @@ Requires-Dist: click>=7
|
|
|
28
28
|
Requires-Dist: tzlocal
|
|
29
29
|
Requires-Dist: natsort
|
|
30
30
|
Requires-Dist: urlobject
|
|
31
|
+
Requires-Dist: fsspec>=2024
|
|
31
32
|
Provides-Extra: test
|
|
32
33
|
Requires-Dist: coverage>=5; extra == "test"
|
|
33
34
|
Requires-Dist: mock>=4.0.3; extra == "test"
|
|
@@ -47,12 +47,17 @@ def _add_download_subcommand(cli: click.Group) -> click.Group:
|
|
|
47
47
|
help='Max threads to download.', show_default=True)
|
|
48
48
|
@click.option('-p', '--password', 'password', type=str, default=None,
|
|
49
49
|
help='Password for the archive file. Only applied when -a is used.', show_default=True)
|
|
50
|
+
@click.option('-w', '--wildcard', 'wildcard', type=str, default=None,
|
|
51
|
+
help='Wildcard for files to download. Only applied when -d is used.', show_default=True)
|
|
50
52
|
@click.option('--tmpdir', 'tmpdir', type=str, default=None,
|
|
51
53
|
help='Use custom temporary Directory.', show_default=True)
|
|
52
54
|
@command_wrap()
|
|
53
|
-
def download(
|
|
54
|
-
|
|
55
|
-
|
|
55
|
+
def download(
|
|
56
|
+
repo_id: str, repo_type: RepoTypeTyping,
|
|
57
|
+
file_in_repo: Optional[str], archive_in_repo: Optional[str], dir_in_repo: Optional[str],
|
|
58
|
+
output_path: str, revision: str, max_workers: int,
|
|
59
|
+
password: Optional[str], wildcard: Optional[str], tmpdir: Optional[str]
|
|
60
|
+
):
|
|
56
61
|
"""
|
|
57
62
|
Download data from HuggingFace repositories.
|
|
58
63
|
|
|
@@ -74,6 +79,8 @@ def _add_download_subcommand(cli: click.Group) -> click.Group:
|
|
|
74
79
|
:type max_workers: int
|
|
75
80
|
:param password: Password for the archive file. Only applied when -a is used.
|
|
76
81
|
:type password: str, optional
|
|
82
|
+
:param wildcard: Wildcard for files to download. Only applied when -d is used.
|
|
83
|
+
:type password: str, optional
|
|
77
84
|
:param tmpdir: Use custom temporary Directory.
|
|
78
85
|
:type tmpdir: str, optional
|
|
79
86
|
"""
|
|
@@ -114,6 +121,7 @@ def _add_download_subcommand(cli: click.Group) -> click.Group:
|
|
|
114
121
|
local_directory=output_path,
|
|
115
122
|
repo_id=repo_id,
|
|
116
123
|
dir_in_repo=dir_in_repo,
|
|
124
|
+
pattern=wildcard or '**/*',
|
|
117
125
|
repo_type=repo_type,
|
|
118
126
|
revision=revision,
|
|
119
127
|
silent=False,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from .base import get_hf_client, get_hf_fs, list_files_in_repository
|
|
1
|
+
from .base import get_hf_client, get_hf_fs, list_all_with_pattern, list_files_in_repository
|
|
2
2
|
from .download import download_file_to_file, download_archive_as_directory, download_directory_as_directory
|
|
3
3
|
from .upload import upload_file_to_file, upload_directory_as_archive, upload_directory_as_directory
|
|
4
4
|
from .validate import is_local_file_ready
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module provides utilities for interacting with the Hugging Face Hub API and filesystem.
|
|
3
|
+
It includes functions for retrieving API clients, listing files in repositories, and handling
|
|
4
|
+
file patterns and ignore rules.
|
|
5
|
+
|
|
6
|
+
The module offers the following main functionalities:
|
|
7
|
+
|
|
8
|
+
1. Retrieving Hugging Face API tokens and clients
|
|
9
|
+
2. Accessing the Hugging Face filesystem
|
|
10
|
+
3. Listing files in Hugging Face repositories with pattern matching and ignore rules
|
|
11
|
+
4. Parsing and normalizing Hugging Face filesystem paths
|
|
12
|
+
|
|
13
|
+
These utilities are designed to simplify working with Hugging Face repositories, especially
|
|
14
|
+
when dealing with datasets, models, and spaces.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import fnmatch
|
|
18
|
+
import logging
|
|
19
|
+
import os
|
|
20
|
+
import re
|
|
21
|
+
from functools import lru_cache
|
|
22
|
+
from typing import Literal, List, Optional, Union, Iterator
|
|
23
|
+
|
|
24
|
+
from huggingface_hub import HfApi, HfFileSystem
|
|
25
|
+
from huggingface_hub.hf_api import RepoFolder, RepoFile
|
|
26
|
+
from huggingface_hub.utils import HfHubHTTPError
|
|
27
|
+
|
|
28
|
+
from ..utils import parse_hf_fs_path, hf_fs_path, tqdm, hf_normpath
|
|
29
|
+
|
|
30
|
+
RepoTypeTyping = Literal['dataset', 'model', 'space']
|
|
31
|
+
REPO_TYPES = ['dataset', 'model', 'space']
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@lru_cache()
|
|
35
|
+
def _get_hf_token() -> Optional[str]:
|
|
36
|
+
"""
|
|
37
|
+
Retrieve the Hugging Face token from the environment variable.
|
|
38
|
+
|
|
39
|
+
This function checks for the 'HF_TOKEN' environment variable and returns its value.
|
|
40
|
+
It is cached to avoid repeated environment variable lookups.
|
|
41
|
+
|
|
42
|
+
:return: The Hugging Face token if set, otherwise None.
|
|
43
|
+
:rtype: Optional[str]
|
|
44
|
+
"""
|
|
45
|
+
return os.environ.get('HF_TOKEN')
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@lru_cache()
|
|
49
|
+
def get_hf_client(hf_token: Optional[str] = None) -> HfApi:
|
|
50
|
+
"""
|
|
51
|
+
Get the Hugging Face API client.
|
|
52
|
+
|
|
53
|
+
This function returns an instance of the Hugging Face API client. If a token is not provided,
|
|
54
|
+
it attempts to use the token from the environment variable.
|
|
55
|
+
|
|
56
|
+
:param hf_token: Hugging Face token for API client. If not provided, uses the 'HF_TOKEN' environment variable.
|
|
57
|
+
:type hf_token: Optional[str]
|
|
58
|
+
|
|
59
|
+
:return: An instance of the Hugging Face API client.
|
|
60
|
+
:rtype: HfApi
|
|
61
|
+
|
|
62
|
+
:example:
|
|
63
|
+
|
|
64
|
+
>>> client = get_hf_client()
|
|
65
|
+
>>> # Use client to interact with Hugging Face API
|
|
66
|
+
>>> client.list_repos(organization="huggingface")
|
|
67
|
+
"""
|
|
68
|
+
return HfApi(token=hf_token or _get_hf_token())
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@lru_cache()
|
|
72
|
+
def get_hf_fs(hf_token: Optional[str] = None) -> HfFileSystem:
|
|
73
|
+
"""
|
|
74
|
+
Get the Hugging Face file system.
|
|
75
|
+
|
|
76
|
+
This function returns an instance of the Hugging Face file system. If a token is not provided,
|
|
77
|
+
it attempts to use the token from the environment variable. The file system is configured
|
|
78
|
+
not to use listings cache to ensure fresh results.
|
|
79
|
+
|
|
80
|
+
:param hf_token: Hugging Face token for API client. If not provided, uses the 'HF_TOKEN' environment variable.
|
|
81
|
+
:type hf_token: Optional[str]
|
|
82
|
+
|
|
83
|
+
:return: An instance of the Hugging Face file system.
|
|
84
|
+
:rtype: HfFileSystem
|
|
85
|
+
|
|
86
|
+
:example:
|
|
87
|
+
|
|
88
|
+
>>> fs = get_hf_fs()
|
|
89
|
+
>>> # Use fs to interact with Hugging Face file system
|
|
90
|
+
>>> fs.ls("dataset/example")
|
|
91
|
+
"""
|
|
92
|
+
# use_listings_cache=False is necessary
|
|
93
|
+
# or the result of glob and ls will be cached, the unittest will down
|
|
94
|
+
return HfFileSystem(token=hf_token or _get_hf_token(), use_listings_cache=False)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
_DEFAULT_IGNORE_PATTERNS = ['.git*']
|
|
98
|
+
_IGNORE_PATTERN_UNSET = object()
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _is_file_ignored(file_segments: List[str], ignore_patterns: List[str]) -> bool:
|
|
102
|
+
"""
|
|
103
|
+
Check if a file should be ignored based on the given ignore patterns.
|
|
104
|
+
|
|
105
|
+
This function checks each segment of the file path against the provided ignore patterns.
|
|
106
|
+
If any segment matches any of the patterns, the file is considered ignored.
|
|
107
|
+
|
|
108
|
+
:param file_segments: The segments of the file path.
|
|
109
|
+
:type file_segments: List[str]
|
|
110
|
+
:param ignore_patterns: List of file patterns to ignore.
|
|
111
|
+
:type ignore_patterns: List[str]
|
|
112
|
+
|
|
113
|
+
:return: True if the file should be ignored, False otherwise.
|
|
114
|
+
:rtype: bool
|
|
115
|
+
|
|
116
|
+
:example:
|
|
117
|
+
|
|
118
|
+
>>> _is_file_ignored(['folder', 'file.txt'], ['.git*', '*.log'])
|
|
119
|
+
False
|
|
120
|
+
>>> _is_file_ignored(['folder', '.gitignore'], ['.git*', '*.log'])
|
|
121
|
+
True
|
|
122
|
+
"""
|
|
123
|
+
for segment in file_segments:
|
|
124
|
+
for pattern in ignore_patterns:
|
|
125
|
+
if fnmatch.fnmatch(segment, pattern):
|
|
126
|
+
return True
|
|
127
|
+
|
|
128
|
+
return False
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def list_all_with_pattern(
|
|
132
|
+
repo_id: str, pattern: str = '**/*', repo_type: RepoTypeTyping = 'dataset',
|
|
133
|
+
revision: str = 'main', startup_batch: int = 500, batch_factor: float = 0.8,
|
|
134
|
+
hf_token: Optional[str] = None, silent: bool = False
|
|
135
|
+
) -> Iterator[Union[RepoFile, RepoFolder]]:
|
|
136
|
+
"""
|
|
137
|
+
List all files and folders in a Hugging Face repository matching a given pattern.
|
|
138
|
+
|
|
139
|
+
This function retrieves information about files and folders in a repository that match
|
|
140
|
+
the specified pattern. It uses batching to handle large repositories efficiently.
|
|
141
|
+
|
|
142
|
+
:param repo_id: The identifier of the repository.
|
|
143
|
+
:type repo_id: str
|
|
144
|
+
:param pattern: Wildcard pattern to match files and folders. Default is `**/*` (all files and folders).
|
|
145
|
+
:type pattern: str
|
|
146
|
+
:param repo_type: The type of the repository ('dataset', 'model', 'space'). Default is 'dataset'.
|
|
147
|
+
:type repo_type: RepoTypeTyping
|
|
148
|
+
:param revision: The revision of the repository (e.g., branch, tag, commit hash). Default is 'main'.
|
|
149
|
+
:type revision: str
|
|
150
|
+
:param startup_batch: Initial batch size for retrieving path information. Default is 500.
|
|
151
|
+
:type startup_batch: int
|
|
152
|
+
:param batch_factor: Factor to reduce batch size if a request fails. Default is 0.8.
|
|
153
|
+
:type batch_factor: float
|
|
154
|
+
:param hf_token: Hugging Face token for API client. If not provided, uses the 'HF_TOKEN' environment variable.
|
|
155
|
+
:type hf_token: Optional[str]
|
|
156
|
+
:param silent: If True, suppresses progress bar. Default is False.
|
|
157
|
+
:type silent: bool
|
|
158
|
+
|
|
159
|
+
:return: An iterator of RepoFile and RepoFolder objects matching the pattern.
|
|
160
|
+
:rtype: Iterator[Union[RepoFile, RepoFolder]]
|
|
161
|
+
|
|
162
|
+
:raises HfHubHTTPError: If there's an error in the API request that's not related to batch size.
|
|
163
|
+
|
|
164
|
+
:example:
|
|
165
|
+
|
|
166
|
+
>>> for item in list_all_with_pattern("username/repo", pattern="*.txt"):
|
|
167
|
+
... print(item.path)
|
|
168
|
+
"""
|
|
169
|
+
hf_fs = get_hf_fs(hf_token=hf_token)
|
|
170
|
+
hf_client = get_hf_client(hf_token=hf_token)
|
|
171
|
+
|
|
172
|
+
try:
|
|
173
|
+
paths = [
|
|
174
|
+
parse_hf_fs_path(path).filename
|
|
175
|
+
for path in hf_fs.glob(hf_fs_path(
|
|
176
|
+
repo_id=repo_id,
|
|
177
|
+
repo_type=repo_type,
|
|
178
|
+
filename=pattern,
|
|
179
|
+
revision=revision,
|
|
180
|
+
))
|
|
181
|
+
]
|
|
182
|
+
except FileNotFoundError:
|
|
183
|
+
return
|
|
184
|
+
|
|
185
|
+
offset, batch_size = 0, startup_batch
|
|
186
|
+
progress = tqdm(total=len(paths), desc='Paths Info', silent=silent)
|
|
187
|
+
while offset < len(paths):
|
|
188
|
+
batch_paths = paths[offset:offset + batch_size]
|
|
189
|
+
try:
|
|
190
|
+
all_items = hf_client.get_paths_info(
|
|
191
|
+
repo_id=repo_id,
|
|
192
|
+
repo_type=repo_type,
|
|
193
|
+
paths=batch_paths,
|
|
194
|
+
revision=revision,
|
|
195
|
+
)
|
|
196
|
+
except HfHubHTTPError as err:
|
|
197
|
+
if err.response.status_code == 413:
|
|
198
|
+
new_batch_size = max(1, int(round(batch_size * batch_factor)))
|
|
199
|
+
logging.warning(f'Reducing batch size {batch_size} --> {new_batch_size} ...')
|
|
200
|
+
batch_size = new_batch_size
|
|
201
|
+
continue
|
|
202
|
+
raise
|
|
203
|
+
else:
|
|
204
|
+
progress.update(len(all_items))
|
|
205
|
+
offset += len(all_items)
|
|
206
|
+
yield from all_items
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def list_files_in_repository(
|
|
210
|
+
repo_id: str, repo_type: RepoTypeTyping = 'dataset',
|
|
211
|
+
subdir: str = '', pattern: str = '**/*', revision: str = 'main',
|
|
212
|
+
ignore_patterns: List[str] = _IGNORE_PATTERN_UNSET,
|
|
213
|
+
hf_token: Optional[str] = None, silent: bool = False) -> List[str]:
|
|
214
|
+
"""
|
|
215
|
+
List files in a Hugging Face repository based on the given parameters.
|
|
216
|
+
|
|
217
|
+
This function retrieves a list of file paths in a specified repository that match
|
|
218
|
+
the given pattern and are not ignored by the ignore patterns.
|
|
219
|
+
|
|
220
|
+
:param repo_id: The identifier of the repository.
|
|
221
|
+
:type repo_id: str
|
|
222
|
+
:param repo_type: The type of the repository ('dataset', 'model', 'space'). Default is 'dataset'.
|
|
223
|
+
:type repo_type: RepoTypeTyping
|
|
224
|
+
:param subdir: The subdirectory to list files from. Default is an empty string (root directory).
|
|
225
|
+
:type subdir: str
|
|
226
|
+
:param pattern: Wildcard pattern of the target files. Default is `**/*` (all files).
|
|
227
|
+
:type pattern: str
|
|
228
|
+
:param revision: The revision of the repository (e.g., branch, tag, commit hash). Default is 'main'.
|
|
229
|
+
:type revision: str
|
|
230
|
+
:param ignore_patterns: List of file patterns to ignore. If not set, uses default ignore patterns.
|
|
231
|
+
:type ignore_patterns: List[str]
|
|
232
|
+
:param hf_token: Hugging Face token for API client. If not provided, uses the 'HF_TOKEN' environment variable.
|
|
233
|
+
:type hf_token: Optional[str]
|
|
234
|
+
:param silent: If True, suppresses progress bar. Default is False.
|
|
235
|
+
:type silent: bool
|
|
236
|
+
|
|
237
|
+
:return: A list of file paths that match the criteria.
|
|
238
|
+
:rtype: List[str]
|
|
239
|
+
|
|
240
|
+
:example:
|
|
241
|
+
|
|
242
|
+
>>> files = list_files_in_repository("username/repo", pattern="*.txt", ignore_patterns=[".git*", "*.log"])
|
|
243
|
+
>>> print(files)
|
|
244
|
+
['file1.txt', 'folder/file2.txt']
|
|
245
|
+
"""
|
|
246
|
+
if ignore_patterns is _IGNORE_PATTERN_UNSET:
|
|
247
|
+
ignore_patterns = _DEFAULT_IGNORE_PATTERNS
|
|
248
|
+
|
|
249
|
+
if subdir and subdir != '.':
|
|
250
|
+
pattern = f'{subdir}/{pattern}'
|
|
251
|
+
|
|
252
|
+
result = []
|
|
253
|
+
for item in list_all_with_pattern(
|
|
254
|
+
repo_id=repo_id,
|
|
255
|
+
repo_type=repo_type,
|
|
256
|
+
revision=revision,
|
|
257
|
+
pattern=pattern,
|
|
258
|
+
hf_token=hf_token,
|
|
259
|
+
silent=silent,
|
|
260
|
+
):
|
|
261
|
+
if isinstance(item, RepoFile):
|
|
262
|
+
path = hf_normpath(os.path.relpath(item.path, start=subdir or '.'))
|
|
263
|
+
segments = list(filter(bool, re.split(r'[\\/]+', path)))
|
|
264
|
+
if not _is_file_ignored(segments, ignore_patterns):
|
|
265
|
+
result.append(path)
|
|
266
|
+
|
|
267
|
+
return result
|
|
@@ -80,11 +80,13 @@ def download_archive_as_directory(local_directory: str, repo_id: str, file_in_re
|
|
|
80
80
|
archive_unpack(archive_file, local_directory, password=password)
|
|
81
81
|
|
|
82
82
|
|
|
83
|
-
def download_directory_as_directory(
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
83
|
+
def download_directory_as_directory(
|
|
84
|
+
local_directory: str, repo_id: str, dir_in_repo: str = '.', pattern: str = '**/*',
|
|
85
|
+
repo_type: RepoTypeTyping = 'dataset', revision: str = 'main',
|
|
86
|
+
silent: bool = False, ignore_patterns: List[str] = _IGNORE_PATTERN_UNSET,
|
|
87
|
+
resume_download: bool = True, max_workers: int = 8, max_retries: int = 5,
|
|
88
|
+
hf_token: Optional[str] = None
|
|
89
|
+
):
|
|
88
90
|
"""
|
|
89
91
|
Download all files in a directory from a Hugging Face repository to a local directory.
|
|
90
92
|
|
|
@@ -94,6 +96,8 @@ def download_directory_as_directory(local_directory: str, repo_id: str, dir_in_r
|
|
|
94
96
|
:type repo_id: str
|
|
95
97
|
:param dir_in_repo: The directory path within the repository.
|
|
96
98
|
:type dir_in_repo: str
|
|
99
|
+
:param pattern: Patterns for filtering.
|
|
100
|
+
:type pattern: str
|
|
97
101
|
:param repo_type: The type of the repository ('dataset', 'model', 'space').
|
|
98
102
|
:type repo_type: RepoTypeTyping
|
|
99
103
|
:param revision: The revision of the repository (e.g., branch, tag, commit hash).
|
|
@@ -111,7 +115,15 @@ def download_directory_as_directory(local_directory: str, repo_id: str, dir_in_r
|
|
|
111
115
|
:param hf_token: Huggingface token for API client, use ``HF_TOKEN`` variable if not assigned.
|
|
112
116
|
:type hf_token: str, optional
|
|
113
117
|
"""
|
|
114
|
-
files = list_files_in_repository(
|
|
118
|
+
files = list_files_in_repository(
|
|
119
|
+
repo_id=repo_id,
|
|
120
|
+
repo_type=repo_type,
|
|
121
|
+
subdir=dir_in_repo,
|
|
122
|
+
pattern=pattern,
|
|
123
|
+
revision=revision,
|
|
124
|
+
ignore_patterns=ignore_patterns,
|
|
125
|
+
hf_token=hf_token,
|
|
126
|
+
)
|
|
115
127
|
progress = tqdm(files, silent=silent, desc=f'Downloading {dir_in_repo!r} ...')
|
|
116
128
|
|
|
117
129
|
def _download_one_file(rel_file):
|
|
@@ -135,7 +135,14 @@ def upload_directory_as_directory(
|
|
|
135
135
|
if clear:
|
|
136
136
|
pre_exist_files = {
|
|
137
137
|
tuple(file.split('/')) for file in
|
|
138
|
-
list_files_in_repository(
|
|
138
|
+
list_files_in_repository(
|
|
139
|
+
repo_id=repo_id,
|
|
140
|
+
repo_type=repo_type,
|
|
141
|
+
subdir=path_in_repo,
|
|
142
|
+
revision=revision,
|
|
143
|
+
ignore_patterns=ignore_patterns,
|
|
144
|
+
hf_token=hf_token
|
|
145
|
+
)
|
|
139
146
|
}
|
|
140
147
|
else:
|
|
141
148
|
pre_exist_files = set()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: hfutils
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.4
|
|
4
4
|
Summary: Useful utilities for huggingface
|
|
5
5
|
Home-page: https://github.com/deepghs/hfutils
|
|
6
6
|
Author: narugo1992
|
|
@@ -28,6 +28,7 @@ Requires-Dist: click>=7
|
|
|
28
28
|
Requires-Dist: tzlocal
|
|
29
29
|
Requires-Dist: natsort
|
|
30
30
|
Requires-Dist: urlobject
|
|
31
|
+
Requires-Dist: fsspec>=2024
|
|
31
32
|
Provides-Extra: test
|
|
32
33
|
Requires-Dist: coverage>=5; extra == "test"
|
|
33
34
|
Requires-Dist: mock>=4.0.3; extra == "test"
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
import fnmatch
|
|
2
|
-
import os
|
|
3
|
-
from functools import lru_cache
|
|
4
|
-
from typing import Literal, List, Optional
|
|
5
|
-
|
|
6
|
-
from huggingface_hub import HfApi, HfFileSystem
|
|
7
|
-
|
|
8
|
-
from hfutils.utils import parse_hf_fs_path
|
|
9
|
-
|
|
10
|
-
RepoTypeTyping = Literal['dataset', 'model', 'space']
|
|
11
|
-
REPO_TYPES = ['dataset', 'model', 'space']
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
@lru_cache()
|
|
15
|
-
def _get_hf_token() -> Optional[str]:
|
|
16
|
-
"""
|
|
17
|
-
Retrieve the Hugging Face token from the environment variable.
|
|
18
|
-
|
|
19
|
-
:return: The Hugging Face token.
|
|
20
|
-
:rtype: Optional[str]
|
|
21
|
-
"""
|
|
22
|
-
return os.environ.get('HF_TOKEN')
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
@lru_cache()
|
|
26
|
-
def get_hf_client(hf_token: Optional[str] = None) -> HfApi:
|
|
27
|
-
"""
|
|
28
|
-
Get the Hugging Face API client.
|
|
29
|
-
|
|
30
|
-
:param hf_token: Huggingface token for API client, use ``HF_TOKEN`` variable if not assigned.
|
|
31
|
-
:type hf_token: str, optional
|
|
32
|
-
|
|
33
|
-
:return: The Hugging Face API client.
|
|
34
|
-
:rtype: HfApi
|
|
35
|
-
"""
|
|
36
|
-
return HfApi(token=hf_token or _get_hf_token())
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
@lru_cache()
|
|
40
|
-
def get_hf_fs(hf_token: Optional[str] = None) -> HfFileSystem:
|
|
41
|
-
"""
|
|
42
|
-
Get the Hugging Face file system.
|
|
43
|
-
|
|
44
|
-
:param hf_token: Huggingface token for API client, use ``HF_TOKEN`` variable if not assigned.
|
|
45
|
-
:type hf_token: str, optional
|
|
46
|
-
|
|
47
|
-
:return: The Hugging Face file system.
|
|
48
|
-
:rtype: HfFileSystem
|
|
49
|
-
"""
|
|
50
|
-
# use_listings_cache=False is necessary
|
|
51
|
-
# or the result of glob and ls will be cached, the unittest will down
|
|
52
|
-
return HfFileSystem(token=hf_token or _get_hf_token(), use_listings_cache=False)
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
_DEFAULT_IGNORE_PATTERNS = ['.git*']
|
|
56
|
-
_IGNORE_PATTERN_UNSET = object()
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
def _is_file_ignored(file_segments: List[str], ignore_patterns: List[str]) -> bool:
|
|
60
|
-
"""
|
|
61
|
-
Check if a file should be ignored based on the given ignore patterns.
|
|
62
|
-
|
|
63
|
-
:param file_segments: The segments of the file path.
|
|
64
|
-
:type file_segments: List[str]
|
|
65
|
-
:param ignore_patterns: List of file patterns to ignore.
|
|
66
|
-
:type ignore_patterns: List[str]
|
|
67
|
-
|
|
68
|
-
:return: True if the file should be ignored, False otherwise.
|
|
69
|
-
:rtype: bool
|
|
70
|
-
"""
|
|
71
|
-
for segment in file_segments:
|
|
72
|
-
for pattern in ignore_patterns:
|
|
73
|
-
if fnmatch.fnmatch(segment, pattern):
|
|
74
|
-
return True
|
|
75
|
-
|
|
76
|
-
return False
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
def list_files_in_repository(repo_id: str, repo_type: RepoTypeTyping = 'dataset',
|
|
80
|
-
subdir: str = '', revision: str = 'main',
|
|
81
|
-
ignore_patterns: List[str] = _IGNORE_PATTERN_UNSET,
|
|
82
|
-
hf_token: Optional[str] = None) -> List[str]:
|
|
83
|
-
"""
|
|
84
|
-
List files in a Hugging Face repository based on the given parameters.
|
|
85
|
-
|
|
86
|
-
:param repo_id: The identifier of the repository.
|
|
87
|
-
:type repo_id: str
|
|
88
|
-
:param repo_type: The type of the repository ('dataset', 'model', 'space').
|
|
89
|
-
:type repo_type: RepoTypeTyping
|
|
90
|
-
:param subdir: The subdirectory to list files from.
|
|
91
|
-
:type subdir: str
|
|
92
|
-
:param revision: The revision of the repository (e.g., branch, tag, commit hash).
|
|
93
|
-
:type revision: str
|
|
94
|
-
:param ignore_patterns: List of file patterns to ignore.
|
|
95
|
-
:type ignore_patterns: List[str]
|
|
96
|
-
:param hf_token: Huggingface token for API client, use ``HF_TOKEN`` variable if not assigned.
|
|
97
|
-
:type hf_token: str, optional
|
|
98
|
-
|
|
99
|
-
:return: A list of file paths.
|
|
100
|
-
:rtype: List[str]
|
|
101
|
-
"""
|
|
102
|
-
if ignore_patterns is _IGNORE_PATTERN_UNSET:
|
|
103
|
-
ignore_patterns = _DEFAULT_IGNORE_PATTERNS
|
|
104
|
-
hf_fs = get_hf_fs(hf_token)
|
|
105
|
-
if repo_type == 'model':
|
|
106
|
-
repo_root_path = repo_id
|
|
107
|
-
elif repo_type == 'dataset':
|
|
108
|
-
repo_root_path = f'datasets/{repo_id}'
|
|
109
|
-
elif repo_type == 'space':
|
|
110
|
-
repo_root_path = f'spaces/{repo_id}'
|
|
111
|
-
else:
|
|
112
|
-
raise ValueError(f'Invalid repo_type - {repo_type!r}.')
|
|
113
|
-
if subdir and subdir != '.':
|
|
114
|
-
repo_root_path = f'{repo_root_path}/{subdir}'
|
|
115
|
-
|
|
116
|
-
try:
|
|
117
|
-
_exist_files = [
|
|
118
|
-
parse_hf_fs_path(file).filename
|
|
119
|
-
for file in hf_fs.glob(f'{repo_root_path}/**', revision=revision)
|
|
120
|
-
]
|
|
121
|
-
except FileNotFoundError:
|
|
122
|
-
return []
|
|
123
|
-
if subdir and subdir != '.':
|
|
124
|
-
_exist_files = [os.path.relpath(file, subdir) for file in _exist_files]
|
|
125
|
-
|
|
126
|
-
_exist_ps = sorted([(file, file.split('/')) for file in _exist_files], key=lambda x: x[1])
|
|
127
|
-
retval = []
|
|
128
|
-
for i, (file, segments) in enumerate(_exist_ps):
|
|
129
|
-
if i < len(_exist_ps) - 1 and segments == _exist_ps[i + 1][1][:len(segments)]:
|
|
130
|
-
continue
|
|
131
|
-
if file != '.':
|
|
132
|
-
if not _is_file_ignored(segments, ignore_patterns):
|
|
133
|
-
retval.append('/'.join(segments))
|
|
134
|
-
|
|
135
|
-
return retval
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|