hfutils 0.2.10__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {hfutils-0.2.10/hfutils.egg-info → hfutils-0.3.0}/PKG-INFO +3 -2
- {hfutils-0.2.10 → hfutils-0.3.0}/hfutils/config/meta.py +1 -1
- {hfutils-0.2.10 → hfutils-0.3.0}/hfutils/entry/cli.py +4 -0
- hfutils-0.3.0/hfutils/entry/clone.py +79 -0
- hfutils-0.3.0/hfutils/entry/rollback.py +61 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/hfutils/operate/upload.py +86 -13
- hfutils-0.3.0/hfutils/repository/__init__.py +7 -0
- hfutils-0.3.0/hfutils/repository/base.py +81 -0
- hfutils-0.3.0/hfutils/repository/clone.py +87 -0
- hfutils-0.3.0/hfutils/repository/rollback.py +102 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/hfutils/utils/logging.py +1 -1
- {hfutils-0.2.10 → hfutils-0.3.0/hfutils.egg-info}/PKG-INFO +3 -2
- {hfutils-0.2.10 → hfutils-0.3.0}/hfutils.egg-info/SOURCES.txt +6 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/hfutils.egg-info/requires.txt +2 -1
- {hfutils-0.2.10 → hfutils-0.3.0}/requirements.txt +3 -2
- {hfutils-0.2.10 → hfutils-0.3.0}/LICENSE +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/MANIFEST.in +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/README.md +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/hfutils/__init__.py +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/hfutils/__main__.py +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/hfutils/archive/__init__.py +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/hfutils/archive/base.py +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/hfutils/archive/rar.py +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/hfutils/archive/sevenz.py +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/hfutils/archive/tar.py +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/hfutils/archive/zip.py +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/hfutils/cache/__init__.py +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/hfutils/cache/delete.py +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/hfutils/config/__init__.py +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/hfutils/entry/__init__.py +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/hfutils/entry/base.py +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/hfutils/entry/dispatch.py +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/hfutils/entry/download.py +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/hfutils/entry/index.py +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/hfutils/entry/ls.py +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/hfutils/entry/ls_repo.py +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/hfutils/entry/upload.py +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/hfutils/entry/whoami.py +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/hfutils/index/__init__.py +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/hfutils/index/fetch.py +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/hfutils/index/hash.py +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/hfutils/index/make.py +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/hfutils/index/validate.py +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/hfutils/operate/__init__.py +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/hfutils/operate/base.py +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/hfutils/operate/download.py +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/hfutils/operate/validate.py +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/hfutils/utils/__init__.py +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/hfutils/utils/binary.py +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/hfutils/utils/download.py +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/hfutils/utils/irregular_repo.json +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/hfutils/utils/path.py +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/hfutils/utils/temp.py +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/hfutils/utils/tqdm_.py +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/hfutils/utils/walk.py +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/hfutils.egg-info/dependency_links.txt +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/hfutils.egg-info/entry_points.txt +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/hfutils.egg-info/top_level.txt +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/requirements-7z.txt +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/requirements-build.txt +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/requirements-doc.txt +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/requirements-rar.txt +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/requirements-test.txt +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/requirements-transfer.txt +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/setup.cfg +0 -0
- {hfutils-0.2.10 → hfutils-0.3.0}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: hfutils
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Useful utilities for huggingface
|
|
5
5
|
Home-page: https://github.com/deepghs/hfutils
|
|
6
6
|
Author: narugo1992
|
|
@@ -20,13 +20,14 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
20
20
|
Requires-Python: >=3.8
|
|
21
21
|
Description-Content-Type: text/markdown
|
|
22
22
|
License-File: LICENSE
|
|
23
|
-
Requires-Dist: hbutils>=0.
|
|
23
|
+
Requires-Dist: hbutils>=0.10.0
|
|
24
24
|
Requires-Dist: huggingface_hub>=0.23
|
|
25
25
|
Requires-Dist: tqdm
|
|
26
26
|
Requires-Dist: requests
|
|
27
27
|
Requires-Dist: click>=7
|
|
28
28
|
Requires-Dist: tzlocal
|
|
29
29
|
Requires-Dist: natsort
|
|
30
|
+
Requires-Dist: urlobject
|
|
30
31
|
Provides-Extra: doc
|
|
31
32
|
Requires-Dist: Jinja2>=3.0.0; extra == "doc"
|
|
32
33
|
Requires-Dist: sphinx>=3.2.0; extra == "doc"
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
from .clone import _add_clone_subcommand
|
|
1
2
|
from .dispatch import hfutilcli
|
|
2
3
|
from .download import _add_download_subcommand
|
|
3
4
|
from .index import _add_index_subcommand
|
|
4
5
|
from .ls import _add_ls_subcommand
|
|
5
6
|
from .ls_repo import _add_ls_repo_subcommand
|
|
7
|
+
from .rollback import _add_rollback_subcommand
|
|
6
8
|
from .upload import _add_upload_subcommand
|
|
7
9
|
from .whoami import _add_whoami_subcommand
|
|
8
10
|
|
|
@@ -13,6 +15,8 @@ _DECORATORS = [
|
|
|
13
15
|
_add_whoami_subcommand,
|
|
14
16
|
_add_ls_repo_subcommand,
|
|
15
17
|
_add_index_subcommand,
|
|
18
|
+
_add_rollback_subcommand,
|
|
19
|
+
_add_clone_subcommand,
|
|
16
20
|
]
|
|
17
21
|
|
|
18
22
|
cli = hfutilcli
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import os.path
|
|
3
|
+
import re
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
import click
|
|
7
|
+
|
|
8
|
+
from .base import CONTEXT_SETTINGS
|
|
9
|
+
from ..operate.base import REPO_TYPES, RepoTypeTyping
|
|
10
|
+
from ..repository import hf_hub_clone
|
|
11
|
+
from ..utils import ColoredFormatter
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _add_clone_subcommand(cli: click.Group) -> click.Group:
|
|
15
|
+
"""
|
|
16
|
+
Add a 'clone' subcommand to the given CLI group.
|
|
17
|
+
|
|
18
|
+
This function defines a 'clone' command that allows users to clone repositories
|
|
19
|
+
from the Hugging Face Hub.
|
|
20
|
+
|
|
21
|
+
:param cli: The Click group to which the 'clone' command will be added.
|
|
22
|
+
:type cli: click.Group
|
|
23
|
+
:return: The modified Click group with the 'clone' command added.
|
|
24
|
+
:rtype: click.Group
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
@cli.command('clone', help='Clone remote repository with permission (if possible).\n\n'
|
|
28
|
+
'Set environment $HF_TOKEN to use your own access token.',
|
|
29
|
+
context_settings=CONTEXT_SETTINGS)
|
|
30
|
+
@click.option('-r', '--repository', 'repo_id', type=str, required=True,
|
|
31
|
+
help='Repository to clone.')
|
|
32
|
+
@click.option('-t', '--type', 'repo_type', type=click.Choice(REPO_TYPES), default='dataset',
|
|
33
|
+
help='Type of the HuggingFace repository.', show_default=True)
|
|
34
|
+
@click.option('-R', '--revision', 'revision', type=str, default='main',
|
|
35
|
+
help='Revision of repository.', show_default=True)
|
|
36
|
+
@click.option('-o', '--output_dir', 'output_dir', type=str, default=None,
|
|
37
|
+
help='Output directory to clone the repository')
|
|
38
|
+
@click.option('--no_lfs', 'no_lfs', type=bool, is_flag=True, default=False,
|
|
39
|
+
help='Do not download LFS files, just download their pointers.')
|
|
40
|
+
@click.option('-d', '--max_depth', 'max_depth', type=int, default=None,
|
|
41
|
+
help='Max commit depth to clone. Useful when this repository has large numbers of commits.')
|
|
42
|
+
def clone(repo_id: str, repo_type: RepoTypeTyping, revision: str, output_dir: Optional[str] = None,
|
|
43
|
+
no_lfs: bool = False, max_depth: Optional[int] = None):
|
|
44
|
+
"""
|
|
45
|
+
Clone a repository from the Hugging Face Hub.
|
|
46
|
+
|
|
47
|
+
This function sets up logging and then calls hf_hub_clone to perform the actual cloning.
|
|
48
|
+
|
|
49
|
+
:param repo_id: The ID of the repository to clone.
|
|
50
|
+
:param repo_type: The type of the repository (e.g., 'dataset', 'model').
|
|
51
|
+
:param revision: The revision of the repository to clone.
|
|
52
|
+
:param output_dir: The directory where the repository will be cloned. If None, uses the repo's short name.
|
|
53
|
+
:param no_lfs: If True, don't download LFS files, just their pointers.
|
|
54
|
+
:param max_depth: The maximum depth of commits to clone.
|
|
55
|
+
"""
|
|
56
|
+
# Set up logging
|
|
57
|
+
logger = logging.getLogger()
|
|
58
|
+
logger.setLevel(logging.INFO)
|
|
59
|
+
console_handler = logging.StreamHandler()
|
|
60
|
+
console_handler.setFormatter(ColoredFormatter())
|
|
61
|
+
logger.addHandler(console_handler)
|
|
62
|
+
|
|
63
|
+
# Determine the output directory
|
|
64
|
+
short_name = list(filter(bool, re.split(r'[/\\]+', repo_id)))[-1]
|
|
65
|
+
output_dir = os.path.abspath(output_dir or short_name)
|
|
66
|
+
if os.path.dirname(output_dir):
|
|
67
|
+
os.makedirs(os.path.dirname(output_dir), exist_ok=True)
|
|
68
|
+
|
|
69
|
+
# Perform the clone operation
|
|
70
|
+
hf_hub_clone(
|
|
71
|
+
repo_id=repo_id,
|
|
72
|
+
repo_type=repo_type,
|
|
73
|
+
revision=revision,
|
|
74
|
+
dst_dir=output_dir,
|
|
75
|
+
no_lfs=no_lfs,
|
|
76
|
+
max_depth=max_depth,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
return cli
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
|
|
5
|
+
from .base import CONTEXT_SETTINGS
|
|
6
|
+
from ..operate.base import REPO_TYPES, RepoTypeTyping
|
|
7
|
+
from ..repository import hf_hub_rollback
|
|
8
|
+
from ..utils import ColoredFormatter
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _add_rollback_subcommand(cli: click.Group) -> click.Group:
|
|
12
|
+
"""
|
|
13
|
+
Add a 'rollback' subcommand to the given CLI group.
|
|
14
|
+
|
|
15
|
+
This function defines a 'rollback' command that allows users to rollback
|
|
16
|
+
repositories on the Hugging Face Hub to a specific commit.
|
|
17
|
+
|
|
18
|
+
:param cli: The Click group to which the 'rollback' command will be added.
|
|
19
|
+
:type cli: click.Group
|
|
20
|
+
:return: The modified Click group with the 'rollback' command added.
|
|
21
|
+
:rtype: click.Group
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
@cli.command('rollback', help='Rollback remote repository to a specific commit.\n\n'
|
|
25
|
+
'Set environment $HF_TOKEN to use your own access token.',
|
|
26
|
+
context_settings=CONTEXT_SETTINGS)
|
|
27
|
+
@click.option('-r', '--repository', 'repo_id', type=str, required=True,
|
|
28
|
+
help='Repository to rollback.')
|
|
29
|
+
@click.option('-t', '--type', 'repo_type', type=click.Choice(REPO_TYPES), default='dataset',
|
|
30
|
+
help='Type of the HuggingFace repository.', show_default=True)
|
|
31
|
+
@click.option('-R', '--revision', 'revision', type=str, default='main',
|
|
32
|
+
help='Revision of repository.', show_default=True)
|
|
33
|
+
@click.option('-c', '--commit_id', 'rollback_to_commit_id', type=str, required=True,
|
|
34
|
+
help='Rollback back to this commit id.')
|
|
35
|
+
def rollback(repo_id: str, repo_type: RepoTypeTyping, revision: str, rollback_to_commit_id: str):
|
|
36
|
+
"""
|
|
37
|
+
Rollback a repository on the Hugging Face Hub to a specific commit.
|
|
38
|
+
|
|
39
|
+
This function sets up logging and then calls hf_hub_rollback to perform the actual rollback.
|
|
40
|
+
|
|
41
|
+
:param repo_id: The ID of the repository to rollback.
|
|
42
|
+
:param repo_type: The type of the repository (e.g., 'dataset', 'model').
|
|
43
|
+
:param revision: The revision of the repository to rollback.
|
|
44
|
+
:param rollback_to_commit_id: The commit ID to rollback to.
|
|
45
|
+
"""
|
|
46
|
+
# Set up logging
|
|
47
|
+
logger = logging.getLogger()
|
|
48
|
+
logger.setLevel(logging.INFO)
|
|
49
|
+
console_handler = logging.StreamHandler()
|
|
50
|
+
console_handler.setFormatter(ColoredFormatter())
|
|
51
|
+
logger.addHandler(console_handler)
|
|
52
|
+
|
|
53
|
+
# Perform the rollback operation
|
|
54
|
+
hf_hub_rollback(
|
|
55
|
+
repo_id=repo_id,
|
|
56
|
+
repo_type=repo_type,
|
|
57
|
+
revision=revision,
|
|
58
|
+
rollback_to=rollback_to_commit_id,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
return cli
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import datetime
|
|
2
|
+
import logging
|
|
3
|
+
import math
|
|
2
4
|
import os.path
|
|
3
5
|
import re
|
|
6
|
+
import time
|
|
4
7
|
from typing import Optional, List
|
|
5
8
|
|
|
9
|
+
from hbutils.string import plural_word
|
|
6
10
|
from huggingface_hub import CommitOperationAdd, CommitOperationDelete
|
|
7
11
|
|
|
8
12
|
from .base import RepoTypeTyping, get_hf_client, list_files_in_repository, _IGNORE_PATTERN_UNSET
|
|
9
13
|
from ..archive import get_archive_type, archive_pack
|
|
10
|
-
from ..utils import walk_files, TemporaryDirectory
|
|
14
|
+
from ..utils import walk_files, TemporaryDirectory, tqdm
|
|
11
15
|
|
|
12
16
|
|
|
13
17
|
def upload_file_to_file(local_file, repo_id: str, file_in_repo: str,
|
|
@@ -77,11 +81,14 @@ def upload_directory_as_archive(local_directory, repo_id: str, archive_in_repo:
|
|
|
77
81
|
_PATH_SEP = re.compile(r'[/\\]+')
|
|
78
82
|
|
|
79
83
|
|
|
80
|
-
def upload_directory_as_directory(
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
84
|
+
def upload_directory_as_directory(
|
|
85
|
+
local_directory, repo_id: str, path_in_repo: str,
|
|
86
|
+
repo_type: RepoTypeTyping = 'dataset', revision: str = 'main',
|
|
87
|
+
message: Optional[str] = None, time_suffix: bool = True,
|
|
88
|
+
clear: bool = False, ignore_patterns: List[str] = _IGNORE_PATTERN_UNSET,
|
|
89
|
+
hf_token: Optional[str] = None, operation_chunk_size: Optional[int] = None,
|
|
90
|
+
upload_timespan: float = 5.0,
|
|
91
|
+
):
|
|
85
92
|
"""
|
|
86
93
|
Upload a local directory and its files to a specified path in a Hugging Face repository.
|
|
87
94
|
|
|
@@ -105,6 +112,24 @@ def upload_directory_as_directory(local_directory, repo_id: str, path_in_repo: s
|
|
|
105
112
|
:type ignore_patterns: List[str]
|
|
106
113
|
:param hf_token: Huggingface token for API client, use ``HF_TOKEN`` variable if not assigned.
|
|
107
114
|
:type hf_token: str, optional
|
|
115
|
+
:param operation_chunk_size: Chunk size of the operations. All the operations will be
|
|
116
|
+
seperated into multiple commits when this is set.
|
|
117
|
+
:type operation_chunk_size: Optional[int]
|
|
118
|
+
:param upload_timespan: Upload minimal time interval when chunked uploading enabled.
|
|
119
|
+
:type upload_timespan: float
|
|
120
|
+
|
|
121
|
+
.. note::
|
|
122
|
+
When `operation_chunk_size` is set, multiple commits will be created. When some commits failed,
|
|
123
|
+
it will roll back to the startup commit, using :func:`hfutils.repository.hf_hub_rollback` function..
|
|
124
|
+
|
|
125
|
+
.. warning::
|
|
126
|
+
When `operation_chunk_size` is set, multiple commits will be created. But HuggingFace's repository
|
|
127
|
+
api cannot guarantee the atomic feature of your data. So **this function is not thread-safe**.
|
|
128
|
+
|
|
129
|
+
.. note::
|
|
130
|
+
The rate limit of HuggingFace repository commit creation is approximately 120 commits / hour.
|
|
131
|
+
So if you really have large number of chunks to create, please set the `upload_timespan` to a value
|
|
132
|
+
no less than `30.0` to make sure your uploading will not be rate-limited.
|
|
108
133
|
"""
|
|
109
134
|
hf_client = get_hf_client(hf_token)
|
|
110
135
|
if clear:
|
|
@@ -135,10 +160,58 @@ def upload_directory_as_directory(local_directory, repo_id: str, path_in_repo: s
|
|
|
135
160
|
if time_suffix:
|
|
136
161
|
commit_message = f'{commit_message}, on {current_time}'
|
|
137
162
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
163
|
+
if operation_chunk_size:
|
|
164
|
+
initial_commit_id = hf_client.list_repo_commits(
|
|
165
|
+
repo_id=repo_id,
|
|
166
|
+
repo_type=repo_type,
|
|
167
|
+
revision=revision
|
|
168
|
+
)[0].commit_id
|
|
169
|
+
|
|
170
|
+
last_upload_at = None
|
|
171
|
+
try:
|
|
172
|
+
total_chunks = int(math.ceil(len(operations) / operation_chunk_size))
|
|
173
|
+
for chunk_id in tqdm(range(total_chunks), desc='Chunked Commits'):
|
|
174
|
+
operation_chunk = operations[chunk_id * operation_chunk_size:(chunk_id + 1) * operation_chunk_size]
|
|
175
|
+
|
|
176
|
+
# sleep for the given time
|
|
177
|
+
if last_upload_at:
|
|
178
|
+
sleep_time = last_upload_at + upload_timespan - time.time()
|
|
179
|
+
if sleep_time > 0:
|
|
180
|
+
logging.info(f'Sleep for {sleep_time:.1f}s due to the timespan limitation ...')
|
|
181
|
+
time.sleep(sleep_time)
|
|
182
|
+
|
|
183
|
+
last_upload_at = time.time()
|
|
184
|
+
logging.info(f'Uploading chunk #{chunk_id + 1}, '
|
|
185
|
+
f'with {plural_word(len(operation_chunk), "operation")} ...')
|
|
186
|
+
hf_client.create_commit(
|
|
187
|
+
repo_id=repo_id,
|
|
188
|
+
repo_type=repo_type,
|
|
189
|
+
revision=revision,
|
|
190
|
+
operations=operation_chunk,
|
|
191
|
+
commit_message=f'[Chunk #{chunk_id + 1}/{total_chunks}] {commit_message}',
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
except Exception:
|
|
196
|
+
from ..repository import hf_hub_rollback
|
|
197
|
+
|
|
198
|
+
logging.error(f'Error found when executing chunked uploading, '
|
|
199
|
+
f'revision {revision!r} will rollback to {initial_commit_id!r} ...')
|
|
200
|
+
hf_hub_rollback(
|
|
201
|
+
repo_id=repo_id,
|
|
202
|
+
repo_type=repo_type,
|
|
203
|
+
revision=revision,
|
|
204
|
+
rollback_to=initial_commit_id,
|
|
205
|
+
hf_token=hf_token,
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
raise
|
|
209
|
+
|
|
210
|
+
else:
|
|
211
|
+
hf_client.create_commit(
|
|
212
|
+
repo_id=repo_id,
|
|
213
|
+
repo_type=repo_type,
|
|
214
|
+
revision=revision,
|
|
215
|
+
operations=operations,
|
|
216
|
+
commit_message=commit_message,
|
|
217
|
+
)
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module provides utility functions for working with Hugging Face Hub repositories and Git.
|
|
3
|
+
|
|
4
|
+
It includes functions to:
|
|
5
|
+
|
|
6
|
+
1. Generate Hugging Face Hub repository URLs.
|
|
7
|
+
2. Check Git and Git LFS installations.
|
|
8
|
+
|
|
9
|
+
The module uses the `git_info` function from the `hbutils.system` package and constants from `huggingface_hub`.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from typing import Optional
|
|
13
|
+
|
|
14
|
+
from hbutils.system import git_info
|
|
15
|
+
from huggingface_hub.constants import ENDPOINT
|
|
16
|
+
|
|
17
|
+
from ..utils.path import RepoTypeTyping
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def hf_hub_repo_url(repo_id: str, repo_type: RepoTypeTyping = 'dataset', endpoint: Optional[str] = None) -> str:
|
|
21
|
+
"""
|
|
22
|
+
Generate a Hugging Face Hub repository URL.
|
|
23
|
+
|
|
24
|
+
:param repo_id: The repository ID.
|
|
25
|
+
:type repo_id: str
|
|
26
|
+
:param repo_type: The type of repository. Can be 'model', 'dataset', or 'space'. Defaults to 'dataset'.
|
|
27
|
+
:type repo_type: RepoTypeTyping
|
|
28
|
+
:param endpoint: The Hugging Face Hub endpoint. If None, uses the default ENDPOINT.
|
|
29
|
+
:type endpoint: Optional[str]
|
|
30
|
+
|
|
31
|
+
:return: The full URL for the specified Hugging Face Hub repository.
|
|
32
|
+
:rtype: str
|
|
33
|
+
|
|
34
|
+
:raises ValueError: If an unknown repository type is provided.
|
|
35
|
+
|
|
36
|
+
:usage:
|
|
37
|
+
>>> hf_hub_repo_url('username/repo', 'model')
|
|
38
|
+
'https://huggingface.co/username/repo'
|
|
39
|
+
>>> hf_hub_repo_url('username/dataset', 'dataset')
|
|
40
|
+
'https://huggingface.co/datasets/username/dataset'
|
|
41
|
+
"""
|
|
42
|
+
endpoint = endpoint or ENDPOINT
|
|
43
|
+
if repo_type == 'model':
|
|
44
|
+
return f'{endpoint}/{repo_id}'
|
|
45
|
+
elif repo_type == 'dataset':
|
|
46
|
+
return f'{endpoint}/datasets/{repo_id}'
|
|
47
|
+
elif repo_type == 'space':
|
|
48
|
+
return f'{endpoint}/spaces/{repo_id}'
|
|
49
|
+
else:
|
|
50
|
+
raise ValueError(f'Unknown repository type - {repo_type!r}.')
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _check_git(requires_lfs: bool = True) -> str:
|
|
54
|
+
"""
|
|
55
|
+
Check Git and Git LFS installations.
|
|
56
|
+
|
|
57
|
+
This function checks if Git is installed and, optionally, if Git LFS is installed.
|
|
58
|
+
The results are cached for improved performance.
|
|
59
|
+
|
|
60
|
+
:param requires_lfs: Whether to check for Git LFS installation. Defaults to True.
|
|
61
|
+
:type requires_lfs: bool
|
|
62
|
+
|
|
63
|
+
:return: The path to the Git executable.
|
|
64
|
+
:rtype: str
|
|
65
|
+
|
|
66
|
+
:raises EnvironmentError: If Git is not installed or if Git LFS is required but not installed.
|
|
67
|
+
|
|
68
|
+
:usage:
|
|
69
|
+
>>> _check_git()
|
|
70
|
+
'/usr/bin/git'
|
|
71
|
+
>>> _check_git(requires_lfs=False)
|
|
72
|
+
'/usr/bin/git'
|
|
73
|
+
"""
|
|
74
|
+
info = git_info()
|
|
75
|
+
if not info['installed']:
|
|
76
|
+
raise EnvironmentError('Git not installed.')
|
|
77
|
+
else:
|
|
78
|
+
if requires_lfs and not info['lfs']['installed']:
|
|
79
|
+
raise EnvironmentError('Git lfs not installed.')
|
|
80
|
+
else:
|
|
81
|
+
return info['exec']
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module provides functionality to clone Hugging Face Hub repositories using Git.
|
|
3
|
+
|
|
4
|
+
It includes a function to clone a repository from Hugging Face Hub to a local directory,
|
|
5
|
+
with options for authentication, repository type, revision, and other Git clone parameters.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import copy
|
|
9
|
+
import logging
|
|
10
|
+
import os
|
|
11
|
+
import subprocess
|
|
12
|
+
import sys
|
|
13
|
+
from typing import Optional
|
|
14
|
+
|
|
15
|
+
from huggingface_hub.errors import LocalTokenNotFoundError
|
|
16
|
+
from urlobject import URLObject
|
|
17
|
+
|
|
18
|
+
from .base import hf_hub_repo_url, _check_git
|
|
19
|
+
from ..operate import get_hf_client
|
|
20
|
+
from ..utils.path import RepoTypeTyping
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def hf_hub_clone(repo_id: str, dst_dir: str,
|
|
24
|
+
repo_type: RepoTypeTyping = 'dataset', revision: str = 'main',
|
|
25
|
+
endpoint: Optional[str] = None, hf_token: Optional[str] = None,
|
|
26
|
+
silent: bool = False, no_lfs: bool = False, max_depth: Optional[int] = None):
|
|
27
|
+
"""
|
|
28
|
+
Clone a repository from Hugging Face Hub.
|
|
29
|
+
|
|
30
|
+
:param repo_id: The ID of the repository to clone.
|
|
31
|
+
:type repo_id: str
|
|
32
|
+
:param dst_dir: The local directory to clone the repository into.
|
|
33
|
+
:type dst_dir: str
|
|
34
|
+
:param repo_type: The type of the repository ('dataset', 'model', or 'space'). Defaults to 'dataset'.
|
|
35
|
+
:type repo_type: RepoTypeTyping
|
|
36
|
+
:param revision: The specific revision to clone. Defaults to 'main'.
|
|
37
|
+
:type revision: str
|
|
38
|
+
:param endpoint: The Hugging Face Hub endpoint. If None, uses the default endpoint.
|
|
39
|
+
:type endpoint: Optional[str]
|
|
40
|
+
:param hf_token: The Hugging Face authentication token. If None, attempts to use stored credentials.
|
|
41
|
+
:type hf_token: Optional[str]
|
|
42
|
+
:param silent: If True, suppresses command output. Defaults to False.
|
|
43
|
+
:type silent: bool
|
|
44
|
+
:param no_lfs: If True, skips downloading of LFS files. Defaults to False.
|
|
45
|
+
:type no_lfs: bool
|
|
46
|
+
:param max_depth: The maximum number of commits to fetch. If None, fetches all commits.
|
|
47
|
+
:type max_depth: Optional[int]
|
|
48
|
+
|
|
49
|
+
:raises subprocess.CalledProcessError: If the Git clone command fails.
|
|
50
|
+
|
|
51
|
+
:usage:
|
|
52
|
+
>>> hf_hub_clone('username/repo', '/path/to/local/dir', repo_type='model', revision='v1.0')
|
|
53
|
+
"""
|
|
54
|
+
_git = _check_git(requires_lfs=not no_lfs)
|
|
55
|
+
hf_client = get_hf_client(hf_token)
|
|
56
|
+
try:
|
|
57
|
+
username = hf_client.whoami()['name']
|
|
58
|
+
except LocalTokenNotFoundError:
|
|
59
|
+
username = None # anonymous mode
|
|
60
|
+
|
|
61
|
+
clone_url = URLObject(hf_hub_repo_url(
|
|
62
|
+
repo_id=repo_id,
|
|
63
|
+
repo_type=repo_type,
|
|
64
|
+
endpoint=endpoint,
|
|
65
|
+
))
|
|
66
|
+
if username:
|
|
67
|
+
clone_url = clone_url.with_username(username).with_password(hf_client.token)
|
|
68
|
+
clone_url = str(clone_url)
|
|
69
|
+
|
|
70
|
+
command = [_git, 'clone', '-b', revision]
|
|
71
|
+
if max_depth is not None:
|
|
72
|
+
command.extend(['--depth', str(max_depth)])
|
|
73
|
+
command.extend([clone_url, dst_dir])
|
|
74
|
+
logging.info(f'Cloning repository with command {command!r} ...')
|
|
75
|
+
with open(os.devnull, 'w') as nf:
|
|
76
|
+
envs = copy.deepcopy(os.environ)
|
|
77
|
+
if no_lfs:
|
|
78
|
+
envs['GIT_LFS_SKIP_SMUDGE'] = '1'
|
|
79
|
+
envs['GIT_TERMINAL_PROMPT'] = '0'
|
|
80
|
+
process = subprocess.run(
|
|
81
|
+
args=command,
|
|
82
|
+
stdout=nf if silent else sys.stdout,
|
|
83
|
+
stderr=nf if silent else sys.stderr,
|
|
84
|
+
env=envs,
|
|
85
|
+
bufsize=0 if not silent else -1,
|
|
86
|
+
)
|
|
87
|
+
process.check_returncode()
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module provides functionality to rollback a Hugging Face Hub repository to a specific commit.
|
|
3
|
+
|
|
4
|
+
It includes a function to clone a repository, reset it to a specified commit, and force push the changes back to the remote repository.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
import os.path
|
|
9
|
+
import subprocess
|
|
10
|
+
import sys
|
|
11
|
+
from typing import Optional
|
|
12
|
+
|
|
13
|
+
from .base import _check_git
|
|
14
|
+
from .clone import hf_hub_clone
|
|
15
|
+
from ..utils import TemporaryDirectory
|
|
16
|
+
from ..utils.path import RepoTypeTyping
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def hf_hub_rollback(repo_id: str, rollback_to: str,
|
|
20
|
+
repo_type: RepoTypeTyping = 'dataset', revision: str = 'main',
|
|
21
|
+
endpoint: Optional[str] = None, hf_token: Optional[str] = None, silent: bool = False):
|
|
22
|
+
"""
|
|
23
|
+
Rollback a Hugging Face Hub repository to a specific commit.
|
|
24
|
+
|
|
25
|
+
This function clones the repository, resets it to the specified commit, and force pushes the changes back to the remote.
|
|
26
|
+
|
|
27
|
+
:param repo_id: The ID of the repository to rollback.
|
|
28
|
+
:type repo_id: str
|
|
29
|
+
:param rollback_to: The commit hash or reference to rollback to.
|
|
30
|
+
:type rollback_to: str
|
|
31
|
+
:param repo_type: The type of the repository ('dataset', 'model', or 'space'). Defaults to 'dataset'.
|
|
32
|
+
:type repo_type: RepoTypeTyping
|
|
33
|
+
:param revision: The branch or tag to clone. Defaults to 'main'.
|
|
34
|
+
:type revision: str
|
|
35
|
+
:param endpoint: The Hugging Face Hub endpoint. If None, uses the default endpoint.
|
|
36
|
+
:type endpoint: Optional[str]
|
|
37
|
+
:param hf_token: The Hugging Face authentication token. If None, attempts to use stored credentials.
|
|
38
|
+
:type hf_token: Optional[str]
|
|
39
|
+
:param silent: If True, suppresses command output. Defaults to False.
|
|
40
|
+
:type silent: bool
|
|
41
|
+
|
|
42
|
+
:raises subprocess.CalledProcessError: If any Git command fails during the process.
|
|
43
|
+
|
|
44
|
+
:usage:
|
|
45
|
+
>>> hf_hub_rollback('username/repo', 'abc123', repo_type='model')
|
|
46
|
+
|
|
47
|
+
.. note::
|
|
48
|
+
|
|
49
|
+
This function performs the following steps:
|
|
50
|
+
|
|
51
|
+
1. Checks for Git installation (without LFS requirement).
|
|
52
|
+
2. Creates a temporary directory for the operation.
|
|
53
|
+
3. Clones the specified repository into the temporary directory.
|
|
54
|
+
4. Resets the repository to the specified commit using 'git reset --hard'.
|
|
55
|
+
5. Force pushes the changes back to the remote repository.
|
|
56
|
+
|
|
57
|
+
.. warning::
|
|
58
|
+
This operation can overwrite remote history and can not be cancelled. Use with caution.
|
|
59
|
+
"""
|
|
60
|
+
_git = _check_git(requires_lfs=False)
|
|
61
|
+
with TemporaryDirectory() as td, open(os.devnull, 'w') as nf:
|
|
62
|
+
envs = {
|
|
63
|
+
**os.environ,
|
|
64
|
+
'GIT_LFS_SKIP_SMUDGE': '1',
|
|
65
|
+
'GIT_TERMINAL_PROMPT': '0',
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
repo_dir = os.path.join(td, 'repo')
|
|
69
|
+
hf_hub_clone(
|
|
70
|
+
repo_id=repo_id,
|
|
71
|
+
dst_dir=repo_dir,
|
|
72
|
+
repo_type=repo_type,
|
|
73
|
+
revision=revision,
|
|
74
|
+
endpoint=endpoint,
|
|
75
|
+
hf_token=hf_token,
|
|
76
|
+
silent=silent,
|
|
77
|
+
no_lfs=True,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
reset_command = [_git, 'reset', '--hard', rollback_to]
|
|
81
|
+
logging.info(f'Git resetting with command {reset_command!r} ...')
|
|
82
|
+
process = subprocess.run(
|
|
83
|
+
args=reset_command,
|
|
84
|
+
stdout=nf if silent else sys.stdout,
|
|
85
|
+
stderr=nf if silent else sys.stderr,
|
|
86
|
+
env=envs,
|
|
87
|
+
bufsize=0 if not silent else -1,
|
|
88
|
+
cwd=repo_dir,
|
|
89
|
+
)
|
|
90
|
+
process.check_returncode()
|
|
91
|
+
|
|
92
|
+
push_command = [_git, 'push', '-f']
|
|
93
|
+
logging.info(f'Git pushing with command {push_command!r} ...')
|
|
94
|
+
process = subprocess.run(
|
|
95
|
+
args=push_command,
|
|
96
|
+
stdout=nf if silent else sys.stdout,
|
|
97
|
+
stderr=nf if silent else sys.stderr,
|
|
98
|
+
env=envs,
|
|
99
|
+
bufsize=0 if not silent else -1,
|
|
100
|
+
cwd=repo_dir,
|
|
101
|
+
)
|
|
102
|
+
process.check_returncode()
|
|
@@ -63,7 +63,7 @@ class ColoredFormatter(logging.Formatter):
|
|
|
63
63
|
format_str = f"{Colors.BRIGHT_BLACK}[%(asctime)s]{Colors.RESET} "
|
|
64
64
|
format_str += f"{log_color}%(levelname)-8s{Colors.RESET} "
|
|
65
65
|
format_str += f"{Colors.CYAN}%(name)s{Colors.RESET} - "
|
|
66
|
-
format_str += f"
|
|
66
|
+
format_str += f"%(message)s"
|
|
67
67
|
|
|
68
68
|
formatter = logging.Formatter(format_str, datefmt="%Y-%m-%d %H:%M:%S")
|
|
69
69
|
return formatter.format(record)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: hfutils
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Useful utilities for huggingface
|
|
5
5
|
Home-page: https://github.com/deepghs/hfutils
|
|
6
6
|
Author: narugo1992
|
|
@@ -20,13 +20,14 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
20
20
|
Requires-Python: >=3.8
|
|
21
21
|
Description-Content-Type: text/markdown
|
|
22
22
|
License-File: LICENSE
|
|
23
|
-
Requires-Dist: hbutils>=0.
|
|
23
|
+
Requires-Dist: hbutils>=0.10.0
|
|
24
24
|
Requires-Dist: huggingface_hub>=0.23
|
|
25
25
|
Requires-Dist: tqdm
|
|
26
26
|
Requires-Dist: requests
|
|
27
27
|
Requires-Dist: click>=7
|
|
28
28
|
Requires-Dist: tzlocal
|
|
29
29
|
Requires-Dist: natsort
|
|
30
|
+
Requires-Dist: urlobject
|
|
30
31
|
Provides-Extra: doc
|
|
31
32
|
Requires-Dist: Jinja2>=3.0.0; extra == "doc"
|
|
32
33
|
Requires-Dist: sphinx>=3.2.0; extra == "doc"
|
|
@@ -30,11 +30,13 @@ hfutils/config/meta.py
|
|
|
30
30
|
hfutils/entry/__init__.py
|
|
31
31
|
hfutils/entry/base.py
|
|
32
32
|
hfutils/entry/cli.py
|
|
33
|
+
hfutils/entry/clone.py
|
|
33
34
|
hfutils/entry/dispatch.py
|
|
34
35
|
hfutils/entry/download.py
|
|
35
36
|
hfutils/entry/index.py
|
|
36
37
|
hfutils/entry/ls.py
|
|
37
38
|
hfutils/entry/ls_repo.py
|
|
39
|
+
hfutils/entry/rollback.py
|
|
38
40
|
hfutils/entry/upload.py
|
|
39
41
|
hfutils/entry/whoami.py
|
|
40
42
|
hfutils/index/__init__.py
|
|
@@ -47,6 +49,10 @@ hfutils/operate/base.py
|
|
|
47
49
|
hfutils/operate/download.py
|
|
48
50
|
hfutils/operate/upload.py
|
|
49
51
|
hfutils/operate/validate.py
|
|
52
|
+
hfutils/repository/__init__.py
|
|
53
|
+
hfutils/repository/base.py
|
|
54
|
+
hfutils/repository/clone.py
|
|
55
|
+
hfutils/repository/rollback.py
|
|
50
56
|
hfutils/utils/__init__.py
|
|
51
57
|
hfutils/utils/binary.py
|
|
52
58
|
hfutils/utils/download.py
|
|
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
|