huggingface-hub 0.13.3__py3-none-any.whl → 0.14.0.dev0__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 +17 -16
- 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 +54 -13
- 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.3.dist-info → huggingface_hub-0.14.0.dev0.dist-info}/METADATA +5 -1
- huggingface_hub-0.14.0.dev0.dist-info/RECORD +61 -0
- {huggingface_hub-0.13.3.dist-info → huggingface_hub-0.14.0.dev0.dist-info}/entry_points.txt +3 -0
- huggingface_hub-0.13.3.dist-info/RECORD +0 -56
- {huggingface_hub-0.13.3.dist-info → huggingface_hub-0.14.0.dev0.dist-info}/LICENSE +0 -0
- {huggingface_hub-0.13.3.dist-info → huggingface_hub-0.14.0.dev0.dist-info}/WHEEL +0 -0
- {huggingface_hub-0.13.3.dist-info → huggingface_hub-0.14.0.dev0.dist-info}/top_level.txt +0 -0
huggingface_hub/hf_api.py
CHANGED
|
@@ -27,7 +27,13 @@ from urllib.parse import quote
|
|
|
27
27
|
import requests
|
|
28
28
|
from requests.exceptions import HTTPError
|
|
29
29
|
|
|
30
|
-
from huggingface_hub.utils import
|
|
30
|
+
from huggingface_hub.utils import (
|
|
31
|
+
IGNORE_GIT_FOLDER_PATTERNS,
|
|
32
|
+
EntryNotFoundError,
|
|
33
|
+
RepositoryNotFoundError,
|
|
34
|
+
experimental,
|
|
35
|
+
get_session,
|
|
36
|
+
)
|
|
31
37
|
|
|
32
38
|
from ._commit_api import (
|
|
33
39
|
CommitOperation,
|
|
@@ -38,6 +44,19 @@ from ._commit_api import (
|
|
|
38
44
|
upload_lfs_files,
|
|
39
45
|
warn_on_overwriting_operations,
|
|
40
46
|
)
|
|
47
|
+
from ._multi_commits import (
|
|
48
|
+
MULTI_COMMIT_PR_CLOSE_COMMENT_FAILURE_BAD_REQUEST_TEMPLATE,
|
|
49
|
+
MULTI_COMMIT_PR_CLOSE_COMMENT_FAILURE_NO_CHANGES_TEMPLATE,
|
|
50
|
+
MULTI_COMMIT_PR_CLOSING_COMMENT_TEMPLATE,
|
|
51
|
+
MULTI_COMMIT_PR_COMPLETION_COMMENT_TEMPLATE,
|
|
52
|
+
MultiCommitException,
|
|
53
|
+
MultiCommitStep,
|
|
54
|
+
MultiCommitStrategy,
|
|
55
|
+
multi_commit_create_pull_request,
|
|
56
|
+
multi_commit_generate_comment,
|
|
57
|
+
multi_commit_parse_pr_description,
|
|
58
|
+
plan_multi_commits,
|
|
59
|
+
)
|
|
41
60
|
from ._space_api import SpaceHardware, SpaceRuntime
|
|
42
61
|
from .community import (
|
|
43
62
|
Discussion,
|
|
@@ -58,24 +77,21 @@ from .constants import (
|
|
|
58
77
|
SPACES_SDK_TYPES,
|
|
59
78
|
)
|
|
60
79
|
from .utils import ( # noqa: F401 # imported for backward compatibility
|
|
80
|
+
BadRequestError,
|
|
61
81
|
HfFolder,
|
|
62
82
|
HfHubHTTPError,
|
|
63
83
|
build_hf_headers,
|
|
64
|
-
erase_from_credential_store,
|
|
65
84
|
filter_repo_objects,
|
|
66
85
|
hf_raise_for_status,
|
|
67
86
|
logging,
|
|
87
|
+
paginate,
|
|
68
88
|
parse_datetime,
|
|
69
|
-
read_from_credential_store,
|
|
70
89
|
validate_hf_hub_args,
|
|
71
|
-
write_to_credential_store,
|
|
72
90
|
)
|
|
73
91
|
from .utils._deprecation import (
|
|
74
92
|
_deprecate_arguments,
|
|
75
93
|
_deprecate_list_output,
|
|
76
|
-
_deprecate_method,
|
|
77
94
|
)
|
|
78
|
-
from .utils._pagination import paginate
|
|
79
95
|
from .utils._typing import Literal, TypedDict
|
|
80
96
|
from .utils.endpoint_helpers import (
|
|
81
97
|
AttributeDictionary,
|
|
@@ -90,6 +106,7 @@ from .utils.endpoint_helpers import (
|
|
|
90
106
|
USERNAME_PLACEHOLDER = "hf_user"
|
|
91
107
|
_REGEX_DISCUSSION_URL = re.compile(r".*/discussions/(\d+)$")
|
|
92
108
|
|
|
109
|
+
|
|
93
110
|
logger = logging.get_logger(__name__)
|
|
94
111
|
|
|
95
112
|
|
|
@@ -192,6 +209,7 @@ def repo_type_and_id_from_hf_id(hf_id: str, hub_url: Optional[str] = None) -> Tu
|
|
|
192
209
|
class BlobLfsInfo(TypedDict, total=False):
|
|
193
210
|
size: int
|
|
194
211
|
sha256: str
|
|
212
|
+
pointer_size: int
|
|
195
213
|
|
|
196
214
|
|
|
197
215
|
@dataclass
|
|
@@ -312,23 +330,21 @@ class RepoUrl(str):
|
|
|
312
330
|
|
|
313
331
|
class RepoFile(ReprMixin):
|
|
314
332
|
"""
|
|
315
|
-
Data structure that represents a public file inside a repo, accessible from
|
|
316
|
-
huggingface.co
|
|
333
|
+
Data structure that represents a public file inside a repo, accessible from huggingface.co
|
|
317
334
|
|
|
318
335
|
Args:
|
|
319
336
|
rfilename (str):
|
|
320
|
-
file name, relative to the repo root. This is the only attribute
|
|
321
|
-
|
|
322
|
-
certain other stuff.
|
|
337
|
+
file name, relative to the repo root. This is the only attribute that's guaranteed to be here, but under
|
|
338
|
+
certain conditions there can certain other stuff.
|
|
323
339
|
size (`int`, *optional*):
|
|
324
|
-
The file's size, in bytes. This attribute is present when `files_metadata` argument
|
|
325
|
-
|
|
340
|
+
The file's size, in bytes. This attribute is present when `files_metadata` argument of [`repo_info`] is set
|
|
341
|
+
to `True`. It's `None` otherwise.
|
|
326
342
|
blob_id (`str`, *optional*):
|
|
327
|
-
The file's git OID. This attribute is present when `files_metadata` argument
|
|
328
|
-
|
|
343
|
+
The file's git OID. This attribute is present when `files_metadata` argument of [`repo_info`] is set to
|
|
344
|
+
`True`. It's `None` otherwise.
|
|
329
345
|
lfs (`BlobLfsInfo`, *optional*):
|
|
330
|
-
The file's LFS metadata. This attribute is present when`files_metadata` argument
|
|
331
|
-
|
|
346
|
+
The file's LFS metadata. This attribute is present when`files_metadata` argument of [`repo_info`] is set to
|
|
347
|
+
`True` and the file is stored with Git LFS. It's `None` otherwise.
|
|
332
348
|
"""
|
|
333
349
|
|
|
334
350
|
def __init__(
|
|
@@ -822,7 +838,7 @@ class HfApi:
|
|
|
822
838
|
Hugging Face token. Will default to the locally saved token if
|
|
823
839
|
not provided.
|
|
824
840
|
"""
|
|
825
|
-
r =
|
|
841
|
+
r = get_session().get(
|
|
826
842
|
f"{self.endpoint}/api/whoami-v2",
|
|
827
843
|
headers=self._build_hf_headers(
|
|
828
844
|
# If `token` is provided and not `None`, it will be used by default.
|
|
@@ -857,43 +873,10 @@ class HfApi:
|
|
|
857
873
|
except HTTPError:
|
|
858
874
|
return False
|
|
859
875
|
|
|
860
|
-
@staticmethod
|
|
861
|
-
@_deprecate_method(
|
|
862
|
-
version="0.14",
|
|
863
|
-
message=(
|
|
864
|
-
"`HfApi.set_access_token` is deprecated as it is very ambiguous. Use"
|
|
865
|
-
" `login` or `set_git_credential` instead."
|
|
866
|
-
),
|
|
867
|
-
)
|
|
868
|
-
def set_access_token(access_token: str):
|
|
869
|
-
"""
|
|
870
|
-
Saves the passed access token so git can correctly authenticate the
|
|
871
|
-
user.
|
|
872
|
-
|
|
873
|
-
Args:
|
|
874
|
-
access_token (`str`):
|
|
875
|
-
The access token to save.
|
|
876
|
-
"""
|
|
877
|
-
write_to_credential_store(USERNAME_PLACEHOLDER, access_token)
|
|
878
|
-
|
|
879
|
-
@staticmethod
|
|
880
|
-
@_deprecate_method(
|
|
881
|
-
version="0.14",
|
|
882
|
-
message=(
|
|
883
|
-
"`HfApi.unset_access_token` is deprecated as it is very ambiguous. Use"
|
|
884
|
-
" `login` or `unset_git_credential` instead."
|
|
885
|
-
),
|
|
886
|
-
)
|
|
887
|
-
def unset_access_token():
|
|
888
|
-
"""
|
|
889
|
-
Resets the user's access token.
|
|
890
|
-
"""
|
|
891
|
-
erase_from_credential_store(USERNAME_PLACEHOLDER)
|
|
892
|
-
|
|
893
876
|
def get_model_tags(self) -> ModelTags:
|
|
894
877
|
"Gets all valid model tags as a nested namespace object"
|
|
895
878
|
path = f"{self.endpoint}/api/models-tags-by-type"
|
|
896
|
-
r =
|
|
879
|
+
r = get_session().get(path)
|
|
897
880
|
hf_raise_for_status(r)
|
|
898
881
|
d = r.json()
|
|
899
882
|
return ModelTags(d)
|
|
@@ -903,7 +886,7 @@ class HfApi:
|
|
|
903
886
|
Gets all valid dataset tags as a nested namespace object.
|
|
904
887
|
"""
|
|
905
888
|
path = f"{self.endpoint}/api/datasets-tags-by-type"
|
|
906
|
-
r =
|
|
889
|
+
r = get_session().get(path)
|
|
907
890
|
hf_raise_for_status(r)
|
|
908
891
|
d = r.json()
|
|
909
892
|
return DatasetTags(d)
|
|
@@ -936,8 +919,7 @@ class HfApi:
|
|
|
936
919
|
A string which identify the author (user or organization) of the
|
|
937
920
|
returned models
|
|
938
921
|
search (`str`, *optional*):
|
|
939
|
-
A string that will be contained in the returned
|
|
940
|
-
usage:
|
|
922
|
+
A string that will be contained in the returned model ids.
|
|
941
923
|
emissions_thresholds (`Tuple`, *optional*):
|
|
942
924
|
A tuple of two ints or floats representing a minimum and maximum
|
|
943
925
|
carbon footprint to filter the resulting models with in grams.
|
|
@@ -1293,7 +1275,7 @@ class HfApi:
|
|
|
1293
1275
|
`List[MetricInfo]`: a list of [`MetricInfo`] objects which.
|
|
1294
1276
|
"""
|
|
1295
1277
|
path = f"{self.endpoint}/api/metrics"
|
|
1296
|
-
r =
|
|
1278
|
+
r = get_session().get(path)
|
|
1297
1279
|
hf_raise_for_status(r)
|
|
1298
1280
|
d = r.json()
|
|
1299
1281
|
return [MetricInfo(**x) for x in d]
|
|
@@ -1427,7 +1409,7 @@ class HfApi:
|
|
|
1427
1409
|
"""
|
|
1428
1410
|
if repo_type is None:
|
|
1429
1411
|
repo_type = REPO_TYPE_MODEL
|
|
1430
|
-
response =
|
|
1412
|
+
response = get_session().post(
|
|
1431
1413
|
url=f"{self.endpoint}/api/{repo_type}s/{repo_id}/like",
|
|
1432
1414
|
headers=self._build_hf_headers(token=token),
|
|
1433
1415
|
)
|
|
@@ -1475,10 +1457,8 @@ class HfApi:
|
|
|
1475
1457
|
"""
|
|
1476
1458
|
if repo_type is None:
|
|
1477
1459
|
repo_type = REPO_TYPE_MODEL
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
url=f"{self.endpoint}/api/{repo_type}s/{repo_id}/like",
|
|
1481
|
-
headers=self._build_hf_headers(token=token),
|
|
1460
|
+
response = get_session().delete(
|
|
1461
|
+
url=f"{self.endpoint}/api/{repo_type}s/{repo_id}/like", headers=self._build_hf_headers(token=token)
|
|
1482
1462
|
)
|
|
1483
1463
|
hf_raise_for_status(response)
|
|
1484
1464
|
|
|
@@ -1620,7 +1600,7 @@ class HfApi:
|
|
|
1620
1600
|
params["securityStatus"] = True
|
|
1621
1601
|
if files_metadata:
|
|
1622
1602
|
params["blobs"] = True
|
|
1623
|
-
r =
|
|
1603
|
+
r = get_session().get(path, headers=headers, timeout=timeout, params=params)
|
|
1624
1604
|
hf_raise_for_status(r)
|
|
1625
1605
|
d = r.json()
|
|
1626
1606
|
return ModelInfo(**d)
|
|
@@ -1683,7 +1663,7 @@ class HfApi:
|
|
|
1683
1663
|
if files_metadata:
|
|
1684
1664
|
params["blobs"] = True
|
|
1685
1665
|
|
|
1686
|
-
r =
|
|
1666
|
+
r = get_session().get(path, headers=headers, timeout=timeout, params=params)
|
|
1687
1667
|
hf_raise_for_status(r)
|
|
1688
1668
|
d = r.json()
|
|
1689
1669
|
return DatasetInfo(**d)
|
|
@@ -1746,7 +1726,7 @@ class HfApi:
|
|
|
1746
1726
|
if files_metadata:
|
|
1747
1727
|
params["blobs"] = True
|
|
1748
1728
|
|
|
1749
|
-
r =
|
|
1729
|
+
r = get_session().get(path, headers=headers, timeout=timeout, params=params)
|
|
1750
1730
|
hf_raise_for_status(r)
|
|
1751
1731
|
d = r.json()
|
|
1752
1732
|
return SpaceInfo(**d)
|
|
@@ -1819,6 +1799,141 @@ class HfApi:
|
|
|
1819
1799
|
files_metadata=files_metadata,
|
|
1820
1800
|
)
|
|
1821
1801
|
|
|
1802
|
+
@validate_hf_hub_args
|
|
1803
|
+
def list_files_info(
|
|
1804
|
+
self,
|
|
1805
|
+
repo_id: str,
|
|
1806
|
+
paths: Union[List[str], str, None] = None,
|
|
1807
|
+
*,
|
|
1808
|
+
revision: Optional[str] = None,
|
|
1809
|
+
repo_type: Optional[str] = None,
|
|
1810
|
+
token: Optional[Union[bool, str]] = None,
|
|
1811
|
+
) -> Iterable[RepoFile]:
|
|
1812
|
+
"""
|
|
1813
|
+
List files on a repo and get information about them.
|
|
1814
|
+
|
|
1815
|
+
Takes as input a list of paths. Those paths can be either files or folders. Two server endpoints are called:
|
|
1816
|
+
1. POST "/paths-info" to get information about the provided paths. Called once.
|
|
1817
|
+
2. GET "/tree?recursive=True" to paginate over the input folders. Called only if a folder path is provided as
|
|
1818
|
+
input. Will be called multiple times to follow pagination.
|
|
1819
|
+
If no path is provided as input, step 1. is ignored and all files from the repo are listed.
|
|
1820
|
+
|
|
1821
|
+
Args:
|
|
1822
|
+
repo_id (`str`):
|
|
1823
|
+
A namespace (user or an organization) and a repo name separated by a `/`.
|
|
1824
|
+
paths (`Union[List[str], str, None]`, *optional*):
|
|
1825
|
+
The paths to get information about. Paths to files are directly resolved. Paths to folders are resolved
|
|
1826
|
+
recursively which means that information is returned about all files in the folder and its subfolders.
|
|
1827
|
+
If `None`, all files are returned (the default). If a path do not exist, it is ignored without raising
|
|
1828
|
+
an exception.
|
|
1829
|
+
revision (`str`, *optional*):
|
|
1830
|
+
The revision of the repository from which to get the information. Defaults to `"main"` branch.
|
|
1831
|
+
repo_type (`str`, *optional*):
|
|
1832
|
+
The type of the repository from which to get the information (`"model"`, `"dataset"` or `"space"`.
|
|
1833
|
+
Defaults to `"model"`.
|
|
1834
|
+
token (`bool` or `str`, *optional*):
|
|
1835
|
+
A valid authentication token (see https://huggingface.co/settings/token). If `None` or `True` and
|
|
1836
|
+
machine is logged in (through `huggingface-cli login` or [`~huggingface_hub.login`]), token will be
|
|
1837
|
+
retrieved from the cache. If `False`, token is not sent in the request header.
|
|
1838
|
+
|
|
1839
|
+
Returns:
|
|
1840
|
+
`Iterable[RepoFile]`:
|
|
1841
|
+
The information about the files, as an iterable of [`RepoFile`] objects. The order of the files is
|
|
1842
|
+
not guaranteed.
|
|
1843
|
+
|
|
1844
|
+
Raises:
|
|
1845
|
+
[`~utils.RepositoryNotFoundError`]:
|
|
1846
|
+
If repository is not found (error 404): wrong repo_id/repo_type, private but not authenticated or repo
|
|
1847
|
+
does not exist.
|
|
1848
|
+
[`~utils.RevisionNotFoundError`]:
|
|
1849
|
+
If revision is not found (error 404) on the repo.
|
|
1850
|
+
|
|
1851
|
+
Examples:
|
|
1852
|
+
|
|
1853
|
+
Get information about files on a repo.
|
|
1854
|
+
```py
|
|
1855
|
+
>>> from huggingface_hub import list_files_info
|
|
1856
|
+
>>> files_info = list_files_info("lysandre/arxiv-nlp", ["README.md", "config.json"])
|
|
1857
|
+
>>> files_info
|
|
1858
|
+
<generator object HfApi.list_files_info at 0x7f93b848e730>
|
|
1859
|
+
>>> list(files_info)
|
|
1860
|
+
[
|
|
1861
|
+
RepoFile: {"blob_id": "43bd404b159de6fba7c2f4d3264347668d43af25", "lfs": None, "rfilename": "README.md", "size": 391},
|
|
1862
|
+
RepoFile: {"blob_id": "2f9618c3a19b9a61add74f70bfb121335aeef666", "lfs": None, "rfilename": "config.json", "size": 554},
|
|
1863
|
+
]
|
|
1864
|
+
```
|
|
1865
|
+
|
|
1866
|
+
List LFS files from the "vae/" folder in "stabilityai/stable-diffusion-2" repository.
|
|
1867
|
+
|
|
1868
|
+
```py
|
|
1869
|
+
>>> from huggingface_hub import list_files_info
|
|
1870
|
+
>>> [info.rfilename for info in list_files_info("stabilityai/stable-diffusion-2", "vae") if info.lfs is not None]
|
|
1871
|
+
['vae/diffusion_pytorch_model.bin', 'vae/diffusion_pytorch_model.safetensors']
|
|
1872
|
+
```
|
|
1873
|
+
|
|
1874
|
+
List all files on a repo.
|
|
1875
|
+
```py
|
|
1876
|
+
>>> from huggingface_hub import list_files_info
|
|
1877
|
+
>>> [info.rfilename for info in list_files_info("glue", repo_type="dataset")]
|
|
1878
|
+
['.gitattributes', 'README.md', 'dataset_infos.json', 'glue.py']
|
|
1879
|
+
```
|
|
1880
|
+
"""
|
|
1881
|
+
repo_type = repo_type or REPO_TYPE_MODEL
|
|
1882
|
+
revision = quote(revision, safe="") if revision is not None else DEFAULT_REVISION
|
|
1883
|
+
headers = self._build_hf_headers(token=token)
|
|
1884
|
+
|
|
1885
|
+
def _format_as_repo_file(info: Dict) -> RepoFile:
|
|
1886
|
+
# Quick alias very specific to the server return type of /paths-info and /tree endpoints. Let's keep this
|
|
1887
|
+
# logic here.
|
|
1888
|
+
rfilename = info.pop("path")
|
|
1889
|
+
size = info.pop("size")
|
|
1890
|
+
blobId = info.pop("oid")
|
|
1891
|
+
lfs = info.pop("lfs", None)
|
|
1892
|
+
info.pop("type", None) # "file" or "folder" -> not needed in practice since we know it's a file
|
|
1893
|
+
# "lastCommit": behavior might change server-side in the near future (it might become optional)
|
|
1894
|
+
# In the meantime, let's remove it so that users don't expect it
|
|
1895
|
+
# TODO: set it back when https://github.com/huggingface/moon-landing/issues/5993 is settled
|
|
1896
|
+
info.pop("lastCommit", None)
|
|
1897
|
+
if lfs is not None:
|
|
1898
|
+
lfs = BlobLfsInfo(size=lfs["size"], sha256=lfs["oid"], pointer_size=lfs["pointerSize"])
|
|
1899
|
+
return RepoFile(rfilename=rfilename, size=size, blobId=blobId, lfs=lfs, **info)
|
|
1900
|
+
|
|
1901
|
+
folder_paths = []
|
|
1902
|
+
if paths is None:
|
|
1903
|
+
# `paths` is not provided => list all files from the repo
|
|
1904
|
+
folder_paths.append("")
|
|
1905
|
+
elif paths == []:
|
|
1906
|
+
# corner case: server would return a 400 error if `paths` is an empty list. Let's return early.
|
|
1907
|
+
return
|
|
1908
|
+
else:
|
|
1909
|
+
# `paths` is provided => get info about those
|
|
1910
|
+
response = get_session().post(
|
|
1911
|
+
f"{self.endpoint}/api/{repo_type}s/{repo_id}/paths-info/{revision}",
|
|
1912
|
+
data={
|
|
1913
|
+
"paths": paths if isinstance(paths, list) else [paths],
|
|
1914
|
+
# "expand": True, # TODO: related to "lastCommit" (see above). Do not return it for now.
|
|
1915
|
+
},
|
|
1916
|
+
headers=headers,
|
|
1917
|
+
)
|
|
1918
|
+
hf_raise_for_status(response)
|
|
1919
|
+
paths_info = response.json()
|
|
1920
|
+
|
|
1921
|
+
# List top-level files first
|
|
1922
|
+
for path_info in paths_info:
|
|
1923
|
+
if path_info["type"] == "file":
|
|
1924
|
+
yield _format_as_repo_file(path_info)
|
|
1925
|
+
else:
|
|
1926
|
+
folder_paths.append(path_info["path"])
|
|
1927
|
+
|
|
1928
|
+
# List files in subdirectories
|
|
1929
|
+
for path in folder_paths:
|
|
1930
|
+
encoded_path = "/" + quote(path, safe="") if path else ""
|
|
1931
|
+
tree_url = f"{self.endpoint}/api/{repo_type}s/{repo_id}/tree/{revision}{encoded_path}"
|
|
1932
|
+
for subpath_info in paginate(path=tree_url, headers=headers, params={"recursive": True}):
|
|
1933
|
+
if subpath_info["type"] == "file":
|
|
1934
|
+
yield _format_as_repo_file(subpath_info)
|
|
1935
|
+
|
|
1936
|
+
@_deprecate_arguments(version="0.17", deprecated_args=["timeout"], custom_message="timeout is not used anymore.")
|
|
1822
1937
|
@validate_hf_hub_args
|
|
1823
1938
|
def list_repo_files(
|
|
1824
1939
|
self,
|
|
@@ -1834,35 +1949,26 @@ class HfApi:
|
|
|
1834
1949
|
|
|
1835
1950
|
Args:
|
|
1836
1951
|
repo_id (`str`):
|
|
1837
|
-
A namespace (user or an organization) and a repo name separated
|
|
1838
|
-
by a `/`.
|
|
1952
|
+
A namespace (user or an organization) and a repo name separated by a `/`.
|
|
1839
1953
|
revision (`str`, *optional*):
|
|
1840
|
-
The revision of the model repository from which to get the
|
|
1841
|
-
information.
|
|
1954
|
+
The revision of the model repository from which to get the information.
|
|
1842
1955
|
repo_type (`str`, *optional*):
|
|
1843
|
-
Set to `"dataset"` or `"space"` if uploading to a dataset or
|
|
1844
|
-
|
|
1845
|
-
`None`.
|
|
1846
|
-
timeout (`float`, *optional*):
|
|
1847
|
-
Whether to set a timeout for the request to the Hub.
|
|
1956
|
+
Set to `"dataset"` or `"space"` if uploading to a dataset or space, `None` or `"model"` if uploading to
|
|
1957
|
+
a model. Default is `None`.
|
|
1848
1958
|
token (`bool` or `str`, *optional*):
|
|
1849
|
-
A valid authentication token (see https://huggingface.co/settings/token).
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
If `False`, token is not sent in the request header.
|
|
1959
|
+
A valid authentication token (see https://huggingface.co/settings/token). If `None` or `True` and
|
|
1960
|
+
machine is logged in (through `huggingface-cli login` or [`~huggingface_hub.login`]), token will be
|
|
1961
|
+
retrieved from the cache. If `False`, token is not sent in the request header.
|
|
1853
1962
|
|
|
1854
1963
|
Returns:
|
|
1855
1964
|
`List[str]`: the list of files in a given repository.
|
|
1856
1965
|
"""
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
timeout=timeout,
|
|
1864
|
-
)
|
|
1865
|
-
return [f.rfilename for f in repo_info.siblings]
|
|
1966
|
+
return [
|
|
1967
|
+
f.rfilename
|
|
1968
|
+
for f in self.list_files_info(
|
|
1969
|
+
repo_id=repo_id, paths=None, revision=revision, repo_type=repo_type, token=token
|
|
1970
|
+
)
|
|
1971
|
+
]
|
|
1866
1972
|
|
|
1867
1973
|
@validate_hf_hub_args
|
|
1868
1974
|
def list_repo_refs(
|
|
@@ -1913,9 +2019,8 @@ class HfApi:
|
|
|
1913
2019
|
repo on the Hub.
|
|
1914
2020
|
"""
|
|
1915
2021
|
repo_type = repo_type or REPO_TYPE_MODEL
|
|
1916
|
-
response =
|
|
1917
|
-
f"{self.endpoint}/api/{repo_type}s/{repo_id}/refs",
|
|
1918
|
-
headers=self._build_hf_headers(token=token),
|
|
2022
|
+
response = get_session().get(
|
|
2023
|
+
f"{self.endpoint}/api/{repo_type}s/{repo_id}/refs", headers=self._build_hf_headers(token=token)
|
|
1919
2024
|
)
|
|
1920
2025
|
hf_raise_for_status(response)
|
|
1921
2026
|
data = response.json()
|
|
@@ -2074,7 +2179,7 @@ class HfApi:
|
|
|
2074
2179
|
# See https://github.com/huggingface/huggingface_hub/pull/733/files#r820604472
|
|
2075
2180
|
json["lfsmultipartthresh"] = self._lfsmultipartthresh # type: ignore
|
|
2076
2181
|
headers = self._build_hf_headers(token=token, is_write_action=True)
|
|
2077
|
-
r =
|
|
2182
|
+
r = get_session().post(path, headers=headers, json=json)
|
|
2078
2183
|
|
|
2079
2184
|
try:
|
|
2080
2185
|
hf_raise_for_status(r)
|
|
@@ -2140,7 +2245,7 @@ class HfApi:
|
|
|
2140
2245
|
json["type"] = repo_type
|
|
2141
2246
|
|
|
2142
2247
|
headers = self._build_hf_headers(token=token, is_write_action=True)
|
|
2143
|
-
r =
|
|
2248
|
+
r = get_session().delete(path, headers=headers, json=json)
|
|
2144
2249
|
hf_raise_for_status(r)
|
|
2145
2250
|
|
|
2146
2251
|
@validate_hf_hub_args
|
|
@@ -2195,7 +2300,7 @@ class HfApi:
|
|
|
2195
2300
|
if repo_type is None:
|
|
2196
2301
|
repo_type = REPO_TYPE_MODEL # default repo type
|
|
2197
2302
|
|
|
2198
|
-
r =
|
|
2303
|
+
r = get_session().put(
|
|
2199
2304
|
url=f"{self.endpoint}/api/{repo_type}s/{namespace}/{name}/settings",
|
|
2200
2305
|
headers=self._build_hf_headers(token=token, is_write_action=True),
|
|
2201
2306
|
json={"private": private},
|
|
@@ -2255,7 +2360,7 @@ class HfApi:
|
|
|
2255
2360
|
|
|
2256
2361
|
path = f"{self.endpoint}/api/repos/move"
|
|
2257
2362
|
headers = self._build_hf_headers(token=token, is_write_action=True)
|
|
2258
|
-
r =
|
|
2363
|
+
r = get_session().post(path, headers=headers, json=json)
|
|
2259
2364
|
try:
|
|
2260
2365
|
hf_raise_for_status(r)
|
|
2261
2366
|
except HfHubHTTPError as e:
|
|
@@ -2439,7 +2544,7 @@ class HfApi:
|
|
|
2439
2544
|
params = {"create_pr": "1"} if create_pr else None
|
|
2440
2545
|
|
|
2441
2546
|
try:
|
|
2442
|
-
commit_resp =
|
|
2547
|
+
commit_resp = get_session().post(url=commit_url, headers=headers, data=data, params=params)
|
|
2443
2548
|
hf_raise_for_status(commit_resp, endpoint_name="commit")
|
|
2444
2549
|
except RepositoryNotFoundError as e:
|
|
2445
2550
|
e.append_to_message(_CREATE_COMMIT_NO_REPO_ERROR_MESSAGE)
|
|
@@ -2461,6 +2566,306 @@ class HfApi:
|
|
|
2461
2566
|
pr_url=commit_data["pullRequestUrl"] if create_pr else None,
|
|
2462
2567
|
)
|
|
2463
2568
|
|
|
2569
|
+
@experimental
|
|
2570
|
+
@validate_hf_hub_args
|
|
2571
|
+
def create_commits_on_pr(
|
|
2572
|
+
self,
|
|
2573
|
+
*,
|
|
2574
|
+
repo_id: str,
|
|
2575
|
+
addition_commits: List[List[CommitOperationAdd]],
|
|
2576
|
+
deletion_commits: List[List[CommitOperationDelete]],
|
|
2577
|
+
commit_message: str,
|
|
2578
|
+
commit_description: Optional[str] = None,
|
|
2579
|
+
token: Optional[str] = None,
|
|
2580
|
+
repo_type: Optional[str] = None,
|
|
2581
|
+
merge_pr: bool = True,
|
|
2582
|
+
num_threads: int = 5, # TODO: use to multithread uploads
|
|
2583
|
+
verbose: bool = False,
|
|
2584
|
+
) -> str:
|
|
2585
|
+
"""Push changes to the Hub in multiple commits.
|
|
2586
|
+
|
|
2587
|
+
Commits are pushed to a draft PR branch. If the upload fails or gets interrupted, it can be resumed. Progress
|
|
2588
|
+
is tracked in the PR description. At the end of the process, the PR is set as open and the title is updated to
|
|
2589
|
+
match the initial commit message. If `merge_pr=True` is passed, the PR is merged automatically.
|
|
2590
|
+
|
|
2591
|
+
All deletion commits are pushed first, followed by the addition commits. The order of the commits is not
|
|
2592
|
+
guaranteed as we might implement parallel commits in the future. Be sure that your are not updating several
|
|
2593
|
+
times the same file.
|
|
2594
|
+
|
|
2595
|
+
<Tip warning={true}>
|
|
2596
|
+
|
|
2597
|
+
`create_commits_on_pr` is experimental. Its API and behavior is subject to change in the future without prior notice.
|
|
2598
|
+
|
|
2599
|
+
</Tip>
|
|
2600
|
+
|
|
2601
|
+
Args:
|
|
2602
|
+
repo_id (`str`):
|
|
2603
|
+
The repository in which the commits will be pushed. Example: `"username/my-cool-model"`.
|
|
2604
|
+
|
|
2605
|
+
addition_commits (`List` of `List` of [`~hf_api.CommitOperationAdd`]):
|
|
2606
|
+
A list containing lists of [`~hf_api.CommitOperationAdd`]. Each sublist will result in a commit on the
|
|
2607
|
+
PR.
|
|
2608
|
+
|
|
2609
|
+
deletion_commits
|
|
2610
|
+
A list containing lists of [`~hf_api.CommitOperationDelete`]. Each sublist will result in a commit on
|
|
2611
|
+
the PR. Deletion commits are pushed before addition commits.
|
|
2612
|
+
|
|
2613
|
+
commit_message (`str`):
|
|
2614
|
+
The summary (first line) of the commit that will be created. Will also be the title of the PR.
|
|
2615
|
+
|
|
2616
|
+
commit_description (`str`, *optional*):
|
|
2617
|
+
The description of the commit that will be created. The description will be added to the PR.
|
|
2618
|
+
|
|
2619
|
+
token (`str`, *optional*):
|
|
2620
|
+
Authentication token, obtained with `HfApi.login` method. Will default to the stored token.
|
|
2621
|
+
|
|
2622
|
+
repo_type (`str`, *optional*):
|
|
2623
|
+
Set to `"dataset"` or `"space"` if uploading to a dataset or space, `None` or `"model"` if uploading to
|
|
2624
|
+
a model. Default is `None`.
|
|
2625
|
+
|
|
2626
|
+
merge_pr (`bool`):
|
|
2627
|
+
If set to `True`, the Pull Request is merged at the end of the process. Defaults to `True`.
|
|
2628
|
+
|
|
2629
|
+
num_threads (`int`, *optional*):
|
|
2630
|
+
Number of concurrent threads for uploading files. Defaults to 5.
|
|
2631
|
+
|
|
2632
|
+
verbose (`bool`):
|
|
2633
|
+
If set to `True`, process will run on verbose mode i.e. print information about the ongoing tasks.
|
|
2634
|
+
Defaults to `False`.
|
|
2635
|
+
|
|
2636
|
+
Returns:
|
|
2637
|
+
`str`: URL to the created PR.
|
|
2638
|
+
|
|
2639
|
+
Example:
|
|
2640
|
+
```python
|
|
2641
|
+
>>> from huggingface_hub import HfApi, plan_multi_commits
|
|
2642
|
+
>>> addition_commits, deletion_commits = plan_multi_commits(
|
|
2643
|
+
... operations=[
|
|
2644
|
+
... CommitOperationAdd(...),
|
|
2645
|
+
... CommitOperationAdd(...),
|
|
2646
|
+
... CommitOperationDelete(...),
|
|
2647
|
+
... CommitOperationDelete(...),
|
|
2648
|
+
... CommitOperationAdd(...),
|
|
2649
|
+
... ],
|
|
2650
|
+
... )
|
|
2651
|
+
>>> HfApi().create_commits_on_pr(
|
|
2652
|
+
... repo_id="my-cool-model",
|
|
2653
|
+
... addition_commits=addition_commits,
|
|
2654
|
+
... deletion_commits=deletion_commits,
|
|
2655
|
+
... (...)
|
|
2656
|
+
... verbose=True,
|
|
2657
|
+
... )
|
|
2658
|
+
```
|
|
2659
|
+
|
|
2660
|
+
Raises:
|
|
2661
|
+
[`MultiCommitException`]:
|
|
2662
|
+
If an unexpected issue occur in the process: empty commits, unexpected commits in a PR, unexpected PR
|
|
2663
|
+
description, etc.
|
|
2664
|
+
|
|
2665
|
+
<Tip warning={true}>
|
|
2666
|
+
|
|
2667
|
+
`create_commits_on_pr` assumes that the repo already exists on the Hub. If you get a Client error 404, please
|
|
2668
|
+
make sure you are authenticated and that `repo_id` and `repo_type` are set correctly. If repo does not exist,
|
|
2669
|
+
create it first using [`~hf_api.create_repo`].
|
|
2670
|
+
|
|
2671
|
+
</Tip>
|
|
2672
|
+
"""
|
|
2673
|
+
logger = logging.get_logger(__name__ + ".create_commits_on_pr")
|
|
2674
|
+
if verbose:
|
|
2675
|
+
logger.setLevel("INFO")
|
|
2676
|
+
|
|
2677
|
+
# 1. Get strategy ID
|
|
2678
|
+
logger.info(
|
|
2679
|
+
f"Will create {len(deletion_commits)} deletion commit(s) and {len(addition_commits)} addition commit(s),"
|
|
2680
|
+
f" totalling {sum(len(ops) for ops in addition_commits+deletion_commits)} atomic operations."
|
|
2681
|
+
)
|
|
2682
|
+
strategy = MultiCommitStrategy(
|
|
2683
|
+
addition_commits=[MultiCommitStep(operations=operations) for operations in addition_commits], # type: ignore
|
|
2684
|
+
deletion_commits=[MultiCommitStep(operations=operations) for operations in deletion_commits], # type: ignore
|
|
2685
|
+
)
|
|
2686
|
+
logger.info(f"Multi-commits strategy with ID {strategy.id}.")
|
|
2687
|
+
|
|
2688
|
+
# 2. Get or create a PR with this strategy ID
|
|
2689
|
+
for discussion in self.get_repo_discussions(repo_id=repo_id, repo_type=repo_type, token=token):
|
|
2690
|
+
# search for a draft PR with strategy ID
|
|
2691
|
+
if discussion.is_pull_request and discussion.status == "draft" and strategy.id in discussion.title:
|
|
2692
|
+
pr = self.get_discussion_details(
|
|
2693
|
+
repo_id=repo_id, discussion_num=discussion.num, repo_type=repo_type, token=token
|
|
2694
|
+
)
|
|
2695
|
+
logger.info(f"PR already exists: {pr.url}. Will resume process where it stopped.")
|
|
2696
|
+
break
|
|
2697
|
+
else:
|
|
2698
|
+
# did not find a PR matching the strategy ID
|
|
2699
|
+
pr = multi_commit_create_pull_request(
|
|
2700
|
+
self,
|
|
2701
|
+
repo_id=repo_id,
|
|
2702
|
+
commit_message=commit_message,
|
|
2703
|
+
commit_description=commit_description,
|
|
2704
|
+
strategy=strategy,
|
|
2705
|
+
token=token,
|
|
2706
|
+
repo_type=repo_type,
|
|
2707
|
+
)
|
|
2708
|
+
logger.info(f"New PR created: {pr.url}")
|
|
2709
|
+
|
|
2710
|
+
# 3. Parse PR description to check consistency with strategy (e.g. same commits are scheduled)
|
|
2711
|
+
for event in pr.events:
|
|
2712
|
+
if isinstance(event, DiscussionComment):
|
|
2713
|
+
pr_comment = event
|
|
2714
|
+
break
|
|
2715
|
+
else:
|
|
2716
|
+
raise MultiCommitException(f"PR #{pr.num} must have at least 1 comment")
|
|
2717
|
+
|
|
2718
|
+
description_commits = multi_commit_parse_pr_description(pr_comment.content)
|
|
2719
|
+
if len(description_commits) != len(strategy.all_steps):
|
|
2720
|
+
raise MultiCommitException(
|
|
2721
|
+
f"Corrupted multi-commit PR #{pr.num}: got {len(description_commits)} steps in"
|
|
2722
|
+
f" description but {len(strategy.all_steps)} in strategy."
|
|
2723
|
+
)
|
|
2724
|
+
for step_id in strategy.all_steps:
|
|
2725
|
+
if step_id not in description_commits:
|
|
2726
|
+
raise MultiCommitException(
|
|
2727
|
+
f"Corrupted multi-commit PR #{pr.num}: expected step {step_id} but didn't find"
|
|
2728
|
+
f" it (have {', '.join(description_commits)})."
|
|
2729
|
+
)
|
|
2730
|
+
|
|
2731
|
+
# 4. Retrieve commit history (and check consistency)
|
|
2732
|
+
commits_on_main_branch = {
|
|
2733
|
+
commit.commit_id
|
|
2734
|
+
for commit in self.list_repo_commits(
|
|
2735
|
+
repo_id=repo_id, repo_type=repo_type, token=token, revision=DEFAULT_REVISION
|
|
2736
|
+
)
|
|
2737
|
+
}
|
|
2738
|
+
pr_commits = [
|
|
2739
|
+
commit
|
|
2740
|
+
for commit in self.list_repo_commits(
|
|
2741
|
+
repo_id=repo_id, repo_type=repo_type, token=token, revision=pr.git_reference
|
|
2742
|
+
)
|
|
2743
|
+
if commit.commit_id not in commits_on_main_branch
|
|
2744
|
+
]
|
|
2745
|
+
if len(pr_commits) > 0:
|
|
2746
|
+
logger.info(f"Found {len(pr_commits)} existing commits on the PR.")
|
|
2747
|
+
|
|
2748
|
+
# At this point `pr_commits` is a list of commits pushed to the PR. We expect all of these commits (if any) to have
|
|
2749
|
+
# a step_id as title. We raise exception if an unexpected commit has been pushed.
|
|
2750
|
+
if len(pr_commits) > len(strategy.all_steps):
|
|
2751
|
+
raise MultiCommitException(
|
|
2752
|
+
f"Corrupted multi-commit PR #{pr.num}: scheduled {len(strategy.all_steps)} steps but"
|
|
2753
|
+
f" {len(pr_commits)} commits have already been pushed to the PR."
|
|
2754
|
+
)
|
|
2755
|
+
|
|
2756
|
+
# Check which steps are already completed
|
|
2757
|
+
remaining_additions = {step.id: step for step in strategy.addition_commits}
|
|
2758
|
+
remaining_deletions = {step.id: step for step in strategy.deletion_commits}
|
|
2759
|
+
for commit in pr_commits:
|
|
2760
|
+
if commit.title in remaining_additions:
|
|
2761
|
+
step = remaining_additions.pop(commit.title)
|
|
2762
|
+
step.completed = True
|
|
2763
|
+
elif commit.title in remaining_deletions:
|
|
2764
|
+
step = remaining_deletions.pop(commit.title)
|
|
2765
|
+
step.completed = True
|
|
2766
|
+
|
|
2767
|
+
if len(remaining_deletions) > 0 and len(remaining_additions) < len(strategy.addition_commits):
|
|
2768
|
+
raise MultiCommitException(
|
|
2769
|
+
f"Corrupted multi-commit PR #{pr.num}: some addition commits have already been pushed to the PR but"
|
|
2770
|
+
" deletion commits are not all completed yet."
|
|
2771
|
+
)
|
|
2772
|
+
nb_remaining = len(remaining_deletions) + len(remaining_additions)
|
|
2773
|
+
if len(pr_commits) > 0:
|
|
2774
|
+
logger.info(
|
|
2775
|
+
f"{nb_remaining} commits remaining ({len(remaining_deletions)} deletion commits and"
|
|
2776
|
+
f" {len(remaining_additions)} addition commits)"
|
|
2777
|
+
)
|
|
2778
|
+
|
|
2779
|
+
# 5. Push remaining commits to the PR + update description
|
|
2780
|
+
# TODO: multi-thread this
|
|
2781
|
+
for step in list(remaining_deletions.values()) + list(remaining_additions.values()):
|
|
2782
|
+
# Push new commit
|
|
2783
|
+
self.create_commit(
|
|
2784
|
+
repo_id=repo_id,
|
|
2785
|
+
repo_type=repo_type,
|
|
2786
|
+
token=token,
|
|
2787
|
+
commit_message=step.id,
|
|
2788
|
+
revision=pr.git_reference,
|
|
2789
|
+
num_threads=num_threads,
|
|
2790
|
+
operations=step.operations,
|
|
2791
|
+
create_pr=False,
|
|
2792
|
+
)
|
|
2793
|
+
step.completed = True
|
|
2794
|
+
nb_remaining -= 1
|
|
2795
|
+
logger.info(f" step {step.id} completed (still {nb_remaining} to go).")
|
|
2796
|
+
|
|
2797
|
+
# Update PR description
|
|
2798
|
+
self.edit_discussion_comment(
|
|
2799
|
+
repo_id=repo_id,
|
|
2800
|
+
repo_type=repo_type,
|
|
2801
|
+
token=token,
|
|
2802
|
+
discussion_num=pr.num,
|
|
2803
|
+
comment_id=pr_comment.id,
|
|
2804
|
+
new_content=multi_commit_generate_comment(
|
|
2805
|
+
commit_message=commit_message, commit_description=commit_description, strategy=strategy
|
|
2806
|
+
),
|
|
2807
|
+
)
|
|
2808
|
+
logger.info("All commits have been pushed.")
|
|
2809
|
+
|
|
2810
|
+
# 6. Update PR (and merge)
|
|
2811
|
+
self.rename_discussion(
|
|
2812
|
+
repo_id=repo_id,
|
|
2813
|
+
repo_type=repo_type,
|
|
2814
|
+
token=token,
|
|
2815
|
+
discussion_num=pr.num,
|
|
2816
|
+
new_title=commit_message,
|
|
2817
|
+
)
|
|
2818
|
+
self.change_discussion_status(
|
|
2819
|
+
repo_id=repo_id,
|
|
2820
|
+
repo_type=repo_type,
|
|
2821
|
+
token=token,
|
|
2822
|
+
discussion_num=pr.num,
|
|
2823
|
+
new_status="open",
|
|
2824
|
+
comment=MULTI_COMMIT_PR_COMPLETION_COMMENT_TEMPLATE,
|
|
2825
|
+
)
|
|
2826
|
+
logger.info("PR is now open for reviews.")
|
|
2827
|
+
|
|
2828
|
+
if merge_pr: # User don't want a PR => merge it
|
|
2829
|
+
try:
|
|
2830
|
+
self.merge_pull_request(
|
|
2831
|
+
repo_id=repo_id,
|
|
2832
|
+
repo_type=repo_type,
|
|
2833
|
+
token=token,
|
|
2834
|
+
discussion_num=pr.num,
|
|
2835
|
+
comment=MULTI_COMMIT_PR_CLOSING_COMMENT_TEMPLATE,
|
|
2836
|
+
)
|
|
2837
|
+
logger.info("PR has been automatically merged (`merge_pr=True` was passed).")
|
|
2838
|
+
except BadRequestError as error:
|
|
2839
|
+
if error.server_message is not None and "no associated changes" in error.server_message:
|
|
2840
|
+
# PR cannot be merged as no changes are associated. We close the PR without merging with a comment to
|
|
2841
|
+
# explain.
|
|
2842
|
+
self.change_discussion_status(
|
|
2843
|
+
repo_id=repo_id,
|
|
2844
|
+
repo_type=repo_type,
|
|
2845
|
+
token=token,
|
|
2846
|
+
discussion_num=pr.num,
|
|
2847
|
+
comment=MULTI_COMMIT_PR_CLOSE_COMMENT_FAILURE_NO_CHANGES_TEMPLATE,
|
|
2848
|
+
new_status="closed",
|
|
2849
|
+
)
|
|
2850
|
+
logger.warning("Couldn't merge the PR: no associated changes.")
|
|
2851
|
+
else:
|
|
2852
|
+
# PR cannot be merged for another reason (conflicting files for example). We comment the PR to explain
|
|
2853
|
+
# and re-raise the exception.
|
|
2854
|
+
self.comment_discussion(
|
|
2855
|
+
repo_id=repo_id,
|
|
2856
|
+
repo_type=repo_type,
|
|
2857
|
+
token=token,
|
|
2858
|
+
discussion_num=pr.num,
|
|
2859
|
+
comment=MULTI_COMMIT_PR_CLOSE_COMMENT_FAILURE_BAD_REQUEST_TEMPLATE.format(
|
|
2860
|
+
error_message=error.server_message
|
|
2861
|
+
),
|
|
2862
|
+
)
|
|
2863
|
+
raise MultiCommitException(
|
|
2864
|
+
f"Couldn't merge Pull Request in multi-commit: {error.server_message}"
|
|
2865
|
+
) from error
|
|
2866
|
+
|
|
2867
|
+
return pr.url
|
|
2868
|
+
|
|
2464
2869
|
@validate_hf_hub_args
|
|
2465
2870
|
def upload_file(
|
|
2466
2871
|
self,
|
|
@@ -2627,9 +3032,11 @@ class HfApi:
|
|
|
2627
3032
|
allow_patterns: Optional[Union[List[str], str]] = None,
|
|
2628
3033
|
ignore_patterns: Optional[Union[List[str], str]] = None,
|
|
2629
3034
|
delete_patterns: Optional[Union[List[str], str]] = None,
|
|
3035
|
+
multi_commits: bool = False,
|
|
3036
|
+
multi_commits_verbose: bool = False,
|
|
2630
3037
|
):
|
|
2631
3038
|
"""
|
|
2632
|
-
Upload a local folder to the given repo. The upload is done through a HTTP
|
|
3039
|
+
Upload a local folder to the given repo. The upload is done through a HTTP requests, and doesn't require git or
|
|
2633
3040
|
git-lfs to be installed.
|
|
2634
3041
|
|
|
2635
3042
|
The structure of the folder will be preserved. Files with the same name already present in the repository will
|
|
@@ -2643,9 +3050,12 @@ class HfApi:
|
|
|
2643
3050
|
Use the `delete_patterns` argument to specify remote files you want to delete. Input type is the same as for
|
|
2644
3051
|
`allow_patterns` (see above). If `path_in_repo` is also provided, the patterns are matched against paths
|
|
2645
3052
|
relative to this folder. For example, `upload_folder(..., path_in_repo="experiment", delete_patterns="logs/*")`
|
|
2646
|
-
will delete any remote file under
|
|
3053
|
+
will delete any remote file under `./experiment/logs/`. Note that the `.gitattributes` file will not be deleted
|
|
2647
3054
|
even if it matches the patterns.
|
|
2648
3055
|
|
|
3056
|
+
Any `.git/` folder present in any subdirectory will be ignored. However, please be aware that the `.gitignore`
|
|
3057
|
+
file is not taken into account.
|
|
3058
|
+
|
|
2649
3059
|
Uses `HfApi.create_commit` under the hood.
|
|
2650
3060
|
|
|
2651
3061
|
Args:
|
|
@@ -2672,11 +3082,11 @@ class HfApi:
|
|
|
2672
3082
|
commit_description (`str` *optional*):
|
|
2673
3083
|
The description of the generated commit
|
|
2674
3084
|
create_pr (`boolean`, *optional*):
|
|
2675
|
-
Whether or not to create a Pull Request with that commit. Defaults to `False`.
|
|
2676
|
-
|
|
2677
|
-
`revision` is set and is a branch
|
|
2678
|
-
`
|
|
2679
|
-
|
|
3085
|
+
Whether or not to create a Pull Request with that commit. Defaults to `False`. If `revision` is not
|
|
3086
|
+
set, PR is opened against the `"main"` branch. If `revision` is set and is a branch, PR is opened
|
|
3087
|
+
against this branch. If `revision` is set and is not a branch name (example: a commit oid), an
|
|
3088
|
+
`RevisionNotFoundError` is returned by the server. If both `multi_commits` and `create_pr` are True,
|
|
3089
|
+
the PR created in the multi-commit process is kept opened.
|
|
2680
3090
|
parent_commit (`str`, *optional*):
|
|
2681
3091
|
The OID / SHA of the parent commit, as a hexadecimal string. Shorthands (7 first characters) are also supported.
|
|
2682
3092
|
If specified and `create_pr` is `False`, the commit will fail if `revision` does not point to `parent_commit`.
|
|
@@ -2691,6 +3101,10 @@ class HfApi:
|
|
|
2691
3101
|
If provided, remote files matching any of the patterns will be deleted from the repo while committing
|
|
2692
3102
|
new files. This is useful if you don't know which files have already been uploaded.
|
|
2693
3103
|
Note: to avoid discrepancies the `.gitattributes` file is not deleted even if it matches the pattern.
|
|
3104
|
+
multi_commits (`bool`):
|
|
3105
|
+
If True, changes are pushed to a PR using a multi-commit process. Defaults to `False`.
|
|
3106
|
+
multi_commits_verbose (`bool`):
|
|
3107
|
+
If True and `multi_commits` is used, more information will be displayed to the user.
|
|
2694
3108
|
|
|
2695
3109
|
Returns:
|
|
2696
3110
|
`str`: A URL to visualize the uploaded folder on the hub
|
|
@@ -2714,6 +3128,12 @@ class HfApi:
|
|
|
2714
3128
|
|
|
2715
3129
|
</Tip>
|
|
2716
3130
|
|
|
3131
|
+
<Tip warning={true}>
|
|
3132
|
+
|
|
3133
|
+
`multi_commits` is experimental. Its API and behavior is subject to change in the future without prior notice.
|
|
3134
|
+
|
|
3135
|
+
</Tip>
|
|
3136
|
+
|
|
2717
3137
|
Example:
|
|
2718
3138
|
|
|
2719
3139
|
```python
|
|
@@ -2749,20 +3169,27 @@ class HfApi:
|
|
|
2749
3169
|
... token="my_token",
|
|
2750
3170
|
... create_pr=True,
|
|
2751
3171
|
... )
|
|
2752
|
-
|
|
3172
|
+
"https://huggingface.co/datasets/username/my-dataset/tree/refs%2Fpr%2F1/remote/experiment/checkpoints"
|
|
2753
3173
|
|
|
2754
3174
|
```
|
|
2755
3175
|
"""
|
|
2756
3176
|
if repo_type not in REPO_TYPES:
|
|
2757
3177
|
raise ValueError(f"Invalid repo type, must be one of {REPO_TYPES}")
|
|
2758
3178
|
|
|
3179
|
+
if multi_commits:
|
|
3180
|
+
if revision is not None and revision != DEFAULT_REVISION:
|
|
3181
|
+
raise ValueError("Cannot use `multi_commit` to commit changes other than the main branch.")
|
|
3182
|
+
|
|
2759
3183
|
# By default, upload folder to the root directory in repo.
|
|
2760
3184
|
if path_in_repo is None:
|
|
2761
3185
|
path_in_repo = ""
|
|
2762
3186
|
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
3187
|
+
# Do not upload .git folder
|
|
3188
|
+
if ignore_patterns is None:
|
|
3189
|
+
ignore_patterns = []
|
|
3190
|
+
elif isinstance(ignore_patterns, str):
|
|
3191
|
+
ignore_patterns = [ignore_patterns]
|
|
3192
|
+
ignore_patterns += IGNORE_GIT_FOLDER_PATTERNS
|
|
2766
3193
|
|
|
2767
3194
|
delete_operations = self._prepare_upload_folder_deletions(
|
|
2768
3195
|
repo_id=repo_id,
|
|
@@ -2787,20 +3214,37 @@ class HfApi:
|
|
|
2787
3214
|
]
|
|
2788
3215
|
commit_operations = delete_operations + add_operations
|
|
2789
3216
|
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
operations=commit_operations
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
3217
|
+
pr_url: Optional[str]
|
|
3218
|
+
commit_message = commit_message or "Upload folder using huggingface_hub"
|
|
3219
|
+
if multi_commits:
|
|
3220
|
+
addition_commits, deletion_commits = plan_multi_commits(operations=commit_operations)
|
|
3221
|
+
pr_url = self.create_commits_on_pr(
|
|
3222
|
+
repo_id=repo_id,
|
|
3223
|
+
repo_type=repo_type,
|
|
3224
|
+
addition_commits=addition_commits,
|
|
3225
|
+
deletion_commits=deletion_commits,
|
|
3226
|
+
commit_message=commit_message,
|
|
3227
|
+
commit_description=commit_description,
|
|
3228
|
+
token=token,
|
|
3229
|
+
merge_pr=not create_pr,
|
|
3230
|
+
verbose=multi_commits_verbose,
|
|
3231
|
+
)
|
|
3232
|
+
else:
|
|
3233
|
+
commit_info = self.create_commit(
|
|
3234
|
+
repo_type=repo_type,
|
|
3235
|
+
repo_id=repo_id,
|
|
3236
|
+
operations=commit_operations,
|
|
3237
|
+
commit_message=commit_message,
|
|
3238
|
+
commit_description=commit_description,
|
|
3239
|
+
token=token,
|
|
3240
|
+
revision=revision,
|
|
3241
|
+
create_pr=create_pr,
|
|
3242
|
+
parent_commit=parent_commit,
|
|
3243
|
+
)
|
|
3244
|
+
pr_url = commit_info.pr_url
|
|
2801
3245
|
|
|
2802
|
-
if
|
|
2803
|
-
revision = quote(_parse_revision_from_pr_url(
|
|
3246
|
+
if create_pr and pr_url is not None:
|
|
3247
|
+
revision = quote(_parse_revision_from_pr_url(pr_url), safe="")
|
|
2804
3248
|
if repo_type in REPO_TYPES_URL_PREFIXES:
|
|
2805
3249
|
repo_id = REPO_TYPES_URL_PREFIXES[repo_type] + repo_id
|
|
2806
3250
|
revision = revision if revision is not None else DEFAULT_REVISION
|
|
@@ -3020,7 +3464,7 @@ class HfApi:
|
|
|
3020
3464
|
payload["startingPoint"] = revision
|
|
3021
3465
|
|
|
3022
3466
|
# Create branch
|
|
3023
|
-
response =
|
|
3467
|
+
response = get_session().post(url=branch_url, headers=headers, json=payload)
|
|
3024
3468
|
try:
|
|
3025
3469
|
hf_raise_for_status(response)
|
|
3026
3470
|
except HfHubHTTPError as e:
|
|
@@ -3073,7 +3517,7 @@ class HfApi:
|
|
|
3073
3517
|
headers = self._build_hf_headers(token=token, is_write_action=True)
|
|
3074
3518
|
|
|
3075
3519
|
# Delete branch
|
|
3076
|
-
response =
|
|
3520
|
+
response = get_session().delete(url=branch_url, headers=headers)
|
|
3077
3521
|
hf_raise_for_status(response)
|
|
3078
3522
|
|
|
3079
3523
|
@validate_hf_hub_args
|
|
@@ -3140,7 +3584,7 @@ class HfApi:
|
|
|
3140
3584
|
payload["message"] = tag_message
|
|
3141
3585
|
|
|
3142
3586
|
# Tag
|
|
3143
|
-
response =
|
|
3587
|
+
response = get_session().post(url=tag_url, headers=headers, json=payload)
|
|
3144
3588
|
try:
|
|
3145
3589
|
hf_raise_for_status(response)
|
|
3146
3590
|
except HfHubHTTPError as e:
|
|
@@ -3190,7 +3634,7 @@ class HfApi:
|
|
|
3190
3634
|
headers = self._build_hf_headers(token=token, is_write_action=True)
|
|
3191
3635
|
|
|
3192
3636
|
# Un-tag
|
|
3193
|
-
response =
|
|
3637
|
+
response = get_session().delete(url=tag_url, headers=headers)
|
|
3194
3638
|
hf_raise_for_status(response)
|
|
3195
3639
|
|
|
3196
3640
|
@validate_hf_hub_args
|
|
@@ -3281,7 +3725,7 @@ class HfApi:
|
|
|
3281
3725
|
|
|
3282
3726
|
def _fetch_discussion_page(page_index: int):
|
|
3283
3727
|
path = f"{self.endpoint}/api/{repo_type}s/{repo_id}/discussions?p={page_index}"
|
|
3284
|
-
resp =
|
|
3728
|
+
resp = get_session().get(path, headers=headers)
|
|
3285
3729
|
hf_raise_for_status(resp)
|
|
3286
3730
|
paginated_discussions = resp.json()
|
|
3287
3731
|
total = paginated_discussions["count"]
|
|
@@ -3304,6 +3748,7 @@ class HfApi:
|
|
|
3304
3748
|
repo_id=discussion["repo"]["name"],
|
|
3305
3749
|
repo_type=discussion["repo"]["type"],
|
|
3306
3750
|
is_pull_request=discussion["isPullRequest"],
|
|
3751
|
+
endpoint=self.endpoint,
|
|
3307
3752
|
)
|
|
3308
3753
|
page_index = page_index + 1
|
|
3309
3754
|
|
|
@@ -3356,7 +3801,7 @@ class HfApi:
|
|
|
3356
3801
|
|
|
3357
3802
|
path = f"{self.endpoint}/api/{repo_type}s/{repo_id}/discussions/{discussion_num}"
|
|
3358
3803
|
headers = self._build_hf_headers(token=token)
|
|
3359
|
-
resp =
|
|
3804
|
+
resp = get_session().get(path, params={"diff": "1"}, headers=headers)
|
|
3360
3805
|
hf_raise_for_status(resp)
|
|
3361
3806
|
|
|
3362
3807
|
discussion_details = resp.json()
|
|
@@ -3380,6 +3825,7 @@ class HfApi:
|
|
|
3380
3825
|
target_branch=target_branch,
|
|
3381
3826
|
merge_commit_oid=merge_commit_oid,
|
|
3382
3827
|
diff=discussion_details.get("diff"),
|
|
3828
|
+
endpoint=self.endpoint,
|
|
3383
3829
|
)
|
|
3384
3830
|
|
|
3385
3831
|
@validate_hf_hub_args
|
|
@@ -3453,7 +3899,7 @@ class HfApi:
|
|
|
3453
3899
|
)
|
|
3454
3900
|
|
|
3455
3901
|
headers = self._build_hf_headers(token=token, is_write_action=True)
|
|
3456
|
-
resp =
|
|
3902
|
+
resp = get_session().post(
|
|
3457
3903
|
f"{self.endpoint}/api/{repo_type}s/{repo_id}/discussions",
|
|
3458
3904
|
json={
|
|
3459
3905
|
"title": title.strip(),
|
|
@@ -3961,7 +4407,7 @@ class HfApi:
|
|
|
3961
4407
|
token (`str`, *optional*):
|
|
3962
4408
|
Hugging Face token. Will default to the locally saved token if not provided.
|
|
3963
4409
|
"""
|
|
3964
|
-
r =
|
|
4410
|
+
r = get_session().post(
|
|
3965
4411
|
f"{self.endpoint}/api/spaces/{repo_id}/secrets",
|
|
3966
4412
|
headers=self._build_hf_headers(token=token),
|
|
3967
4413
|
json={"key": key, "value": value},
|
|
@@ -3983,7 +4429,7 @@ class HfApi:
|
|
|
3983
4429
|
token (`str`, *optional*):
|
|
3984
4430
|
Hugging Face token. Will default to the locally saved token if not provided.
|
|
3985
4431
|
"""
|
|
3986
|
-
r =
|
|
4432
|
+
r = get_session().delete(
|
|
3987
4433
|
f"{self.endpoint}/api/spaces/{repo_id}/secrets",
|
|
3988
4434
|
headers=self._build_hf_headers(token=token),
|
|
3989
4435
|
json={"key": key},
|
|
@@ -4003,12 +4449,21 @@ class HfApi:
|
|
|
4003
4449
|
Returns:
|
|
4004
4450
|
[`SpaceRuntime`]: Runtime information about a Space including Space stage and hardware.
|
|
4005
4451
|
"""
|
|
4006
|
-
r =
|
|
4452
|
+
r = get_session().get(
|
|
4453
|
+
f"{self.endpoint}/api/spaces/{repo_id}/runtime", headers=self._build_hf_headers(token=token)
|
|
4454
|
+
)
|
|
4007
4455
|
hf_raise_for_status(r)
|
|
4008
4456
|
return SpaceRuntime(r.json())
|
|
4009
4457
|
|
|
4010
4458
|
@validate_hf_hub_args
|
|
4011
|
-
def request_space_hardware(
|
|
4459
|
+
def request_space_hardware(
|
|
4460
|
+
self,
|
|
4461
|
+
repo_id: str,
|
|
4462
|
+
hardware: SpaceHardware,
|
|
4463
|
+
*,
|
|
4464
|
+
token: Optional[str] = None,
|
|
4465
|
+
sleep_time: Optional[int] = None,
|
|
4466
|
+
) -> SpaceRuntime:
|
|
4012
4467
|
"""Request new hardware for a Space.
|
|
4013
4468
|
|
|
4014
4469
|
Args:
|
|
@@ -4018,6 +4473,13 @@ class HfApi:
|
|
|
4018
4473
|
Hardware on which to run the Space. Example: `"t4-medium"`.
|
|
4019
4474
|
token (`str`, *optional*):
|
|
4020
4475
|
Hugging Face token. Will default to the locally saved token if not provided.
|
|
4476
|
+
sleep_time (`int`, *optional*):
|
|
4477
|
+
Number of seconds of inactivity to wait before a Space is put to sleep. Set to `-1` if you don't want
|
|
4478
|
+
your Space to sleep (default behavior for upgraded hardware). For free hardware, you can't configure
|
|
4479
|
+
the sleep time (value is fixed to 48 hours of inactivity).
|
|
4480
|
+
See https://huggingface.co/docs/hub/spaces-gpus#sleep-time for more details.
|
|
4481
|
+
Returns:
|
|
4482
|
+
[`SpaceRuntime`]: Runtime information about a Space including Space stage and hardware.
|
|
4021
4483
|
|
|
4022
4484
|
<Tip>
|
|
4023
4485
|
|
|
@@ -4025,19 +4487,80 @@ class HfApi:
|
|
|
4025
4487
|
|
|
4026
4488
|
</Tip>
|
|
4027
4489
|
"""
|
|
4028
|
-
|
|
4490
|
+
if sleep_time is not None and hardware == SpaceHardware.CPU_BASIC:
|
|
4491
|
+
warnings.warn(
|
|
4492
|
+
(
|
|
4493
|
+
"If your Space runs on the default 'cpu-basic' hardware, it will go to sleep if inactive for more"
|
|
4494
|
+
" than 48 hours. This value is not configurable. If you don't want your Space to deactivate or if"
|
|
4495
|
+
" you want to set a custom sleep time, you need to upgrade to a paid Hardware."
|
|
4496
|
+
),
|
|
4497
|
+
UserWarning,
|
|
4498
|
+
)
|
|
4499
|
+
payload: Dict[str, Any] = {"flavor": hardware}
|
|
4500
|
+
if sleep_time is not None:
|
|
4501
|
+
payload["sleepTimeSeconds"] = sleep_time
|
|
4502
|
+
r = get_session().post(
|
|
4029
4503
|
f"{self.endpoint}/api/spaces/{repo_id}/hardware",
|
|
4030
4504
|
headers=self._build_hf_headers(token=token),
|
|
4031
|
-
json=
|
|
4505
|
+
json=payload,
|
|
4506
|
+
)
|
|
4507
|
+
hf_raise_for_status(r)
|
|
4508
|
+
return SpaceRuntime(r.json())
|
|
4509
|
+
|
|
4510
|
+
@validate_hf_hub_args
|
|
4511
|
+
def set_space_sleep_time(self, repo_id: str, sleep_time: int, *, token: Optional[str] = None) -> SpaceRuntime:
|
|
4512
|
+
"""Set a custom sleep time for a Space running on upgraded hardware..
|
|
4513
|
+
|
|
4514
|
+
Your Space will go to sleep after X seconds of inactivity. You are not billed when your Space is in "sleep"
|
|
4515
|
+
mode. If a new visitor lands on your Space, it will "wake it up". Only upgraded hardware can have a
|
|
4516
|
+
configurable sleep time. To know more about the sleep stage, please refer to
|
|
4517
|
+
https://huggingface.co/docs/hub/spaces-gpus#sleep-time.
|
|
4518
|
+
|
|
4519
|
+
Args:
|
|
4520
|
+
repo_id (`str`):
|
|
4521
|
+
ID of the repo to update. Example: `"bigcode/in-the-stack"`.
|
|
4522
|
+
sleep_time (`int`, *optional*):
|
|
4523
|
+
Number of seconds of inactivity to wait before a Space is put to sleep. Set to `-1` if you don't want
|
|
4524
|
+
your Space to pause (default behavior for upgraded hardware). For free hardware, you can't configure
|
|
4525
|
+
the sleep time (value is fixed to 48 hours of inactivity).
|
|
4526
|
+
See https://huggingface.co/docs/hub/spaces-gpus#sleep-time for more details.
|
|
4527
|
+
token (`str`, *optional*):
|
|
4528
|
+
Hugging Face token. Will default to the locally saved token if not provided.
|
|
4529
|
+
Returns:
|
|
4530
|
+
[`SpaceRuntime`]: Runtime information about a Space including Space stage and hardware.
|
|
4531
|
+
|
|
4532
|
+
<Tip>
|
|
4533
|
+
|
|
4534
|
+
It is also possible to set a custom sleep time when requesting hardware with [`request_space_hardware`].
|
|
4535
|
+
|
|
4536
|
+
</Tip>
|
|
4537
|
+
"""
|
|
4538
|
+
r = get_session().post(
|
|
4539
|
+
f"{self.endpoint}/api/spaces/{repo_id}/sleeptime",
|
|
4540
|
+
headers=self._build_hf_headers(token=token),
|
|
4541
|
+
json={"seconds": sleep_time},
|
|
4032
4542
|
)
|
|
4033
4543
|
hf_raise_for_status(r)
|
|
4544
|
+
runtime = SpaceRuntime(r.json())
|
|
4545
|
+
|
|
4546
|
+
hardware = runtime.requested_hardware or runtime.hardware
|
|
4547
|
+
if hardware == SpaceHardware.CPU_BASIC:
|
|
4548
|
+
warnings.warn(
|
|
4549
|
+
(
|
|
4550
|
+
"If your Space runs on the default 'cpu-basic' hardware, it will go to sleep if inactive for more"
|
|
4551
|
+
" than 48 hours. This value is not configurable. If you don't want your Space to deactivate or if"
|
|
4552
|
+
" you want to set a custom sleep time, you need to upgrade to a paid Hardware."
|
|
4553
|
+
),
|
|
4554
|
+
UserWarning,
|
|
4555
|
+
)
|
|
4556
|
+
return runtime
|
|
4034
4557
|
|
|
4035
4558
|
@validate_hf_hub_args
|
|
4036
4559
|
def pause_space(self, repo_id: str, *, token: Optional[str] = None) -> SpaceRuntime:
|
|
4037
4560
|
"""Pause your Space.
|
|
4038
4561
|
|
|
4039
4562
|
A paused Space stops executing until manually restarted by its owner. This is different from the sleeping
|
|
4040
|
-
state in which free Spaces go after
|
|
4563
|
+
state in which free Spaces go after 48h of inactivity. Paused time is not billed to your account, no matter the
|
|
4041
4564
|
hardware you've selected. To restart your Space, use [`restart_space`] and go to your Space settings page.
|
|
4042
4565
|
|
|
4043
4566
|
For more details, please visit [the docs](https://huggingface.co/docs/hub/spaces-gpus#pause).
|
|
@@ -4062,7 +4585,9 @@ class HfApi:
|
|
|
4062
4585
|
If your Space is a static Space. Static Spaces are always running and never billed. If you want to hide
|
|
4063
4586
|
a static Space, you can set it to private.
|
|
4064
4587
|
"""
|
|
4065
|
-
r =
|
|
4588
|
+
r = get_session().post(
|
|
4589
|
+
f"{self.endpoint}/api/spaces/{repo_id}/pause", headers=self._build_hf_headers(token=token)
|
|
4590
|
+
)
|
|
4066
4591
|
hf_raise_for_status(r)
|
|
4067
4592
|
return SpaceRuntime(r.json())
|
|
4068
4593
|
|
|
@@ -4096,7 +4621,9 @@ class HfApi:
|
|
|
4096
4621
|
If your Space is a static Space. Static Spaces are always running and never billed. If you want to hide
|
|
4097
4622
|
a static Space, you can set it to private.
|
|
4098
4623
|
"""
|
|
4099
|
-
r =
|
|
4624
|
+
r = get_session().post(
|
|
4625
|
+
f"{self.endpoint}/api/spaces/{repo_id}/restart", headers=self._build_hf_headers(token=token)
|
|
4626
|
+
)
|
|
4100
4627
|
hf_raise_for_status(r)
|
|
4101
4628
|
return SpaceRuntime(r.json())
|
|
4102
4629
|
|
|
@@ -4109,7 +4636,7 @@ class HfApi:
|
|
|
4109
4636
|
private: Optional[bool] = None,
|
|
4110
4637
|
token: Optional[str] = None,
|
|
4111
4638
|
exist_ok: bool = False,
|
|
4112
|
-
) ->
|
|
4639
|
+
) -> RepoUrl:
|
|
4113
4640
|
"""Duplicate a Space.
|
|
4114
4641
|
|
|
4115
4642
|
Programmatically duplicate a Space. The new Space will be created in your account and will be in the same state
|
|
@@ -4170,7 +4697,7 @@ class HfApi:
|
|
|
4170
4697
|
if private is not None:
|
|
4171
4698
|
payload["private"] = private
|
|
4172
4699
|
|
|
4173
|
-
r =
|
|
4700
|
+
r = get_session().post(
|
|
4174
4701
|
f"{self.endpoint}/api/spaces/{from_id}/duplicate",
|
|
4175
4702
|
headers=self._build_hf_headers(token=token, is_write_action=True),
|
|
4176
4703
|
json=payload,
|
|
@@ -4304,9 +4831,6 @@ def _parse_revision_from_pr_url(pr_url: str) -> str:
|
|
|
4304
4831
|
|
|
4305
4832
|
api = HfApi()
|
|
4306
4833
|
|
|
4307
|
-
set_access_token = api.set_access_token
|
|
4308
|
-
unset_access_token = api.unset_access_token
|
|
4309
|
-
|
|
4310
4834
|
whoami = api.whoami
|
|
4311
4835
|
|
|
4312
4836
|
list_models = api.list_models
|
|
@@ -4322,6 +4846,7 @@ repo_info = api.repo_info
|
|
|
4322
4846
|
list_repo_files = api.list_repo_files
|
|
4323
4847
|
list_repo_refs = api.list_repo_refs
|
|
4324
4848
|
list_repo_commits = api.list_repo_commits
|
|
4849
|
+
list_files_info = api.list_files_info
|
|
4325
4850
|
|
|
4326
4851
|
list_metrics = api.list_metrics
|
|
4327
4852
|
|
|
@@ -4337,6 +4862,7 @@ upload_file = api.upload_file
|
|
|
4337
4862
|
upload_folder = api.upload_folder
|
|
4338
4863
|
delete_file = api.delete_file
|
|
4339
4864
|
delete_folder = api.delete_folder
|
|
4865
|
+
create_commits_on_pr = api.create_commits_on_pr
|
|
4340
4866
|
create_branch = api.create_branch
|
|
4341
4867
|
delete_branch = api.delete_branch
|
|
4342
4868
|
create_tag = api.create_tag
|
|
@@ -4364,6 +4890,7 @@ add_space_secret = api.add_space_secret
|
|
|
4364
4890
|
delete_space_secret = api.delete_space_secret
|
|
4365
4891
|
get_space_runtime = api.get_space_runtime
|
|
4366
4892
|
request_space_hardware = api.request_space_hardware
|
|
4893
|
+
set_space_sleep_time = api.set_space_sleep_time
|
|
4367
4894
|
pause_space = api.pause_space
|
|
4368
4895
|
restart_space = api.restart_space
|
|
4369
4896
|
duplicate_space = api.duplicate_space
|