huggingface-hub 0.13.4__py3-none-any.whl → 0.14.0rc0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of huggingface-hub might be problematic. Click here for more details.
- huggingface_hub/__init__.py +59 -5
- huggingface_hub/_commit_api.py +26 -71
- huggingface_hub/_login.py +16 -13
- huggingface_hub/_multi_commits.py +305 -0
- huggingface_hub/_snapshot_download.py +4 -0
- huggingface_hub/_space_api.py +6 -0
- huggingface_hub/_webhooks_payload.py +124 -0
- huggingface_hub/_webhooks_server.py +362 -0
- huggingface_hub/commands/lfs.py +3 -5
- huggingface_hub/commands/user.py +0 -3
- huggingface_hub/community.py +21 -0
- huggingface_hub/constants.py +3 -0
- huggingface_hub/file_download.py +25 -10
- huggingface_hub/hf_api.py +666 -139
- huggingface_hub/hf_file_system.py +441 -0
- huggingface_hub/hub_mixin.py +1 -1
- huggingface_hub/inference_api.py +2 -4
- huggingface_hub/keras_mixin.py +1 -1
- huggingface_hub/lfs.py +196 -176
- huggingface_hub/repocard.py +2 -2
- huggingface_hub/repository.py +1 -1
- huggingface_hub/templates/modelcard_template.md +1 -1
- huggingface_hub/utils/__init__.py +8 -11
- huggingface_hub/utils/_errors.py +4 -4
- huggingface_hub/utils/_experimental.py +65 -0
- huggingface_hub/utils/_git_credential.py +1 -80
- huggingface_hub/utils/_http.py +85 -2
- huggingface_hub/utils/_pagination.py +4 -3
- huggingface_hub/utils/_paths.py +2 -0
- huggingface_hub/utils/_runtime.py +12 -0
- huggingface_hub/utils/_subprocess.py +22 -0
- huggingface_hub/utils/_telemetry.py +2 -4
- huggingface_hub/utils/tqdm.py +23 -18
- {huggingface_hub-0.13.4.dist-info → huggingface_hub-0.14.0rc0.dist-info}/METADATA +5 -1
- huggingface_hub-0.14.0rc0.dist-info/RECORD +61 -0
- {huggingface_hub-0.13.4.dist-info → huggingface_hub-0.14.0rc0.dist-info}/entry_points.txt +3 -0
- huggingface_hub-0.13.4.dist-info/RECORD +0 -56
- {huggingface_hub-0.13.4.dist-info → huggingface_hub-0.14.0rc0.dist-info}/LICENSE +0 -0
- {huggingface_hub-0.13.4.dist-info → huggingface_hub-0.14.0rc0.dist-info}/WHEEL +0 -0
- {huggingface_hub-0.13.4.dist-info → huggingface_hub-0.14.0rc0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
import itertools
|
|
2
|
+
import os
|
|
3
|
+
import tempfile
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from glob import has_magic
|
|
7
|
+
from typing import Any, Dict, List, Optional, Tuple, Union
|
|
8
|
+
from urllib.parse import quote, unquote
|
|
9
|
+
|
|
10
|
+
import fsspec
|
|
11
|
+
import requests
|
|
12
|
+
|
|
13
|
+
from ._commit_api import CommitOperationDelete
|
|
14
|
+
from .constants import DEFAULT_REVISION, ENDPOINT, REPO_TYPE_MODEL, REPO_TYPES_MAPPING, REPO_TYPES_URL_PREFIXES
|
|
15
|
+
from .hf_api import HfApi
|
|
16
|
+
from .utils import (
|
|
17
|
+
EntryNotFoundError,
|
|
18
|
+
HFValidationError,
|
|
19
|
+
RepositoryNotFoundError,
|
|
20
|
+
RevisionNotFoundError,
|
|
21
|
+
hf_raise_for_status,
|
|
22
|
+
http_backoff,
|
|
23
|
+
paginate,
|
|
24
|
+
parse_datetime,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
class HfFileSystemResolvedPath:
|
|
30
|
+
"""Data structure containing information about a resolved hffs path."""
|
|
31
|
+
|
|
32
|
+
repo_type: str
|
|
33
|
+
repo_id: str
|
|
34
|
+
revision: str
|
|
35
|
+
path_in_repo: str
|
|
36
|
+
|
|
37
|
+
def unresolve(self) -> str:
|
|
38
|
+
return (
|
|
39
|
+
f"{REPO_TYPES_URL_PREFIXES.get(self.repo_type, '') + self.repo_id}@{safe_quote(self.revision)}/{self.path_in_repo}"
|
|
40
|
+
.rstrip("/")
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class HfFileSystem(fsspec.AbstractFileSystem):
|
|
45
|
+
"""
|
|
46
|
+
Access a remote Hugging Face Hub repository as if were a local file system.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
endpoint (`str`, *optional*):
|
|
50
|
+
The endpoint to use. If not provided, the default one (https://huggingface.co) is used.
|
|
51
|
+
token (`str`, *optional*):
|
|
52
|
+
Authentication token, obtained with [`HfApi.login`] method. Will default to the stored token.
|
|
53
|
+
|
|
54
|
+
Usage:
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
>>> import hffs
|
|
58
|
+
|
|
59
|
+
>>> fs = hffs.HfFileSystem()
|
|
60
|
+
|
|
61
|
+
>>> # List files
|
|
62
|
+
>>> fs.glob("my-username/my-model/*.bin")
|
|
63
|
+
['my-username/my-model/pytorch_model.bin']
|
|
64
|
+
>>> fs.ls("datasets/my-username/my-dataset", detail=False)
|
|
65
|
+
['datasets/my-username/my-dataset/.gitattributes', 'datasets/my-username/my-dataset/README.md', 'datasets/my-username/my-dataset/data.json']
|
|
66
|
+
|
|
67
|
+
>>> # Read/write files
|
|
68
|
+
>>> with fs.open("my-username/my-model/pytorch_model.bin") as f:
|
|
69
|
+
... data = f.read()
|
|
70
|
+
>>> with fs.open("my-username/my-model/pytorch_model.bin", "wb") as f:
|
|
71
|
+
... f.write(data)
|
|
72
|
+
```
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
root_marker = ""
|
|
76
|
+
protocol = "hf"
|
|
77
|
+
|
|
78
|
+
def __init__(
|
|
79
|
+
self,
|
|
80
|
+
*args,
|
|
81
|
+
endpoint: Optional[str] = None,
|
|
82
|
+
token: Optional[str] = None,
|
|
83
|
+
**storage_options,
|
|
84
|
+
):
|
|
85
|
+
super().__init__(*args, **storage_options)
|
|
86
|
+
self.endpoint = endpoint or ENDPOINT
|
|
87
|
+
self.token = token
|
|
88
|
+
self._api = HfApi(endpoint=endpoint, token=token)
|
|
89
|
+
# Maps (repo_type, repo_id, revision) to a 2-tuple with:
|
|
90
|
+
# * the 1st element indicating whether the repositoy and the revision exist
|
|
91
|
+
# * the 2nd element being the exception raised if the repository or revision doesn't exist
|
|
92
|
+
self._repo_and_revision_exists_cache: Dict[
|
|
93
|
+
Tuple[str, str, Optional[str]], Tuple[bool, Optional[Exception]]
|
|
94
|
+
] = {}
|
|
95
|
+
|
|
96
|
+
def _repo_and_revision_exist(
|
|
97
|
+
self, repo_type: str, repo_id: str, revision: Optional[str]
|
|
98
|
+
) -> Tuple[bool, Optional[Exception]]:
|
|
99
|
+
if (repo_type, repo_id, revision) not in self._repo_and_revision_exists_cache:
|
|
100
|
+
try:
|
|
101
|
+
self._api.repo_info(repo_id, revision=revision, repo_type=repo_type)
|
|
102
|
+
except (RepositoryNotFoundError, HFValidationError) as e:
|
|
103
|
+
self._repo_and_revision_exists_cache[(repo_type, repo_id, revision)] = False, e
|
|
104
|
+
self._repo_and_revision_exists_cache[(repo_type, repo_id, None)] = False, e
|
|
105
|
+
except RevisionNotFoundError as e:
|
|
106
|
+
self._repo_and_revision_exists_cache[(repo_type, repo_id, revision)] = False, e
|
|
107
|
+
self._repo_and_revision_exists_cache[(repo_type, repo_id, None)] = True, None
|
|
108
|
+
else:
|
|
109
|
+
self._repo_and_revision_exists_cache[(repo_type, repo_id, revision)] = True, None
|
|
110
|
+
self._repo_and_revision_exists_cache[(repo_type, repo_id, None)] = True, None
|
|
111
|
+
return self._repo_and_revision_exists_cache[(repo_type, repo_id, revision)]
|
|
112
|
+
|
|
113
|
+
def resolve_path(self, path: str, revision: Optional[str] = None) -> HfFileSystemResolvedPath:
|
|
114
|
+
def _align_revision_in_path_with_revision(
|
|
115
|
+
revision_in_path: Optional[str], revision: Optional[str]
|
|
116
|
+
) -> Optional[str]:
|
|
117
|
+
if revision is not None:
|
|
118
|
+
if revision_in_path is not None and revision_in_path != revision:
|
|
119
|
+
raise ValueError(
|
|
120
|
+
f'Revision specified in path ("{revision_in_path}") and in `revision` argument ("{revision}")'
|
|
121
|
+
" are not the same."
|
|
122
|
+
)
|
|
123
|
+
else:
|
|
124
|
+
revision = revision_in_path
|
|
125
|
+
return revision
|
|
126
|
+
|
|
127
|
+
path = self._strip_protocol(path)
|
|
128
|
+
if not path:
|
|
129
|
+
# can't list repositories at root
|
|
130
|
+
raise NotImplementedError("Access to repositories lists is not implemented.")
|
|
131
|
+
elif path.split("/")[0] + "/" in REPO_TYPES_URL_PREFIXES.values():
|
|
132
|
+
if "/" not in path:
|
|
133
|
+
# can't list repositories at the repository type level
|
|
134
|
+
raise NotImplementedError("Acces to repositories lists is not implemented.")
|
|
135
|
+
repo_type, path = path.split("/", 1)
|
|
136
|
+
repo_type = REPO_TYPES_MAPPING[repo_type]
|
|
137
|
+
else:
|
|
138
|
+
repo_type = REPO_TYPE_MODEL
|
|
139
|
+
if path.count("/") > 0:
|
|
140
|
+
if "@" in path:
|
|
141
|
+
repo_id, revision_in_path = path.split("@", 1)
|
|
142
|
+
if "/" in revision_in_path:
|
|
143
|
+
revision_in_path, path_in_repo = revision_in_path.split("/", 1)
|
|
144
|
+
else:
|
|
145
|
+
path_in_repo = ""
|
|
146
|
+
revision_in_path = unquote(revision_in_path)
|
|
147
|
+
revision = _align_revision_in_path_with_revision(revision_in_path, revision)
|
|
148
|
+
repo_and_revision_exist, err = self._repo_and_revision_exist(repo_type, repo_id, revision)
|
|
149
|
+
if not repo_and_revision_exist:
|
|
150
|
+
raise FileNotFoundError(path) from err
|
|
151
|
+
else:
|
|
152
|
+
repo_id_with_namespace = "/".join(path.split("/")[:2])
|
|
153
|
+
path_in_repo_with_namespace = "/".join(path.split("/")[2:])
|
|
154
|
+
repo_id_without_namespace = path.split("/")[0]
|
|
155
|
+
path_in_repo_without_namespace = "/".join(path.split("/")[1:])
|
|
156
|
+
repo_id = repo_id_with_namespace
|
|
157
|
+
path_in_repo = path_in_repo_with_namespace
|
|
158
|
+
repo_and_revision_exist, err = self._repo_and_revision_exist(repo_type, repo_id, revision)
|
|
159
|
+
if not repo_and_revision_exist:
|
|
160
|
+
if isinstance(err, (RepositoryNotFoundError, HFValidationError)):
|
|
161
|
+
repo_id = repo_id_without_namespace
|
|
162
|
+
path_in_repo = path_in_repo_without_namespace
|
|
163
|
+
repo_and_revision_exist, _ = self._repo_and_revision_exist(repo_type, repo_id, revision)
|
|
164
|
+
if not repo_and_revision_exist:
|
|
165
|
+
raise FileNotFoundError(path) from err
|
|
166
|
+
else:
|
|
167
|
+
raise FileNotFoundError(path) from err
|
|
168
|
+
else:
|
|
169
|
+
repo_id = path
|
|
170
|
+
path_in_repo = ""
|
|
171
|
+
if "@" in path:
|
|
172
|
+
repo_id, revision_in_path = path.split("@", 1)
|
|
173
|
+
revision_in_path = unquote(revision_in_path)
|
|
174
|
+
revision = _align_revision_in_path_with_revision(revision_in_path, revision)
|
|
175
|
+
repo_and_revision_exist, _ = self._repo_and_revision_exist(repo_type, repo_id, revision)
|
|
176
|
+
if not repo_and_revision_exist:
|
|
177
|
+
raise NotImplementedError("Acces to repositories lists is not implemented.")
|
|
178
|
+
|
|
179
|
+
revision = revision if revision is not None else DEFAULT_REVISION
|
|
180
|
+
return HfFileSystemResolvedPath(repo_type, repo_id, revision, path_in_repo)
|
|
181
|
+
|
|
182
|
+
def invalidate_cache(self, path: Optional[str] = None) -> None:
|
|
183
|
+
if not path:
|
|
184
|
+
self.dircache.clear()
|
|
185
|
+
self._repository_type_and_id_exists_cache.clear()
|
|
186
|
+
else:
|
|
187
|
+
path = self.resolve_path(path).unresolve()
|
|
188
|
+
while path:
|
|
189
|
+
self.dircache.pop(path, None)
|
|
190
|
+
path = self._parent(path)
|
|
191
|
+
|
|
192
|
+
def _open(
|
|
193
|
+
self,
|
|
194
|
+
path: str,
|
|
195
|
+
mode: str = "rb",
|
|
196
|
+
revision: Optional[str] = None,
|
|
197
|
+
**kwargs,
|
|
198
|
+
) -> "HfFileSystemFile":
|
|
199
|
+
if mode == "ab":
|
|
200
|
+
raise NotImplementedError("Appending to remote files is not yet supported.")
|
|
201
|
+
return HfFileSystemFile(self, path, mode=mode, revision=revision, **kwargs)
|
|
202
|
+
|
|
203
|
+
def _rm(self, path: str, revision: Optional[str] = None, **kwargs) -> None:
|
|
204
|
+
resolved_path = self.resolve_path(path, revision=revision)
|
|
205
|
+
self._api.delete_file(
|
|
206
|
+
path_in_repo=resolved_path.path_in_repo,
|
|
207
|
+
repo_id=resolved_path.repo_id,
|
|
208
|
+
token=self.token,
|
|
209
|
+
repo_type=resolved_path.repo_type,
|
|
210
|
+
revision=resolved_path.revision,
|
|
211
|
+
commit_message=kwargs.get("commit_message"),
|
|
212
|
+
commit_description=kwargs.get("commit_description"),
|
|
213
|
+
)
|
|
214
|
+
self.invalidate_cache(path=resolved_path.unresolve())
|
|
215
|
+
|
|
216
|
+
def rm(
|
|
217
|
+
self,
|
|
218
|
+
path: str,
|
|
219
|
+
recursive: bool = False,
|
|
220
|
+
maxdepth: Optional[int] = None,
|
|
221
|
+
revision: Optional[str] = None,
|
|
222
|
+
**kwargs,
|
|
223
|
+
) -> None:
|
|
224
|
+
resolved_path = self.resolve_path(path, revision=revision)
|
|
225
|
+
root_path = REPO_TYPES_URL_PREFIXES.get(resolved_path.repo_type, "") + resolved_path.repo_id
|
|
226
|
+
paths = self.expand_path(path, recursive=recursive, maxdepth=maxdepth, revision=resolved_path.revision)
|
|
227
|
+
paths_in_repo = [path[len(root_path) + 1 :] for path in paths if not self.isdir(path)]
|
|
228
|
+
operations = [CommitOperationDelete(path_in_repo=path_in_repo) for path_in_repo in paths_in_repo]
|
|
229
|
+
commit_message = f"Delete {path} "
|
|
230
|
+
commit_message += "recursively " if recursive else ""
|
|
231
|
+
commit_message += f"up to depth {maxdepth} " if maxdepth is not None else ""
|
|
232
|
+
# TODO: use `commit_description` to list all the deleted paths?
|
|
233
|
+
self._api.create_commit(
|
|
234
|
+
repo_id=resolved_path.repo_id,
|
|
235
|
+
repo_type=resolved_path.repo_type,
|
|
236
|
+
token=self.token,
|
|
237
|
+
operations=operations,
|
|
238
|
+
revision=resolved_path.revision,
|
|
239
|
+
commit_message=kwargs.get("commit_message", commit_message),
|
|
240
|
+
commit_description=kwargs.get("commit_description"),
|
|
241
|
+
)
|
|
242
|
+
self.invalidate_cache(path=resolved_path.unresolve())
|
|
243
|
+
|
|
244
|
+
def ls(
|
|
245
|
+
self, path: str, detail: bool = True, refresh: bool = False, revision: Optional[str] = None, **kwargs
|
|
246
|
+
) -> List[Union[str, Dict[str, Any]]]:
|
|
247
|
+
"""List the contents of a directory."""
|
|
248
|
+
resolved_path = self.resolve_path(path, revision=revision)
|
|
249
|
+
revision_in_path = "@" + safe_quote(resolved_path.revision)
|
|
250
|
+
has_revision_in_path = revision_in_path in path
|
|
251
|
+
path = resolved_path.unresolve()
|
|
252
|
+
if path not in self.dircache or refresh:
|
|
253
|
+
path_prefix = (
|
|
254
|
+
HfFileSystemResolvedPath(
|
|
255
|
+
resolved_path.repo_type, resolved_path.repo_id, resolved_path.revision, ""
|
|
256
|
+
).unresolve()
|
|
257
|
+
+ "/"
|
|
258
|
+
)
|
|
259
|
+
tree_path = path
|
|
260
|
+
tree_iter = self._iter_tree(tree_path, revision=resolved_path.revision)
|
|
261
|
+
try:
|
|
262
|
+
tree_item = next(tree_iter)
|
|
263
|
+
except EntryNotFoundError:
|
|
264
|
+
if "/" in resolved_path.path_in_repo:
|
|
265
|
+
tree_path = self._parent(path)
|
|
266
|
+
tree_iter = self._iter_tree(tree_path, revision=resolved_path.revision)
|
|
267
|
+
else:
|
|
268
|
+
raise
|
|
269
|
+
else:
|
|
270
|
+
tree_iter = itertools.chain([tree_item], tree_iter)
|
|
271
|
+
child_infos = []
|
|
272
|
+
for tree_item in tree_iter:
|
|
273
|
+
child_info = {
|
|
274
|
+
"name": path_prefix + tree_item["path"],
|
|
275
|
+
"size": tree_item["size"],
|
|
276
|
+
"type": tree_item["type"],
|
|
277
|
+
}
|
|
278
|
+
if tree_item["type"] == "file":
|
|
279
|
+
child_info.update(
|
|
280
|
+
{
|
|
281
|
+
"blob_id": tree_item["oid"],
|
|
282
|
+
"lfs": tree_item.get("lfs"),
|
|
283
|
+
"last_modified": parse_datetime(tree_item["lastCommit"]["date"]),
|
|
284
|
+
},
|
|
285
|
+
)
|
|
286
|
+
child_infos.append(child_info)
|
|
287
|
+
self.dircache[tree_path] = child_infos
|
|
288
|
+
out = self._ls_from_cache(path)
|
|
289
|
+
if not has_revision_in_path:
|
|
290
|
+
out = [{**o, "name": o["name"].replace(revision_in_path, "", 1)} for o in out]
|
|
291
|
+
return out if detail else [o["name"] for o in out]
|
|
292
|
+
|
|
293
|
+
def _iter_tree(self, path: str, revision: Optional[str] = None):
|
|
294
|
+
# TODO: use HfApi.list_files_info instead when it supports "lastCommit" and "expand=True"
|
|
295
|
+
# See https://github.com/huggingface/moon-landing/issues/5993
|
|
296
|
+
resolved_path = self.resolve_path(path, revision=revision)
|
|
297
|
+
path = f"{self._api.endpoint}/api/{resolved_path.repo_type}s/{resolved_path.repo_id}/tree/{safe_quote(resolved_path.revision)}/{resolved_path.path_in_repo}".rstrip(
|
|
298
|
+
"/"
|
|
299
|
+
)
|
|
300
|
+
headers = self._api._build_hf_headers()
|
|
301
|
+
yield from paginate(path, params={"expand": True}, headers=headers)
|
|
302
|
+
|
|
303
|
+
def cp_file(self, path1: str, path2: str, revision: Optional[str] = None, **kwargs) -> None:
|
|
304
|
+
resolved_path1 = self.resolve_path(path1, revision=revision)
|
|
305
|
+
resolved_path2 = self.resolve_path(path2, revision=revision)
|
|
306
|
+
|
|
307
|
+
same_repo = (
|
|
308
|
+
resolved_path1.repo_type == resolved_path2.repo_type and resolved_path1.repo_id == resolved_path2.repo_id
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
# TODO: Wait for https://github.com/huggingface/huggingface_hub/issues/1083 to be resolved to simplify this logic
|
|
312
|
+
if same_repo and self.info(path1, revision=resolved_path1.revision)["lfs"] is not None:
|
|
313
|
+
headers = self._api._build_hf_headers(is_write_action=True)
|
|
314
|
+
commit_message = f"Copy {path1} to {path2}"
|
|
315
|
+
payload = {
|
|
316
|
+
"summary": kwargs.get("commit_message", commit_message),
|
|
317
|
+
"description": kwargs.get("commit_description", ""),
|
|
318
|
+
"files": [],
|
|
319
|
+
"lfsFiles": [
|
|
320
|
+
{
|
|
321
|
+
"path": resolved_path2.path_in_repo,
|
|
322
|
+
"algo": "sha256",
|
|
323
|
+
"oid": self.info(path1, revision=resolved_path1.revision)["lfs"]["oid"],
|
|
324
|
+
}
|
|
325
|
+
],
|
|
326
|
+
"deletedFiles": [],
|
|
327
|
+
}
|
|
328
|
+
r = requests.post(
|
|
329
|
+
f"{self.endpoint}/api/{resolved_path1.repo_type}s/{resolved_path1.repo_id}/commit/{safe_quote(resolved_path2.revision)}",
|
|
330
|
+
json=payload,
|
|
331
|
+
headers=headers,
|
|
332
|
+
)
|
|
333
|
+
hf_raise_for_status(r)
|
|
334
|
+
else:
|
|
335
|
+
with self.open(path1, "rb", revision=resolved_path1.revision) as f:
|
|
336
|
+
content = f.read()
|
|
337
|
+
commit_message = f"Copy {path1} to {path2}"
|
|
338
|
+
self._api.upload_file(
|
|
339
|
+
path_or_fileobj=content,
|
|
340
|
+
path_in_repo=resolved_path2.path_in_repo,
|
|
341
|
+
repo_id=resolved_path2.repo_id,
|
|
342
|
+
token=self.token,
|
|
343
|
+
repo_type=resolved_path2.repo_type,
|
|
344
|
+
revision=resolved_path2.revision,
|
|
345
|
+
commit_message=kwargs.get("commit_message", commit_message),
|
|
346
|
+
commit_description=kwargs.get("commit_description"),
|
|
347
|
+
)
|
|
348
|
+
self.invalidate_cache(path=resolved_path1.unresolve())
|
|
349
|
+
self.invalidate_cache(path=resolved_path2.unresolve())
|
|
350
|
+
|
|
351
|
+
def modified(self, path: str, **kwargs) -> datetime:
|
|
352
|
+
info = self.info(path, **kwargs)
|
|
353
|
+
if "last_modified" not in info:
|
|
354
|
+
raise IsADirectoryError(path)
|
|
355
|
+
return info["last_modified"]
|
|
356
|
+
|
|
357
|
+
def info(self, path: str, **kwargs) -> Dict[str, Any]:
|
|
358
|
+
resolved_path = self.resolve_path(path)
|
|
359
|
+
if not resolved_path.path_in_repo:
|
|
360
|
+
revision_in_path = "@" + safe_quote(resolved_path.revision)
|
|
361
|
+
has_revision_in_path = revision_in_path in path
|
|
362
|
+
name = resolved_path.unresolve()
|
|
363
|
+
name = name.replace(revision_in_path, "", 1) if not has_revision_in_path else name
|
|
364
|
+
return {"name": name, "size": 0, "type": "directory"}
|
|
365
|
+
return super().info(path, **kwargs)
|
|
366
|
+
|
|
367
|
+
def expand_path(
|
|
368
|
+
self, path: Union[str, List[str]], recursive: bool = False, maxdepth: Optional[int] = None, **kwargs
|
|
369
|
+
) -> List[str]:
|
|
370
|
+
# The default implementation does not allow passing custom kwargs (e.g., we use these kwargs to propagate the `revision`)
|
|
371
|
+
if maxdepth is not None and maxdepth < 1:
|
|
372
|
+
raise ValueError("maxdepth must be at least 1")
|
|
373
|
+
|
|
374
|
+
if isinstance(path, str):
|
|
375
|
+
return self.expand_path([path], recursive, maxdepth)
|
|
376
|
+
|
|
377
|
+
out = set()
|
|
378
|
+
path = [self._strip_protocol(p) for p in path]
|
|
379
|
+
for p in path:
|
|
380
|
+
if has_magic(p):
|
|
381
|
+
bit = set(self.glob(p, **kwargs))
|
|
382
|
+
out |= bit
|
|
383
|
+
if recursive:
|
|
384
|
+
out |= set(self.expand_path(list(bit), recursive=recursive, maxdepth=maxdepth, **kwargs))
|
|
385
|
+
continue
|
|
386
|
+
elif recursive:
|
|
387
|
+
rec = set(self.find(p, maxdepth=maxdepth, withdirs=True, detail=False, **kwargs))
|
|
388
|
+
out |= rec
|
|
389
|
+
if p not in out and (recursive is False or self.exists(p)):
|
|
390
|
+
# should only check once, for the root
|
|
391
|
+
out.add(p)
|
|
392
|
+
if not out:
|
|
393
|
+
raise FileNotFoundError(path)
|
|
394
|
+
return list(sorted(out))
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
class HfFileSystemFile(fsspec.spec.AbstractBufferedFile):
|
|
398
|
+
def __init__(self, fs: HfFileSystem, path: str, revision: Optional[str] = None, **kwargs):
|
|
399
|
+
super().__init__(fs, path, **kwargs)
|
|
400
|
+
self.fs: HfFileSystem
|
|
401
|
+
self.resolved_path = fs.resolve_path(path, revision=revision)
|
|
402
|
+
|
|
403
|
+
def _fetch_range(self, start: int, end: int) -> bytes:
|
|
404
|
+
headers = {
|
|
405
|
+
"range": f"bytes={start}-{end - 1}",
|
|
406
|
+
**self.fs._api._build_hf_headers(),
|
|
407
|
+
}
|
|
408
|
+
url = (
|
|
409
|
+
f"{self.fs.endpoint}/{REPO_TYPES_URL_PREFIXES.get(self.resolved_path.repo_type, '') + self.resolved_path.repo_id}/resolve/{safe_quote(self.resolved_path.revision)}/{safe_quote(self.resolved_path.path_in_repo)}"
|
|
410
|
+
)
|
|
411
|
+
r = http_backoff("GET", url, headers=headers)
|
|
412
|
+
hf_raise_for_status(r)
|
|
413
|
+
return r.content
|
|
414
|
+
|
|
415
|
+
def _initiate_upload(self) -> None:
|
|
416
|
+
self.temp_file = tempfile.NamedTemporaryFile(prefix="hffs-", delete=False)
|
|
417
|
+
|
|
418
|
+
def _upload_chunk(self, final: bool = False) -> None:
|
|
419
|
+
self.buffer.seek(0)
|
|
420
|
+
block = self.buffer.read()
|
|
421
|
+
self.temp_file.write(block)
|
|
422
|
+
if final:
|
|
423
|
+
self.temp_file.close()
|
|
424
|
+
self.fs._api.upload_file(
|
|
425
|
+
path_or_fileobj=self.temp_file.name,
|
|
426
|
+
path_in_repo=self.resolved_path.path_in_repo,
|
|
427
|
+
repo_id=self.resolved_path.repo_id,
|
|
428
|
+
token=self.fs.token,
|
|
429
|
+
repo_type=self.resolved_path.repo_type,
|
|
430
|
+
revision=self.resolved_path.revision,
|
|
431
|
+
commit_message=self.kwargs.get("commit_message"),
|
|
432
|
+
commit_description=self.kwargs.get("commit_description"),
|
|
433
|
+
)
|
|
434
|
+
os.remove(self.temp_file.name)
|
|
435
|
+
self.fs.invalidate_cache(
|
|
436
|
+
path=self.resolved_path.unresolve(),
|
|
437
|
+
)
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
def safe_quote(s: str) -> str:
|
|
441
|
+
return quote(s, safe="")
|
huggingface_hub/hub_mixin.py
CHANGED
|
@@ -260,7 +260,7 @@ class ModelHubMixin:
|
|
|
260
260
|
|
|
261
261
|
Args:
|
|
262
262
|
repo_id (`str`):
|
|
263
|
-
|
|
263
|
+
ID of the repository to push to (example: `"username/my-model"`).
|
|
264
264
|
config (`dict`, *optional*):
|
|
265
265
|
Configuration object to be saved alongside the model weights.
|
|
266
266
|
commit_message (`str`, *optional*):
|
huggingface_hub/inference_api.py
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import io
|
|
2
2
|
from typing import Any, Dict, List, Optional, Union
|
|
3
3
|
|
|
4
|
-
import requests
|
|
5
|
-
|
|
6
4
|
from .constants import INFERENCE_ENDPOINT
|
|
7
5
|
from .hf_api import HfApi
|
|
8
|
-
from .utils import build_hf_headers, is_pillow_available, logging, validate_hf_hub_args
|
|
6
|
+
from .utils import build_hf_headers, get_session, is_pillow_available, logging, validate_hf_hub_args
|
|
9
7
|
|
|
10
8
|
|
|
11
9
|
logger = logging.get_logger(__name__)
|
|
@@ -179,7 +177,7 @@ class InferenceApi:
|
|
|
179
177
|
payload["parameters"] = params
|
|
180
178
|
|
|
181
179
|
# Make API call
|
|
182
|
-
response =
|
|
180
|
+
response = get_session().post(self.api_url, headers=self.headers, json=payload, data=data)
|
|
183
181
|
|
|
184
182
|
# Let the user handle the response
|
|
185
183
|
if raw_response:
|
huggingface_hub/keras_mixin.py
CHANGED
|
@@ -306,7 +306,7 @@ def push_to_hub_keras(
|
|
|
306
306
|
The [Keras model](`https://www.tensorflow.org/api_docs/python/tf/keras/Model`) you'd like to push to the
|
|
307
307
|
Hub. The model must be compiled and built.
|
|
308
308
|
repo_id (`str`):
|
|
309
|
-
|
|
309
|
+
ID of the repository to push to (example: `"username/my-model"`).
|
|
310
310
|
commit_message (`str`, *optional*, defaults to "Add Keras model"):
|
|
311
311
|
Message to commit while pushing.
|
|
312
312
|
private (`bool`, *optional*, defaults to `False`):
|