Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- .venv/lib/python3.11/site-packages/huggingface_hub/commands/__init__.py +27 -0
- .venv/lib/python3.11/site-packages/huggingface_hub/commands/__pycache__/__init__.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/huggingface_hub/commands/__pycache__/_cli_utils.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/huggingface_hub/commands/__pycache__/delete_cache.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/huggingface_hub/commands/__pycache__/download.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/huggingface_hub/commands/__pycache__/env.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/huggingface_hub/commands/__pycache__/huggingface_cli.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/huggingface_hub/commands/__pycache__/lfs.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/huggingface_hub/commands/__pycache__/repo_files.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/huggingface_hub/commands/__pycache__/scan_cache.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/huggingface_hub/commands/__pycache__/tag.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/huggingface_hub/commands/__pycache__/upload.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/huggingface_hub/commands/__pycache__/upload_large_folder.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/huggingface_hub/commands/__pycache__/user.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/huggingface_hub/commands/__pycache__/version.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/huggingface_hub/commands/_cli_utils.py +69 -0
- .venv/lib/python3.11/site-packages/huggingface_hub/commands/delete_cache.py +428 -0
- .venv/lib/python3.11/site-packages/huggingface_hub/commands/download.py +200 -0
- .venv/lib/python3.11/site-packages/huggingface_hub/commands/env.py +36 -0
- .venv/lib/python3.11/site-packages/huggingface_hub/commands/huggingface_cli.py +61 -0
- .venv/lib/python3.11/site-packages/huggingface_hub/commands/lfs.py +200 -0
- .venv/lib/python3.11/site-packages/huggingface_hub/commands/repo_files.py +128 -0
- .venv/lib/python3.11/site-packages/huggingface_hub/commands/scan_cache.py +181 -0
- .venv/lib/python3.11/site-packages/huggingface_hub/commands/tag.py +159 -0
- .venv/lib/python3.11/site-packages/huggingface_hub/commands/upload.py +299 -0
- .venv/lib/python3.11/site-packages/huggingface_hub/commands/upload_large_folder.py +129 -0
- .venv/lib/python3.11/site-packages/huggingface_hub/commands/user.py +304 -0
- .venv/lib/python3.11/site-packages/huggingface_hub/commands/version.py +37 -0
- .venv/lib/python3.11/site-packages/huggingface_hub/serialization/__pycache__/__init__.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/huggingface_hub/serialization/__pycache__/_base.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/huggingface_hub/serialization/__pycache__/_tensorflow.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/huggingface_hub/serialization/__pycache__/_torch.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/huggingface_hub/utils/__init__.py +110 -0
- .venv/lib/python3.11/site-packages/huggingface_hub/utils/__pycache__/_chunk_utils.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/huggingface_hub/utils/__pycache__/_fixes.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/huggingface_hub/utils/__pycache__/_git_credential.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/huggingface_hub/utils/__pycache__/_headers.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/huggingface_hub/utils/__pycache__/_hf_folder.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/huggingface_hub/utils/__pycache__/_http.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/huggingface_hub/utils/__pycache__/_lfs.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/huggingface_hub/utils/__pycache__/_pagination.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/huggingface_hub/utils/__pycache__/_paths.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/huggingface_hub/utils/__pycache__/_runtime.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/huggingface_hub/utils/__pycache__/_safetensors.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/huggingface_hub/utils/__pycache__/_subprocess.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/huggingface_hub/utils/__pycache__/_telemetry.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/huggingface_hub/utils/__pycache__/_typing.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/huggingface_hub/utils/__pycache__/_validators.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/huggingface_hub/utils/__pycache__/sha.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/huggingface_hub/utils/_auth.py +214 -0
.venv/lib/python3.11/site-packages/huggingface_hub/commands/__init__.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2020 The HuggingFace Team. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
from abc import ABC, abstractmethod
|
| 16 |
+
from argparse import _SubParsersAction
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
class BaseHuggingfaceCLICommand(ABC):
|
| 20 |
+
@staticmethod
|
| 21 |
+
@abstractmethod
|
| 22 |
+
def register_subcommand(parser: _SubParsersAction):
|
| 23 |
+
raise NotImplementedError()
|
| 24 |
+
|
| 25 |
+
@abstractmethod
|
| 26 |
+
def run(self):
|
| 27 |
+
raise NotImplementedError()
|
.venv/lib/python3.11/site-packages/huggingface_hub/commands/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (1.12 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/huggingface_hub/commands/__pycache__/_cli_utils.cpython-311.pyc
ADDED
|
Binary file (3.89 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/huggingface_hub/commands/__pycache__/delete_cache.cpython-311.pyc
ADDED
|
Binary file (20 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/huggingface_hub/commands/__pycache__/download.cpython-311.pyc
ADDED
|
Binary file (8.85 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/huggingface_hub/commands/__pycache__/env.cpython-311.pyc
ADDED
|
Binary file (1.63 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/huggingface_hub/commands/__pycache__/huggingface_cli.cpython-311.pyc
ADDED
|
Binary file (2.81 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/huggingface_hub/commands/__pycache__/lfs.cpython-311.pyc
ADDED
|
Binary file (9.6 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/huggingface_hub/commands/__pycache__/repo_files.cpython-311.pyc
ADDED
|
Binary file (5.87 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/huggingface_hub/commands/__pycache__/scan_cache.cpython-311.pyc
ADDED
|
Binary file (10.5 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/huggingface_hub/commands/__pycache__/tag.cpython-311.pyc
ADDED
|
Binary file (10.9 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/huggingface_hub/commands/__pycache__/upload.cpython-311.pyc
ADDED
|
Binary file (13.8 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/huggingface_hub/commands/__pycache__/upload_large_folder.cpython-311.pyc
ADDED
|
Binary file (6.84 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/huggingface_hub/commands/__pycache__/user.cpython-311.pyc
ADDED
|
Binary file (18 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/huggingface_hub/commands/__pycache__/version.cpython-311.pyc
ADDED
|
Binary file (1.7 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/huggingface_hub/commands/_cli_utils.py
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2022 The HuggingFace Team. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
"""Contains a utility for good-looking prints."""
|
| 15 |
+
|
| 16 |
+
import os
|
| 17 |
+
from typing import List, Union
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
class ANSI:
|
| 21 |
+
"""
|
| 22 |
+
Helper for en.wikipedia.org/wiki/ANSI_escape_code
|
| 23 |
+
"""
|
| 24 |
+
|
| 25 |
+
_bold = "\u001b[1m"
|
| 26 |
+
_gray = "\u001b[90m"
|
| 27 |
+
_red = "\u001b[31m"
|
| 28 |
+
_reset = "\u001b[0m"
|
| 29 |
+
_yellow = "\u001b[33m"
|
| 30 |
+
|
| 31 |
+
@classmethod
|
| 32 |
+
def bold(cls, s: str) -> str:
|
| 33 |
+
return cls._format(s, cls._bold)
|
| 34 |
+
|
| 35 |
+
@classmethod
|
| 36 |
+
def gray(cls, s: str) -> str:
|
| 37 |
+
return cls._format(s, cls._gray)
|
| 38 |
+
|
| 39 |
+
@classmethod
|
| 40 |
+
def red(cls, s: str) -> str:
|
| 41 |
+
return cls._format(s, cls._bold + cls._red)
|
| 42 |
+
|
| 43 |
+
@classmethod
|
| 44 |
+
def yellow(cls, s: str) -> str:
|
| 45 |
+
return cls._format(s, cls._yellow)
|
| 46 |
+
|
| 47 |
+
@classmethod
|
| 48 |
+
def _format(cls, s: str, code: str) -> str:
|
| 49 |
+
if os.environ.get("NO_COLOR"):
|
| 50 |
+
# See https://no-color.org/
|
| 51 |
+
return s
|
| 52 |
+
return f"{code}{s}{cls._reset}"
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
def tabulate(rows: List[List[Union[str, int]]], headers: List[str]) -> str:
|
| 56 |
+
"""
|
| 57 |
+
Inspired by:
|
| 58 |
+
|
| 59 |
+
- stackoverflow.com/a/8356620/593036
|
| 60 |
+
- stackoverflow.com/questions/9535954/printing-lists-as-tabular-data
|
| 61 |
+
"""
|
| 62 |
+
col_widths = [max(len(str(x)) for x in col) for col in zip(*rows, headers)]
|
| 63 |
+
row_format = ("{{:{}}} " * len(headers)).format(*col_widths)
|
| 64 |
+
lines = []
|
| 65 |
+
lines.append(row_format.format(*headers))
|
| 66 |
+
lines.append(row_format.format(*["-" * w for w in col_widths]))
|
| 67 |
+
for row in rows:
|
| 68 |
+
lines.append(row_format.format(*row))
|
| 69 |
+
return "\n".join(lines)
|
.venv/lib/python3.11/site-packages/huggingface_hub/commands/delete_cache.py
ADDED
|
@@ -0,0 +1,428 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# coding=utf-8
|
| 2 |
+
# Copyright 2022-present, the HuggingFace Inc. team.
|
| 3 |
+
#
|
| 4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
+
# you may not use this file except in compliance with the License.
|
| 6 |
+
# You may obtain a copy of the License at
|
| 7 |
+
#
|
| 8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
+
#
|
| 10 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
+
# See the License for the specific language governing permissions and
|
| 14 |
+
# limitations under the License.
|
| 15 |
+
"""Contains command to delete some revisions from the HF cache directory.
|
| 16 |
+
|
| 17 |
+
Usage:
|
| 18 |
+
huggingface-cli delete-cache
|
| 19 |
+
huggingface-cli delete-cache --disable-tui
|
| 20 |
+
huggingface-cli delete-cache --dir ~/.cache/huggingface/hub
|
| 21 |
+
|
| 22 |
+
NOTE:
|
| 23 |
+
This command is based on `InquirerPy` to build the multiselect menu in the terminal.
|
| 24 |
+
This dependency has to be installed with `pip install huggingface_hub[cli]`. Since
|
| 25 |
+
we want to avoid as much as possible cross-platform issues, I chose a library that
|
| 26 |
+
is built on top of `python-prompt-toolkit` which seems to be a reference in terminal
|
| 27 |
+
GUI (actively maintained on both Unix and Windows, 7.9k stars).
|
| 28 |
+
|
| 29 |
+
For the moment, the TUI feature is in beta.
|
| 30 |
+
|
| 31 |
+
See:
|
| 32 |
+
- https://github.com/kazhala/InquirerPy
|
| 33 |
+
- https://inquirerpy.readthedocs.io/en/latest/
|
| 34 |
+
- https://github.com/prompt-toolkit/python-prompt-toolkit
|
| 35 |
+
|
| 36 |
+
Other solutions could have been:
|
| 37 |
+
- `simple_term_menu`: would be good as well for our use case but some issues suggest
|
| 38 |
+
that Windows is less supported.
|
| 39 |
+
See: https://github.com/IngoMeyer441/simple-term-menu
|
| 40 |
+
- `PyInquirer`: very similar to `InquirerPy` but older and not maintained anymore.
|
| 41 |
+
In particular, no support of Python3.10.
|
| 42 |
+
See: https://github.com/CITGuru/PyInquirer
|
| 43 |
+
- `pick` (or `pickpack`): easy to use and flexible but built on top of Python's
|
| 44 |
+
standard library `curses` that is specific to Unix (not implemented on Windows).
|
| 45 |
+
See https://github.com/wong2/pick and https://github.com/anafvana/pickpack.
|
| 46 |
+
- `inquirer`: lot of traction (700 stars) but explicitly states "experimental
|
| 47 |
+
support of Windows". Not built on top of `python-prompt-toolkit`.
|
| 48 |
+
See https://github.com/magmax/python-inquirer
|
| 49 |
+
|
| 50 |
+
TODO: add support for `huggingface-cli delete-cache aaaaaa bbbbbb cccccc (...)` ?
|
| 51 |
+
TODO: add "--keep-last" arg to delete revisions that are not on `main` ref
|
| 52 |
+
TODO: add "--filter" arg to filter repositories by name ?
|
| 53 |
+
TODO: add "--sort" arg to sort by size ?
|
| 54 |
+
TODO: add "--limit" arg to limit to X repos ?
|
| 55 |
+
TODO: add "-y" arg for immediate deletion ?
|
| 56 |
+
See discussions in https://github.com/huggingface/huggingface_hub/issues/1025.
|
| 57 |
+
"""
|
| 58 |
+
|
| 59 |
+
import os
|
| 60 |
+
from argparse import Namespace, _SubParsersAction
|
| 61 |
+
from functools import wraps
|
| 62 |
+
from tempfile import mkstemp
|
| 63 |
+
from typing import Any, Callable, Iterable, List, Optional, Union
|
| 64 |
+
|
| 65 |
+
from ..utils import CachedRepoInfo, CachedRevisionInfo, HFCacheInfo, scan_cache_dir
|
| 66 |
+
from . import BaseHuggingfaceCLICommand
|
| 67 |
+
from ._cli_utils import ANSI
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
try:
|
| 71 |
+
from InquirerPy import inquirer
|
| 72 |
+
from InquirerPy.base.control import Choice
|
| 73 |
+
from InquirerPy.separator import Separator
|
| 74 |
+
|
| 75 |
+
_inquirer_py_available = True
|
| 76 |
+
except ImportError:
|
| 77 |
+
_inquirer_py_available = False
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
def require_inquirer_py(fn: Callable) -> Callable:
|
| 81 |
+
"""Decorator to flag methods that require `InquirerPy`."""
|
| 82 |
+
|
| 83 |
+
# TODO: refactor this + imports in a unified pattern across codebase
|
| 84 |
+
@wraps(fn)
|
| 85 |
+
def _inner(*args, **kwargs):
|
| 86 |
+
if not _inquirer_py_available:
|
| 87 |
+
raise ImportError(
|
| 88 |
+
"The `delete-cache` command requires extra dependencies to work with"
|
| 89 |
+
" the TUI.\nPlease run `pip install huggingface_hub[cli]` to install"
|
| 90 |
+
" them.\nOtherwise, disable TUI using the `--disable-tui` flag."
|
| 91 |
+
)
|
| 92 |
+
|
| 93 |
+
return fn(*args, **kwargs)
|
| 94 |
+
|
| 95 |
+
return _inner
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
# Possibility for the user to cancel deletion
|
| 99 |
+
_CANCEL_DELETION_STR = "CANCEL_DELETION"
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
class DeleteCacheCommand(BaseHuggingfaceCLICommand):
|
| 103 |
+
@staticmethod
|
| 104 |
+
def register_subcommand(parser: _SubParsersAction):
|
| 105 |
+
delete_cache_parser = parser.add_parser("delete-cache", help="Delete revisions from the cache directory.")
|
| 106 |
+
|
| 107 |
+
delete_cache_parser.add_argument(
|
| 108 |
+
"--dir",
|
| 109 |
+
type=str,
|
| 110 |
+
default=None,
|
| 111 |
+
help="cache directory (optional). Default to the default HuggingFace cache.",
|
| 112 |
+
)
|
| 113 |
+
|
| 114 |
+
delete_cache_parser.add_argument(
|
| 115 |
+
"--disable-tui",
|
| 116 |
+
action="store_true",
|
| 117 |
+
help=(
|
| 118 |
+
"Disable Terminal User Interface (TUI) mode. Useful if your"
|
| 119 |
+
" platform/terminal doesn't support the multiselect menu."
|
| 120 |
+
),
|
| 121 |
+
)
|
| 122 |
+
|
| 123 |
+
delete_cache_parser.set_defaults(func=DeleteCacheCommand)
|
| 124 |
+
|
| 125 |
+
def __init__(self, args: Namespace) -> None:
|
| 126 |
+
self.cache_dir: Optional[str] = args.dir
|
| 127 |
+
self.disable_tui: bool = args.disable_tui
|
| 128 |
+
|
| 129 |
+
def run(self):
|
| 130 |
+
"""Run `delete-cache` command with or without TUI."""
|
| 131 |
+
# Scan cache directory
|
| 132 |
+
hf_cache_info = scan_cache_dir(self.cache_dir)
|
| 133 |
+
|
| 134 |
+
# Manual review from the user
|
| 135 |
+
if self.disable_tui:
|
| 136 |
+
selected_hashes = _manual_review_no_tui(hf_cache_info, preselected=[])
|
| 137 |
+
else:
|
| 138 |
+
selected_hashes = _manual_review_tui(hf_cache_info, preselected=[])
|
| 139 |
+
|
| 140 |
+
# If deletion is not cancelled
|
| 141 |
+
if len(selected_hashes) > 0 and _CANCEL_DELETION_STR not in selected_hashes:
|
| 142 |
+
confirm_message = _get_expectations_str(hf_cache_info, selected_hashes) + " Confirm deletion ?"
|
| 143 |
+
|
| 144 |
+
# Confirm deletion
|
| 145 |
+
if self.disable_tui:
|
| 146 |
+
confirmed = _ask_for_confirmation_no_tui(confirm_message)
|
| 147 |
+
else:
|
| 148 |
+
confirmed = _ask_for_confirmation_tui(confirm_message)
|
| 149 |
+
|
| 150 |
+
# Deletion is confirmed
|
| 151 |
+
if confirmed:
|
| 152 |
+
strategy = hf_cache_info.delete_revisions(*selected_hashes)
|
| 153 |
+
print("Start deletion.")
|
| 154 |
+
strategy.execute()
|
| 155 |
+
print(
|
| 156 |
+
f"Done. Deleted {len(strategy.repos)} repo(s) and"
|
| 157 |
+
f" {len(strategy.snapshots)} revision(s) for a total of"
|
| 158 |
+
f" {strategy.expected_freed_size_str}."
|
| 159 |
+
)
|
| 160 |
+
return
|
| 161 |
+
|
| 162 |
+
# Deletion is cancelled
|
| 163 |
+
print("Deletion is cancelled. Do nothing.")
|
| 164 |
+
|
| 165 |
+
|
| 166 |
+
@require_inquirer_py
|
| 167 |
+
def _manual_review_tui(hf_cache_info: HFCacheInfo, preselected: List[str]) -> List[str]:
|
| 168 |
+
"""Ask the user for a manual review of the revisions to delete.
|
| 169 |
+
|
| 170 |
+
Displays a multi-select menu in the terminal (TUI).
|
| 171 |
+
"""
|
| 172 |
+
# Define multiselect list
|
| 173 |
+
choices = _get_tui_choices_from_scan(repos=hf_cache_info.repos, preselected=preselected)
|
| 174 |
+
checkbox = inquirer.checkbox(
|
| 175 |
+
message="Select revisions to delete:",
|
| 176 |
+
choices=choices, # List of revisions with some pre-selection
|
| 177 |
+
cycle=False, # No loop between top and bottom
|
| 178 |
+
height=100, # Large list if possible
|
| 179 |
+
# We use the instruction to display to the user the expected effect of the
|
| 180 |
+
# deletion.
|
| 181 |
+
instruction=_get_expectations_str(
|
| 182 |
+
hf_cache_info,
|
| 183 |
+
selected_hashes=[c.value for c in choices if isinstance(c, Choice) and c.enabled],
|
| 184 |
+
),
|
| 185 |
+
# We use the long instruction to should keybindings instructions to the user
|
| 186 |
+
long_instruction="Press <space> to select, <enter> to validate and <ctrl+c> to quit without modification.",
|
| 187 |
+
# Message that is displayed once the user validates its selection.
|
| 188 |
+
transformer=lambda result: f"{len(result)} revision(s) selected.",
|
| 189 |
+
)
|
| 190 |
+
|
| 191 |
+
# Add a callback to update the information line when a revision is
|
| 192 |
+
# selected/unselected
|
| 193 |
+
def _update_expectations(_) -> None:
|
| 194 |
+
# Hacky way to dynamically set an instruction message to the checkbox when
|
| 195 |
+
# a revision hash is selected/unselected.
|
| 196 |
+
checkbox._instruction = _get_expectations_str(
|
| 197 |
+
hf_cache_info,
|
| 198 |
+
selected_hashes=[choice["value"] for choice in checkbox.content_control.choices if choice["enabled"]],
|
| 199 |
+
)
|
| 200 |
+
|
| 201 |
+
checkbox.kb_func_lookup["toggle"].append({"func": _update_expectations})
|
| 202 |
+
|
| 203 |
+
# Finally display the form to the user.
|
| 204 |
+
try:
|
| 205 |
+
return checkbox.execute()
|
| 206 |
+
except KeyboardInterrupt:
|
| 207 |
+
return [] # Quit without deletion
|
| 208 |
+
|
| 209 |
+
|
| 210 |
+
@require_inquirer_py
|
| 211 |
+
def _ask_for_confirmation_tui(message: str, default: bool = True) -> bool:
|
| 212 |
+
"""Ask for confirmation using Inquirer."""
|
| 213 |
+
return inquirer.confirm(message, default=default).execute()
|
| 214 |
+
|
| 215 |
+
|
| 216 |
+
def _get_tui_choices_from_scan(repos: Iterable[CachedRepoInfo], preselected: List[str]) -> List:
|
| 217 |
+
"""Build a list of choices from the scanned repos.
|
| 218 |
+
|
| 219 |
+
Args:
|
| 220 |
+
repos (*Iterable[`CachedRepoInfo`]*):
|
| 221 |
+
List of scanned repos on which we want to delete revisions.
|
| 222 |
+
preselected (*List[`str`]*):
|
| 223 |
+
List of revision hashes that will be preselected.
|
| 224 |
+
|
| 225 |
+
Return:
|
| 226 |
+
The list of choices to pass to `inquirer.checkbox`.
|
| 227 |
+
"""
|
| 228 |
+
choices: List[Union[Choice, Separator]] = []
|
| 229 |
+
|
| 230 |
+
# First choice is to cancel the deletion. If selected, nothing will be deleted,
|
| 231 |
+
# no matter the other selected items.
|
| 232 |
+
choices.append(
|
| 233 |
+
Choice(
|
| 234 |
+
_CANCEL_DELETION_STR,
|
| 235 |
+
name="None of the following (if selected, nothing will be deleted).",
|
| 236 |
+
enabled=False,
|
| 237 |
+
)
|
| 238 |
+
)
|
| 239 |
+
|
| 240 |
+
# Display a separator per repo and a Choice for each revisions of the repo
|
| 241 |
+
for repo in sorted(repos, key=_repo_sorting_order):
|
| 242 |
+
# Repo as separator
|
| 243 |
+
choices.append(
|
| 244 |
+
Separator(
|
| 245 |
+
f"\n{repo.repo_type.capitalize()} {repo.repo_id} ({repo.size_on_disk_str},"
|
| 246 |
+
f" used {repo.last_accessed_str})"
|
| 247 |
+
)
|
| 248 |
+
)
|
| 249 |
+
for revision in sorted(repo.revisions, key=_revision_sorting_order):
|
| 250 |
+
# Revision as choice
|
| 251 |
+
choices.append(
|
| 252 |
+
Choice(
|
| 253 |
+
revision.commit_hash,
|
| 254 |
+
name=(
|
| 255 |
+
f"{revision.commit_hash[:8]}:"
|
| 256 |
+
f" {', '.join(sorted(revision.refs)) or '(detached)'} #"
|
| 257 |
+
f" modified {revision.last_modified_str}"
|
| 258 |
+
),
|
| 259 |
+
enabled=revision.commit_hash in preselected,
|
| 260 |
+
)
|
| 261 |
+
)
|
| 262 |
+
|
| 263 |
+
# Return choices
|
| 264 |
+
return choices
|
| 265 |
+
|
| 266 |
+
|
| 267 |
+
def _manual_review_no_tui(hf_cache_info: HFCacheInfo, preselected: List[str]) -> List[str]:
|
| 268 |
+
"""Ask the user for a manual review of the revisions to delete.
|
| 269 |
+
|
| 270 |
+
Used when TUI is disabled. Manual review happens in a separate tmp file that the
|
| 271 |
+
user can manually edit.
|
| 272 |
+
"""
|
| 273 |
+
# 1. Generate temporary file with delete commands.
|
| 274 |
+
fd, tmp_path = mkstemp(suffix=".txt") # suffix to make it easier to find by editors
|
| 275 |
+
os.close(fd)
|
| 276 |
+
|
| 277 |
+
lines = []
|
| 278 |
+
for repo in sorted(hf_cache_info.repos, key=_repo_sorting_order):
|
| 279 |
+
lines.append(
|
| 280 |
+
f"\n# {repo.repo_type.capitalize()} {repo.repo_id} ({repo.size_on_disk_str},"
|
| 281 |
+
f" used {repo.last_accessed_str})"
|
| 282 |
+
)
|
| 283 |
+
for revision in sorted(repo.revisions, key=_revision_sorting_order):
|
| 284 |
+
lines.append(
|
| 285 |
+
# Deselect by prepending a '#'
|
| 286 |
+
f"{'' if revision.commit_hash in preselected else '#'} "
|
| 287 |
+
f" {revision.commit_hash} # Refs:"
|
| 288 |
+
# Print `refs` as comment on same line
|
| 289 |
+
f" {', '.join(sorted(revision.refs)) or '(detached)'} # modified"
|
| 290 |
+
# Print `last_modified` as comment on same line
|
| 291 |
+
f" {revision.last_modified_str}"
|
| 292 |
+
)
|
| 293 |
+
|
| 294 |
+
with open(tmp_path, "w") as f:
|
| 295 |
+
f.write(_MANUAL_REVIEW_NO_TUI_INSTRUCTIONS)
|
| 296 |
+
f.write("\n".join(lines))
|
| 297 |
+
|
| 298 |
+
# 2. Prompt instructions to user.
|
| 299 |
+
instructions = f"""
|
| 300 |
+
TUI is disabled. In order to select which revisions you want to delete, please edit
|
| 301 |
+
the following file using the text editor of your choice. Instructions for manual
|
| 302 |
+
editing are located at the beginning of the file. Edit the file, save it and confirm
|
| 303 |
+
to continue.
|
| 304 |
+
File to edit: {ANSI.bold(tmp_path)}
|
| 305 |
+
"""
|
| 306 |
+
print("\n".join(line.strip() for line in instructions.strip().split("\n")))
|
| 307 |
+
|
| 308 |
+
# 3. Wait for user confirmation.
|
| 309 |
+
while True:
|
| 310 |
+
selected_hashes = _read_manual_review_tmp_file(tmp_path)
|
| 311 |
+
if _ask_for_confirmation_no_tui(
|
| 312 |
+
_get_expectations_str(hf_cache_info, selected_hashes) + " Continue ?",
|
| 313 |
+
default=False,
|
| 314 |
+
):
|
| 315 |
+
break
|
| 316 |
+
|
| 317 |
+
# 4. Return selected_hashes
|
| 318 |
+
os.remove(tmp_path)
|
| 319 |
+
return selected_hashes
|
| 320 |
+
|
| 321 |
+
|
| 322 |
+
def _ask_for_confirmation_no_tui(message: str, default: bool = True) -> bool:
|
| 323 |
+
"""Ask for confirmation using pure-python."""
|
| 324 |
+
YES = ("y", "yes", "1")
|
| 325 |
+
NO = ("n", "no", "0")
|
| 326 |
+
DEFAULT = ""
|
| 327 |
+
ALL = YES + NO + (DEFAULT,)
|
| 328 |
+
full_message = message + (" (Y/n) " if default else " (y/N) ")
|
| 329 |
+
while True:
|
| 330 |
+
answer = input(full_message).lower()
|
| 331 |
+
if answer == DEFAULT:
|
| 332 |
+
return default
|
| 333 |
+
if answer in YES:
|
| 334 |
+
return True
|
| 335 |
+
if answer in NO:
|
| 336 |
+
return False
|
| 337 |
+
print(f"Invalid input. Must be one of {ALL}")
|
| 338 |
+
|
| 339 |
+
|
| 340 |
+
def _get_expectations_str(hf_cache_info: HFCacheInfo, selected_hashes: List[str]) -> str:
|
| 341 |
+
"""Format a string to display to the user how much space would be saved.
|
| 342 |
+
|
| 343 |
+
Example:
|
| 344 |
+
```
|
| 345 |
+
>>> _get_expectations_str(hf_cache_info, selected_hashes)
|
| 346 |
+
'7 revisions selected counting for 4.3G.'
|
| 347 |
+
```
|
| 348 |
+
"""
|
| 349 |
+
if _CANCEL_DELETION_STR in selected_hashes:
|
| 350 |
+
return "Nothing will be deleted."
|
| 351 |
+
strategy = hf_cache_info.delete_revisions(*selected_hashes)
|
| 352 |
+
return f"{len(selected_hashes)} revisions selected counting for {strategy.expected_freed_size_str}."
|
| 353 |
+
|
| 354 |
+
|
| 355 |
+
def _read_manual_review_tmp_file(tmp_path: str) -> List[str]:
|
| 356 |
+
"""Read the manually reviewed instruction file and return a list of revision hash.
|
| 357 |
+
|
| 358 |
+
Example:
|
| 359 |
+
```txt
|
| 360 |
+
# This is the tmp file content
|
| 361 |
+
###
|
| 362 |
+
|
| 363 |
+
# Commented out line
|
| 364 |
+
123456789 # revision hash
|
| 365 |
+
|
| 366 |
+
# Something else
|
| 367 |
+
# a_newer_hash # 2 days ago
|
| 368 |
+
an_older_hash # 3 days ago
|
| 369 |
+
```
|
| 370 |
+
|
| 371 |
+
```py
|
| 372 |
+
>>> _read_manual_review_tmp_file(tmp_path)
|
| 373 |
+
['123456789', 'an_older_hash']
|
| 374 |
+
```
|
| 375 |
+
"""
|
| 376 |
+
with open(tmp_path) as f:
|
| 377 |
+
content = f.read()
|
| 378 |
+
|
| 379 |
+
# Split lines
|
| 380 |
+
lines = [line.strip() for line in content.split("\n")]
|
| 381 |
+
|
| 382 |
+
# Filter commented lines
|
| 383 |
+
selected_lines = [line for line in lines if not line.startswith("#")]
|
| 384 |
+
|
| 385 |
+
# Select only before comment
|
| 386 |
+
selected_hashes = [line.split("#")[0].strip() for line in selected_lines]
|
| 387 |
+
|
| 388 |
+
# Return revision hashes
|
| 389 |
+
return [hash for hash in selected_hashes if len(hash) > 0]
|
| 390 |
+
|
| 391 |
+
|
| 392 |
+
_MANUAL_REVIEW_NO_TUI_INSTRUCTIONS = f"""
|
| 393 |
+
# INSTRUCTIONS
|
| 394 |
+
# ------------
|
| 395 |
+
# This is a temporary file created by running `huggingface-cli delete-cache` with the
|
| 396 |
+
# `--disable-tui` option. It contains a set of revisions that can be deleted from your
|
| 397 |
+
# local cache directory.
|
| 398 |
+
#
|
| 399 |
+
# Please manually review the revisions you want to delete:
|
| 400 |
+
# - Revision hashes can be commented out with '#'.
|
| 401 |
+
# - Only non-commented revisions in this file will be deleted.
|
| 402 |
+
# - Revision hashes that are removed from this file are ignored as well.
|
| 403 |
+
# - If `{_CANCEL_DELETION_STR}` line is uncommented, the all cache deletion is cancelled and
|
| 404 |
+
# no changes will be applied.
|
| 405 |
+
#
|
| 406 |
+
# Once you've manually reviewed this file, please confirm deletion in the terminal. This
|
| 407 |
+
# file will be automatically removed once done.
|
| 408 |
+
# ------------
|
| 409 |
+
|
| 410 |
+
# KILL SWITCH
|
| 411 |
+
# ------------
|
| 412 |
+
# Un-comment following line to completely cancel the deletion process
|
| 413 |
+
# {_CANCEL_DELETION_STR}
|
| 414 |
+
# ------------
|
| 415 |
+
|
| 416 |
+
# REVISIONS
|
| 417 |
+
# ------------
|
| 418 |
+
""".strip()
|
| 419 |
+
|
| 420 |
+
|
| 421 |
+
def _repo_sorting_order(repo: CachedRepoInfo) -> Any:
|
| 422 |
+
# First split by Dataset/Model, then sort by last accessed (oldest first)
|
| 423 |
+
return (repo.repo_type, repo.last_accessed)
|
| 424 |
+
|
| 425 |
+
|
| 426 |
+
def _revision_sorting_order(revision: CachedRevisionInfo) -> Any:
|
| 427 |
+
# Sort by last modified (oldest first)
|
| 428 |
+
return revision.last_modified
|
.venv/lib/python3.11/site-packages/huggingface_hub/commands/download.py
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# coding=utf-8
|
| 2 |
+
# Copyright 2023-present, the HuggingFace Inc. team.
|
| 3 |
+
#
|
| 4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
+
# you may not use this file except in compliance with the License.
|
| 6 |
+
# You may obtain a copy of the License at
|
| 7 |
+
#
|
| 8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
+
#
|
| 10 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
+
# See the License for the specific language governing permissions and
|
| 14 |
+
# limitations under the License.
|
| 15 |
+
"""Contains command to download files from the Hub with the CLI.
|
| 16 |
+
|
| 17 |
+
Usage:
|
| 18 |
+
huggingface-cli download --help
|
| 19 |
+
|
| 20 |
+
# Download file
|
| 21 |
+
huggingface-cli download gpt2 config.json
|
| 22 |
+
|
| 23 |
+
# Download entire repo
|
| 24 |
+
huggingface-cli download fffiloni/zeroscope --repo-type=space --revision=refs/pr/78
|
| 25 |
+
|
| 26 |
+
# Download repo with filters
|
| 27 |
+
huggingface-cli download gpt2 --include="*.safetensors"
|
| 28 |
+
|
| 29 |
+
# Download with token
|
| 30 |
+
huggingface-cli download Wauplin/private-model --token=hf_***
|
| 31 |
+
|
| 32 |
+
# Download quietly (no progress bar, no warnings, only the returned path)
|
| 33 |
+
huggingface-cli download gpt2 config.json --quiet
|
| 34 |
+
|
| 35 |
+
# Download to local dir
|
| 36 |
+
huggingface-cli download gpt2 --local-dir=./models/gpt2
|
| 37 |
+
"""
|
| 38 |
+
|
| 39 |
+
import warnings
|
| 40 |
+
from argparse import Namespace, _SubParsersAction
|
| 41 |
+
from typing import List, Optional
|
| 42 |
+
|
| 43 |
+
from huggingface_hub import logging
|
| 44 |
+
from huggingface_hub._snapshot_download import snapshot_download
|
| 45 |
+
from huggingface_hub.commands import BaseHuggingfaceCLICommand
|
| 46 |
+
from huggingface_hub.file_download import hf_hub_download
|
| 47 |
+
from huggingface_hub.utils import disable_progress_bars, enable_progress_bars
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
logger = logging.get_logger(__name__)
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
class DownloadCommand(BaseHuggingfaceCLICommand):
|
| 54 |
+
@staticmethod
|
| 55 |
+
def register_subcommand(parser: _SubParsersAction):
|
| 56 |
+
download_parser = parser.add_parser("download", help="Download files from the Hub")
|
| 57 |
+
download_parser.add_argument(
|
| 58 |
+
"repo_id", type=str, help="ID of the repo to download from (e.g. `username/repo-name`)."
|
| 59 |
+
)
|
| 60 |
+
download_parser.add_argument(
|
| 61 |
+
"filenames", type=str, nargs="*", help="Files to download (e.g. `config.json`, `data/metadata.jsonl`)."
|
| 62 |
+
)
|
| 63 |
+
download_parser.add_argument(
|
| 64 |
+
"--repo-type",
|
| 65 |
+
choices=["model", "dataset", "space"],
|
| 66 |
+
default="model",
|
| 67 |
+
help="Type of repo to download from (defaults to 'model').",
|
| 68 |
+
)
|
| 69 |
+
download_parser.add_argument(
|
| 70 |
+
"--revision",
|
| 71 |
+
type=str,
|
| 72 |
+
help="An optional Git revision id which can be a branch name, a tag, or a commit hash.",
|
| 73 |
+
)
|
| 74 |
+
download_parser.add_argument(
|
| 75 |
+
"--include", nargs="*", type=str, help="Glob patterns to match files to download."
|
| 76 |
+
)
|
| 77 |
+
download_parser.add_argument(
|
| 78 |
+
"--exclude", nargs="*", type=str, help="Glob patterns to exclude from files to download."
|
| 79 |
+
)
|
| 80 |
+
download_parser.add_argument(
|
| 81 |
+
"--cache-dir", type=str, help="Path to the directory where to save the downloaded files."
|
| 82 |
+
)
|
| 83 |
+
download_parser.add_argument(
|
| 84 |
+
"--local-dir",
|
| 85 |
+
type=str,
|
| 86 |
+
help=(
|
| 87 |
+
"If set, the downloaded file will be placed under this directory. Check out"
|
| 88 |
+
" https://huggingface.co/docs/huggingface_hub/guides/download#download-files-to-local-folder for more"
|
| 89 |
+
" details."
|
| 90 |
+
),
|
| 91 |
+
)
|
| 92 |
+
download_parser.add_argument(
|
| 93 |
+
"--local-dir-use-symlinks",
|
| 94 |
+
choices=["auto", "True", "False"],
|
| 95 |
+
help=("Deprecated and ignored. Downloading to a local directory does not use symlinks anymore."),
|
| 96 |
+
)
|
| 97 |
+
download_parser.add_argument(
|
| 98 |
+
"--force-download",
|
| 99 |
+
action="store_true",
|
| 100 |
+
help="If True, the files will be downloaded even if they are already cached.",
|
| 101 |
+
)
|
| 102 |
+
download_parser.add_argument(
|
| 103 |
+
"--resume-download",
|
| 104 |
+
action="store_true",
|
| 105 |
+
help="Deprecated and ignored. Downloading a file to local dir always attempts to resume previously interrupted downloads (unless hf-transfer is enabled).",
|
| 106 |
+
)
|
| 107 |
+
download_parser.add_argument(
|
| 108 |
+
"--token", type=str, help="A User Access Token generated from https://huggingface.co/settings/tokens"
|
| 109 |
+
)
|
| 110 |
+
download_parser.add_argument(
|
| 111 |
+
"--quiet",
|
| 112 |
+
action="store_true",
|
| 113 |
+
help="If True, progress bars are disabled and only the path to the download files is printed.",
|
| 114 |
+
)
|
| 115 |
+
download_parser.add_argument(
|
| 116 |
+
"--max-workers",
|
| 117 |
+
type=int,
|
| 118 |
+
default=8,
|
| 119 |
+
help="Maximum number of workers to use for downloading files. Default is 8.",
|
| 120 |
+
)
|
| 121 |
+
download_parser.set_defaults(func=DownloadCommand)
|
| 122 |
+
|
| 123 |
+
def __init__(self, args: Namespace) -> None:
|
| 124 |
+
self.token = args.token
|
| 125 |
+
self.repo_id: str = args.repo_id
|
| 126 |
+
self.filenames: List[str] = args.filenames
|
| 127 |
+
self.repo_type: str = args.repo_type
|
| 128 |
+
self.revision: Optional[str] = args.revision
|
| 129 |
+
self.include: Optional[List[str]] = args.include
|
| 130 |
+
self.exclude: Optional[List[str]] = args.exclude
|
| 131 |
+
self.cache_dir: Optional[str] = args.cache_dir
|
| 132 |
+
self.local_dir: Optional[str] = args.local_dir
|
| 133 |
+
self.force_download: bool = args.force_download
|
| 134 |
+
self.resume_download: Optional[bool] = args.resume_download or None
|
| 135 |
+
self.quiet: bool = args.quiet
|
| 136 |
+
self.max_workers: int = args.max_workers
|
| 137 |
+
|
| 138 |
+
if args.local_dir_use_symlinks is not None:
|
| 139 |
+
warnings.warn(
|
| 140 |
+
"Ignoring --local-dir-use-symlinks. Downloading to a local directory does not use symlinks anymore.",
|
| 141 |
+
FutureWarning,
|
| 142 |
+
)
|
| 143 |
+
|
| 144 |
+
def run(self) -> None:
|
| 145 |
+
if self.quiet:
|
| 146 |
+
disable_progress_bars()
|
| 147 |
+
with warnings.catch_warnings():
|
| 148 |
+
warnings.simplefilter("ignore")
|
| 149 |
+
print(self._download()) # Print path to downloaded files
|
| 150 |
+
enable_progress_bars()
|
| 151 |
+
else:
|
| 152 |
+
logging.set_verbosity_info()
|
| 153 |
+
print(self._download()) # Print path to downloaded files
|
| 154 |
+
logging.set_verbosity_warning()
|
| 155 |
+
|
| 156 |
+
def _download(self) -> str:
|
| 157 |
+
# Warn user if patterns are ignored
|
| 158 |
+
if len(self.filenames) > 0:
|
| 159 |
+
if self.include is not None and len(self.include) > 0:
|
| 160 |
+
warnings.warn("Ignoring `--include` since filenames have being explicitly set.")
|
| 161 |
+
if self.exclude is not None and len(self.exclude) > 0:
|
| 162 |
+
warnings.warn("Ignoring `--exclude` since filenames have being explicitly set.")
|
| 163 |
+
|
| 164 |
+
# Single file to download: use `hf_hub_download`
|
| 165 |
+
if len(self.filenames) == 1:
|
| 166 |
+
return hf_hub_download(
|
| 167 |
+
repo_id=self.repo_id,
|
| 168 |
+
repo_type=self.repo_type,
|
| 169 |
+
revision=self.revision,
|
| 170 |
+
filename=self.filenames[0],
|
| 171 |
+
cache_dir=self.cache_dir,
|
| 172 |
+
resume_download=self.resume_download,
|
| 173 |
+
force_download=self.force_download,
|
| 174 |
+
token=self.token,
|
| 175 |
+
local_dir=self.local_dir,
|
| 176 |
+
library_name="huggingface-cli",
|
| 177 |
+
)
|
| 178 |
+
|
| 179 |
+
# Otherwise: use `snapshot_download` to ensure all files comes from same revision
|
| 180 |
+
elif len(self.filenames) == 0:
|
| 181 |
+
allow_patterns = self.include
|
| 182 |
+
ignore_patterns = self.exclude
|
| 183 |
+
else:
|
| 184 |
+
allow_patterns = self.filenames
|
| 185 |
+
ignore_patterns = None
|
| 186 |
+
|
| 187 |
+
return snapshot_download(
|
| 188 |
+
repo_id=self.repo_id,
|
| 189 |
+
repo_type=self.repo_type,
|
| 190 |
+
revision=self.revision,
|
| 191 |
+
allow_patterns=allow_patterns,
|
| 192 |
+
ignore_patterns=ignore_patterns,
|
| 193 |
+
resume_download=self.resume_download,
|
| 194 |
+
force_download=self.force_download,
|
| 195 |
+
cache_dir=self.cache_dir,
|
| 196 |
+
token=self.token,
|
| 197 |
+
local_dir=self.local_dir,
|
| 198 |
+
library_name="huggingface-cli",
|
| 199 |
+
max_workers=self.max_workers,
|
| 200 |
+
)
|
.venv/lib/python3.11/site-packages/huggingface_hub/commands/env.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2022 The HuggingFace Team. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
"""Contains command to print information about the environment.
|
| 15 |
+
|
| 16 |
+
Usage:
|
| 17 |
+
huggingface-cli env
|
| 18 |
+
"""
|
| 19 |
+
|
| 20 |
+
from argparse import _SubParsersAction
|
| 21 |
+
|
| 22 |
+
from ..utils import dump_environment_info
|
| 23 |
+
from . import BaseHuggingfaceCLICommand
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
class EnvironmentCommand(BaseHuggingfaceCLICommand):
|
| 27 |
+
def __init__(self, args):
|
| 28 |
+
self.args = args
|
| 29 |
+
|
| 30 |
+
@staticmethod
|
| 31 |
+
def register_subcommand(parser: _SubParsersAction):
|
| 32 |
+
env_parser = parser.add_parser("env", help="Print information about the environment.")
|
| 33 |
+
env_parser.set_defaults(func=EnvironmentCommand)
|
| 34 |
+
|
| 35 |
+
def run(self) -> None:
|
| 36 |
+
dump_environment_info()
|
.venv/lib/python3.11/site-packages/huggingface_hub/commands/huggingface_cli.py
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2020 The HuggingFace Team. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
from argparse import ArgumentParser
|
| 16 |
+
|
| 17 |
+
from huggingface_hub.commands.delete_cache import DeleteCacheCommand
|
| 18 |
+
from huggingface_hub.commands.download import DownloadCommand
|
| 19 |
+
from huggingface_hub.commands.env import EnvironmentCommand
|
| 20 |
+
from huggingface_hub.commands.lfs import LfsCommands
|
| 21 |
+
from huggingface_hub.commands.repo_files import RepoFilesCommand
|
| 22 |
+
from huggingface_hub.commands.scan_cache import ScanCacheCommand
|
| 23 |
+
from huggingface_hub.commands.tag import TagCommands
|
| 24 |
+
from huggingface_hub.commands.upload import UploadCommand
|
| 25 |
+
from huggingface_hub.commands.upload_large_folder import UploadLargeFolderCommand
|
| 26 |
+
from huggingface_hub.commands.user import UserCommands
|
| 27 |
+
from huggingface_hub.commands.version import VersionCommand
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
def main():
|
| 31 |
+
parser = ArgumentParser("huggingface-cli", usage="huggingface-cli <command> [<args>]")
|
| 32 |
+
commands_parser = parser.add_subparsers(help="huggingface-cli command helpers")
|
| 33 |
+
|
| 34 |
+
# Register commands
|
| 35 |
+
DownloadCommand.register_subcommand(commands_parser)
|
| 36 |
+
UploadCommand.register_subcommand(commands_parser)
|
| 37 |
+
RepoFilesCommand.register_subcommand(commands_parser)
|
| 38 |
+
EnvironmentCommand.register_subcommand(commands_parser)
|
| 39 |
+
UserCommands.register_subcommand(commands_parser)
|
| 40 |
+
LfsCommands.register_subcommand(commands_parser)
|
| 41 |
+
ScanCacheCommand.register_subcommand(commands_parser)
|
| 42 |
+
DeleteCacheCommand.register_subcommand(commands_parser)
|
| 43 |
+
TagCommands.register_subcommand(commands_parser)
|
| 44 |
+
VersionCommand.register_subcommand(commands_parser)
|
| 45 |
+
|
| 46 |
+
# Experimental
|
| 47 |
+
UploadLargeFolderCommand.register_subcommand(commands_parser)
|
| 48 |
+
|
| 49 |
+
# Let's go
|
| 50 |
+
args = parser.parse_args()
|
| 51 |
+
if not hasattr(args, "func"):
|
| 52 |
+
parser.print_help()
|
| 53 |
+
exit(1)
|
| 54 |
+
|
| 55 |
+
# Run
|
| 56 |
+
service = args.func(args)
|
| 57 |
+
service.run()
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
if __name__ == "__main__":
|
| 61 |
+
main()
|
.venv/lib/python3.11/site-packages/huggingface_hub/commands/lfs.py
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Implementation of a custom transfer agent for the transfer type "multipart" for
|
| 3 |
+
git-lfs.
|
| 4 |
+
|
| 5 |
+
Inspired by:
|
| 6 |
+
github.com/cbartz/git-lfs-swift-transfer-agent/blob/master/git_lfs_swift_transfer.py
|
| 7 |
+
|
| 8 |
+
Spec is: github.com/git-lfs/git-lfs/blob/master/docs/custom-transfers.md
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
To launch debugger while developing:
|
| 12 |
+
|
| 13 |
+
``` [lfs "customtransfer.multipart"]
|
| 14 |
+
path = /path/to/huggingface_hub/.env/bin/python args = -m debugpy --listen 5678
|
| 15 |
+
--wait-for-client
|
| 16 |
+
/path/to/huggingface_hub/src/huggingface_hub/commands/huggingface_cli.py
|
| 17 |
+
lfs-multipart-upload ```"""
|
| 18 |
+
|
| 19 |
+
import json
|
| 20 |
+
import os
|
| 21 |
+
import subprocess
|
| 22 |
+
import sys
|
| 23 |
+
from argparse import _SubParsersAction
|
| 24 |
+
from typing import Dict, List, Optional
|
| 25 |
+
|
| 26 |
+
from huggingface_hub.commands import BaseHuggingfaceCLICommand
|
| 27 |
+
from huggingface_hub.lfs import LFS_MULTIPART_UPLOAD_COMMAND
|
| 28 |
+
|
| 29 |
+
from ..utils import get_session, hf_raise_for_status, logging
|
| 30 |
+
from ..utils._lfs import SliceFileObj
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
logger = logging.get_logger(__name__)
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
class LfsCommands(BaseHuggingfaceCLICommand):
|
| 37 |
+
"""
|
| 38 |
+
Implementation of a custom transfer agent for the transfer type "multipart"
|
| 39 |
+
for git-lfs. This lets users upload large files >5GB 🔥. Spec for LFS custom
|
| 40 |
+
transfer agent is:
|
| 41 |
+
https://github.com/git-lfs/git-lfs/blob/master/docs/custom-transfers.md
|
| 42 |
+
|
| 43 |
+
This introduces two commands to the CLI:
|
| 44 |
+
|
| 45 |
+
1. $ huggingface-cli lfs-enable-largefiles
|
| 46 |
+
|
| 47 |
+
This should be executed once for each model repo that contains a model file
|
| 48 |
+
>5GB. It's documented in the error message you get if you just try to git
|
| 49 |
+
push a 5GB file without having enabled it before.
|
| 50 |
+
|
| 51 |
+
2. $ huggingface-cli lfs-multipart-upload
|
| 52 |
+
|
| 53 |
+
This command is called by lfs directly and is not meant to be called by the
|
| 54 |
+
user.
|
| 55 |
+
"""
|
| 56 |
+
|
| 57 |
+
@staticmethod
|
| 58 |
+
def register_subcommand(parser: _SubParsersAction):
|
| 59 |
+
enable_parser = parser.add_parser(
|
| 60 |
+
"lfs-enable-largefiles", help="Configure your repository to enable upload of files > 5GB."
|
| 61 |
+
)
|
| 62 |
+
enable_parser.add_argument("path", type=str, help="Local path to repository you want to configure.")
|
| 63 |
+
enable_parser.set_defaults(func=lambda args: LfsEnableCommand(args))
|
| 64 |
+
|
| 65 |
+
# Command will get called by git-lfs, do not call it directly.
|
| 66 |
+
upload_parser = parser.add_parser(LFS_MULTIPART_UPLOAD_COMMAND, add_help=False)
|
| 67 |
+
upload_parser.set_defaults(func=lambda args: LfsUploadCommand(args))
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
class LfsEnableCommand:
|
| 71 |
+
def __init__(self, args):
|
| 72 |
+
self.args = args
|
| 73 |
+
|
| 74 |
+
def run(self):
|
| 75 |
+
local_path = os.path.abspath(self.args.path)
|
| 76 |
+
if not os.path.isdir(local_path):
|
| 77 |
+
print("This does not look like a valid git repo.")
|
| 78 |
+
exit(1)
|
| 79 |
+
subprocess.run(
|
| 80 |
+
"git config lfs.customtransfer.multipart.path huggingface-cli".split(),
|
| 81 |
+
check=True,
|
| 82 |
+
cwd=local_path,
|
| 83 |
+
)
|
| 84 |
+
subprocess.run(
|
| 85 |
+
f"git config lfs.customtransfer.multipart.args {LFS_MULTIPART_UPLOAD_COMMAND}".split(),
|
| 86 |
+
check=True,
|
| 87 |
+
cwd=local_path,
|
| 88 |
+
)
|
| 89 |
+
print("Local repo set up for largefiles")
|
| 90 |
+
|
| 91 |
+
|
| 92 |
+
def write_msg(msg: Dict):
|
| 93 |
+
"""Write out the message in Line delimited JSON."""
|
| 94 |
+
msg_str = json.dumps(msg) + "\n"
|
| 95 |
+
sys.stdout.write(msg_str)
|
| 96 |
+
sys.stdout.flush()
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
def read_msg() -> Optional[Dict]:
|
| 100 |
+
"""Read Line delimited JSON from stdin."""
|
| 101 |
+
msg = json.loads(sys.stdin.readline().strip())
|
| 102 |
+
|
| 103 |
+
if "terminate" in (msg.get("type"), msg.get("event")):
|
| 104 |
+
# terminate message received
|
| 105 |
+
return None
|
| 106 |
+
|
| 107 |
+
if msg.get("event") not in ("download", "upload"):
|
| 108 |
+
logger.critical("Received unexpected message")
|
| 109 |
+
sys.exit(1)
|
| 110 |
+
|
| 111 |
+
return msg
|
| 112 |
+
|
| 113 |
+
|
| 114 |
+
class LfsUploadCommand:
|
| 115 |
+
def __init__(self, args) -> None:
|
| 116 |
+
self.args = args
|
| 117 |
+
|
| 118 |
+
def run(self) -> None:
|
| 119 |
+
# Immediately after invoking a custom transfer process, git-lfs
|
| 120 |
+
# sends initiation data to the process over stdin.
|
| 121 |
+
# This tells the process useful information about the configuration.
|
| 122 |
+
init_msg = json.loads(sys.stdin.readline().strip())
|
| 123 |
+
if not (init_msg.get("event") == "init" and init_msg.get("operation") == "upload"):
|
| 124 |
+
write_msg({"error": {"code": 32, "message": "Wrong lfs init operation"}})
|
| 125 |
+
sys.exit(1)
|
| 126 |
+
|
| 127 |
+
# The transfer process should use the information it needs from the
|
| 128 |
+
# initiation structure, and also perform any one-off setup tasks it
|
| 129 |
+
# needs to do. It should then respond on stdout with a simple empty
|
| 130 |
+
# confirmation structure, as follows:
|
| 131 |
+
write_msg({})
|
| 132 |
+
|
| 133 |
+
# After the initiation exchange, git-lfs will send any number of
|
| 134 |
+
# transfer requests to the stdin of the transfer process, in a serial sequence.
|
| 135 |
+
while True:
|
| 136 |
+
msg = read_msg()
|
| 137 |
+
if msg is None:
|
| 138 |
+
# When all transfers have been processed, git-lfs will send
|
| 139 |
+
# a terminate event to the stdin of the transfer process.
|
| 140 |
+
# On receiving this message the transfer process should
|
| 141 |
+
# clean up and terminate. No response is expected.
|
| 142 |
+
sys.exit(0)
|
| 143 |
+
|
| 144 |
+
oid = msg["oid"]
|
| 145 |
+
filepath = msg["path"]
|
| 146 |
+
completion_url = msg["action"]["href"]
|
| 147 |
+
header = msg["action"]["header"]
|
| 148 |
+
chunk_size = int(header.pop("chunk_size"))
|
| 149 |
+
presigned_urls: List[str] = list(header.values())
|
| 150 |
+
|
| 151 |
+
# Send a "started" progress event to allow other workers to start.
|
| 152 |
+
# Otherwise they're delayed until first "progress" event is reported,
|
| 153 |
+
# i.e. after the first 5GB by default (!)
|
| 154 |
+
write_msg(
|
| 155 |
+
{
|
| 156 |
+
"event": "progress",
|
| 157 |
+
"oid": oid,
|
| 158 |
+
"bytesSoFar": 1,
|
| 159 |
+
"bytesSinceLast": 0,
|
| 160 |
+
}
|
| 161 |
+
)
|
| 162 |
+
|
| 163 |
+
parts = []
|
| 164 |
+
with open(filepath, "rb") as file:
|
| 165 |
+
for i, presigned_url in enumerate(presigned_urls):
|
| 166 |
+
with SliceFileObj(
|
| 167 |
+
file,
|
| 168 |
+
seek_from=i * chunk_size,
|
| 169 |
+
read_limit=chunk_size,
|
| 170 |
+
) as data:
|
| 171 |
+
r = get_session().put(presigned_url, data=data)
|
| 172 |
+
hf_raise_for_status(r)
|
| 173 |
+
parts.append(
|
| 174 |
+
{
|
| 175 |
+
"etag": r.headers.get("etag"),
|
| 176 |
+
"partNumber": i + 1,
|
| 177 |
+
}
|
| 178 |
+
)
|
| 179 |
+
# In order to support progress reporting while data is uploading / downloading,
|
| 180 |
+
# the transfer process should post messages to stdout
|
| 181 |
+
write_msg(
|
| 182 |
+
{
|
| 183 |
+
"event": "progress",
|
| 184 |
+
"oid": oid,
|
| 185 |
+
"bytesSoFar": (i + 1) * chunk_size,
|
| 186 |
+
"bytesSinceLast": chunk_size,
|
| 187 |
+
}
|
| 188 |
+
)
|
| 189 |
+
# Not precise but that's ok.
|
| 190 |
+
|
| 191 |
+
r = get_session().post(
|
| 192 |
+
completion_url,
|
| 193 |
+
json={
|
| 194 |
+
"oid": oid,
|
| 195 |
+
"parts": parts,
|
| 196 |
+
},
|
| 197 |
+
)
|
| 198 |
+
hf_raise_for_status(r)
|
| 199 |
+
|
| 200 |
+
write_msg({"event": "complete", "oid": oid})
|
.venv/lib/python3.11/site-packages/huggingface_hub/commands/repo_files.py
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# coding=utf-8
|
| 2 |
+
# Copyright 2023-present, the HuggingFace Inc. team.
|
| 3 |
+
#
|
| 4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
+
# you may not use this file except in compliance with the License.
|
| 6 |
+
# You may obtain a copy of the License at
|
| 7 |
+
#
|
| 8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
+
#
|
| 10 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
+
# See the License for the specific language governing permissions and
|
| 14 |
+
# limitations under the License.
|
| 15 |
+
"""Contains command to update or delete files in a repository using the CLI.
|
| 16 |
+
|
| 17 |
+
Usage:
|
| 18 |
+
# delete all
|
| 19 |
+
huggingface-cli repo-files <repo_id> delete "*"
|
| 20 |
+
|
| 21 |
+
# delete single file
|
| 22 |
+
huggingface-cli repo-files <repo_id> delete file.txt
|
| 23 |
+
|
| 24 |
+
# delete single folder
|
| 25 |
+
huggingface-cli repo-files <repo_id> delete folder/
|
| 26 |
+
|
| 27 |
+
# delete multiple
|
| 28 |
+
huggingface-cli repo-files <repo_id> delete file.txt folder/ file2.txt
|
| 29 |
+
|
| 30 |
+
# delete multiple patterns
|
| 31 |
+
huggingface-cli repo-files <repo_id> delete file.txt "*.json" "folder/*.parquet"
|
| 32 |
+
|
| 33 |
+
# delete from different revision / repo-type
|
| 34 |
+
huggingface-cli repo-files <repo_id> delete file.txt --revision=refs/pr/1 --repo-type=dataset
|
| 35 |
+
"""
|
| 36 |
+
|
| 37 |
+
from argparse import _SubParsersAction
|
| 38 |
+
from typing import List, Optional
|
| 39 |
+
|
| 40 |
+
from huggingface_hub import logging
|
| 41 |
+
from huggingface_hub.commands import BaseHuggingfaceCLICommand
|
| 42 |
+
from huggingface_hub.hf_api import HfApi
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
logger = logging.get_logger(__name__)
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
class DeleteFilesSubCommand:
|
| 49 |
+
def __init__(self, args) -> None:
|
| 50 |
+
self.args = args
|
| 51 |
+
self.repo_id: str = args.repo_id
|
| 52 |
+
self.repo_type: Optional[str] = args.repo_type
|
| 53 |
+
self.revision: Optional[str] = args.revision
|
| 54 |
+
self.api: HfApi = HfApi(token=args.token, library_name="huggingface-cli")
|
| 55 |
+
self.patterns: List[str] = args.patterns
|
| 56 |
+
self.commit_message: Optional[str] = args.commit_message
|
| 57 |
+
self.commit_description: Optional[str] = args.commit_description
|
| 58 |
+
self.create_pr: bool = args.create_pr
|
| 59 |
+
self.token: Optional[str] = args.token
|
| 60 |
+
|
| 61 |
+
def run(self) -> None:
|
| 62 |
+
logging.set_verbosity_info()
|
| 63 |
+
url = self.api.delete_files(
|
| 64 |
+
delete_patterns=self.patterns,
|
| 65 |
+
repo_id=self.repo_id,
|
| 66 |
+
repo_type=self.repo_type,
|
| 67 |
+
revision=self.revision,
|
| 68 |
+
commit_message=self.commit_message,
|
| 69 |
+
commit_description=self.commit_description,
|
| 70 |
+
create_pr=self.create_pr,
|
| 71 |
+
)
|
| 72 |
+
print(f"Files correctly deleted from repo. Commit: {url}.")
|
| 73 |
+
logging.set_verbosity_warning()
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
class RepoFilesCommand(BaseHuggingfaceCLICommand):
|
| 77 |
+
@staticmethod
|
| 78 |
+
def register_subcommand(parser: _SubParsersAction):
|
| 79 |
+
repo_files_parser = parser.add_parser("repo-files", help="Manage files in a repo on the Hub")
|
| 80 |
+
repo_files_parser.add_argument(
|
| 81 |
+
"repo_id", type=str, help="The ID of the repo to manage (e.g. `username/repo-name`)."
|
| 82 |
+
)
|
| 83 |
+
repo_files_subparsers = repo_files_parser.add_subparsers(
|
| 84 |
+
help="Action to execute against the files.",
|
| 85 |
+
required=True,
|
| 86 |
+
)
|
| 87 |
+
delete_subparser = repo_files_subparsers.add_parser(
|
| 88 |
+
"delete",
|
| 89 |
+
help="Delete files from a repo on the Hub",
|
| 90 |
+
)
|
| 91 |
+
delete_subparser.set_defaults(func=lambda args: DeleteFilesSubCommand(args))
|
| 92 |
+
delete_subparser.add_argument(
|
| 93 |
+
"patterns",
|
| 94 |
+
nargs="+",
|
| 95 |
+
type=str,
|
| 96 |
+
help="Glob patterns to match files to delete.",
|
| 97 |
+
)
|
| 98 |
+
delete_subparser.add_argument(
|
| 99 |
+
"--repo-type",
|
| 100 |
+
choices=["model", "dataset", "space"],
|
| 101 |
+
default="model",
|
| 102 |
+
help="Type of the repo to upload to (e.g. `dataset`).",
|
| 103 |
+
)
|
| 104 |
+
delete_subparser.add_argument(
|
| 105 |
+
"--revision",
|
| 106 |
+
type=str,
|
| 107 |
+
help=(
|
| 108 |
+
"An optional Git revision to push to. It can be a branch name "
|
| 109 |
+
"or a PR reference. If revision does not"
|
| 110 |
+
" exist and `--create-pr` is not set, a branch will be automatically created."
|
| 111 |
+
),
|
| 112 |
+
)
|
| 113 |
+
delete_subparser.add_argument(
|
| 114 |
+
"--commit-message", type=str, help="The summary / title / first line of the generated commit."
|
| 115 |
+
)
|
| 116 |
+
delete_subparser.add_argument(
|
| 117 |
+
"--commit-description", type=str, help="The description of the generated commit."
|
| 118 |
+
)
|
| 119 |
+
delete_subparser.add_argument(
|
| 120 |
+
"--create-pr", action="store_true", help="Whether to create a new Pull Request for these changes."
|
| 121 |
+
)
|
| 122 |
+
repo_files_parser.add_argument(
|
| 123 |
+
"--token",
|
| 124 |
+
type=str,
|
| 125 |
+
help="A User Access Token generated from https://huggingface.co/settings/tokens",
|
| 126 |
+
)
|
| 127 |
+
|
| 128 |
+
repo_files_parser.set_defaults(func=RepoFilesCommand)
|
.venv/lib/python3.11/site-packages/huggingface_hub/commands/scan_cache.py
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# coding=utf-8
|
| 2 |
+
# Copyright 2022-present, the HuggingFace Inc. team.
|
| 3 |
+
#
|
| 4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
+
# you may not use this file except in compliance with the License.
|
| 6 |
+
# You may obtain a copy of the License at
|
| 7 |
+
#
|
| 8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
+
#
|
| 10 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
+
# See the License for the specific language governing permissions and
|
| 14 |
+
# limitations under the License.
|
| 15 |
+
"""Contains command to scan the HF cache directory.
|
| 16 |
+
|
| 17 |
+
Usage:
|
| 18 |
+
huggingface-cli scan-cache
|
| 19 |
+
huggingface-cli scan-cache -v
|
| 20 |
+
huggingface-cli scan-cache -vvv
|
| 21 |
+
huggingface-cli scan-cache --dir ~/.cache/huggingface/hub
|
| 22 |
+
"""
|
| 23 |
+
|
| 24 |
+
import time
|
| 25 |
+
from argparse import Namespace, _SubParsersAction
|
| 26 |
+
from typing import Optional
|
| 27 |
+
|
| 28 |
+
from ..utils import CacheNotFound, HFCacheInfo, scan_cache_dir
|
| 29 |
+
from . import BaseHuggingfaceCLICommand
|
| 30 |
+
from ._cli_utils import ANSI, tabulate
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
class ScanCacheCommand(BaseHuggingfaceCLICommand):
|
| 34 |
+
@staticmethod
|
| 35 |
+
def register_subcommand(parser: _SubParsersAction):
|
| 36 |
+
scan_cache_parser = parser.add_parser("scan-cache", help="Scan cache directory.")
|
| 37 |
+
|
| 38 |
+
scan_cache_parser.add_argument(
|
| 39 |
+
"--dir",
|
| 40 |
+
type=str,
|
| 41 |
+
default=None,
|
| 42 |
+
help="cache directory to scan (optional). Default to the default HuggingFace cache.",
|
| 43 |
+
)
|
| 44 |
+
scan_cache_parser.add_argument(
|
| 45 |
+
"-v",
|
| 46 |
+
"--verbose",
|
| 47 |
+
action="count",
|
| 48 |
+
default=0,
|
| 49 |
+
help="show a more verbose output",
|
| 50 |
+
)
|
| 51 |
+
scan_cache_parser.set_defaults(func=ScanCacheCommand)
|
| 52 |
+
|
| 53 |
+
def __init__(self, args: Namespace) -> None:
|
| 54 |
+
self.verbosity: int = args.verbose
|
| 55 |
+
self.cache_dir: Optional[str] = args.dir
|
| 56 |
+
|
| 57 |
+
def run(self):
|
| 58 |
+
try:
|
| 59 |
+
t0 = time.time()
|
| 60 |
+
hf_cache_info = scan_cache_dir(self.cache_dir)
|
| 61 |
+
t1 = time.time()
|
| 62 |
+
except CacheNotFound as exc:
|
| 63 |
+
cache_dir = exc.cache_dir
|
| 64 |
+
print(f"Cache directory not found: {cache_dir}")
|
| 65 |
+
return
|
| 66 |
+
|
| 67 |
+
self._print_hf_cache_info_as_table(hf_cache_info)
|
| 68 |
+
|
| 69 |
+
print(
|
| 70 |
+
f"\nDone in {round(t1 - t0, 1)}s. Scanned {len(hf_cache_info.repos)} repo(s)"
|
| 71 |
+
f" for a total of {ANSI.red(hf_cache_info.size_on_disk_str)}."
|
| 72 |
+
)
|
| 73 |
+
if len(hf_cache_info.warnings) > 0:
|
| 74 |
+
message = f"Got {len(hf_cache_info.warnings)} warning(s) while scanning."
|
| 75 |
+
if self.verbosity >= 3:
|
| 76 |
+
print(ANSI.gray(message))
|
| 77 |
+
for warning in hf_cache_info.warnings:
|
| 78 |
+
print(ANSI.gray(warning))
|
| 79 |
+
else:
|
| 80 |
+
print(ANSI.gray(message + " Use -vvv to print details."))
|
| 81 |
+
|
| 82 |
+
def _print_hf_cache_info_as_table(self, hf_cache_info: HFCacheInfo) -> None:
|
| 83 |
+
print(get_table(hf_cache_info, verbosity=self.verbosity))
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
def get_table(hf_cache_info: HFCacheInfo, *, verbosity: int = 0) -> str:
|
| 87 |
+
"""Generate a table from the [`HFCacheInfo`] object.
|
| 88 |
+
|
| 89 |
+
Pass `verbosity=0` to get a table with a single row per repo, with columns
|
| 90 |
+
"repo_id", "repo_type", "size_on_disk", "nb_files", "last_accessed", "last_modified", "refs", "local_path".
|
| 91 |
+
|
| 92 |
+
Pass `verbosity=1` to get a table with a row per repo and revision (thus multiple rows can appear for a single repo), with columns
|
| 93 |
+
"repo_id", "repo_type", "revision", "size_on_disk", "nb_files", "last_modified", "refs", "local_path".
|
| 94 |
+
|
| 95 |
+
Example:
|
| 96 |
+
```py
|
| 97 |
+
>>> from huggingface_hub.utils import scan_cache_dir
|
| 98 |
+
>>> from huggingface_hub.commands.scan_cache import get_table
|
| 99 |
+
|
| 100 |
+
>>> hf_cache_info = scan_cache_dir()
|
| 101 |
+
HFCacheInfo(...)
|
| 102 |
+
|
| 103 |
+
>>> print(get_table(hf_cache_info, verbosity=0))
|
| 104 |
+
REPO ID REPO TYPE SIZE ON DISK NB FILES LAST_ACCESSED LAST_MODIFIED REFS LOCAL PATH
|
| 105 |
+
--------------------------------------------------- --------- ------------ -------- ------------- ------------- ---- --------------------------------------------------------------------------------------------------
|
| 106 |
+
roberta-base model 2.7M 5 1 day ago 1 week ago main C:\\Users\\admin\\.cache\\huggingface\\hub\\models--roberta-base
|
| 107 |
+
suno/bark model 8.8K 1 1 week ago 1 week ago main C:\\Users\\admin\\.cache\\huggingface\\hub\\models--suno--bark
|
| 108 |
+
t5-base model 893.8M 4 4 days ago 7 months ago main C:\\Users\\admin\\.cache\\huggingface\\hub\\models--t5-base
|
| 109 |
+
t5-large model 3.0G 4 5 weeks ago 5 months ago main C:\\Users\\admin\\.cache\\huggingface\\hub\\models--t5-large
|
| 110 |
+
|
| 111 |
+
>>> print(get_table(hf_cache_info, verbosity=1))
|
| 112 |
+
REPO ID REPO TYPE REVISION SIZE ON DISK NB FILES LAST_MODIFIED REFS LOCAL PATH
|
| 113 |
+
--------------------------------------------------- --------- ---------------------------------------- ------------ -------- ------------- ---- -----------------------------------------------------------------------------------------------------------------------------------------------------
|
| 114 |
+
roberta-base model e2da8e2f811d1448a5b465c236feacd80ffbac7b 2.7M 5 1 week ago main C:\\Users\\admin\\.cache\\huggingface\\hub\\models--roberta-base\\snapshots\\e2da8e2f811d1448a5b465c236feacd80ffbac7b
|
| 115 |
+
suno/bark model 70a8a7d34168586dc5d028fa9666aceade177992 8.8K 1 1 week ago main C:\\Users\\admin\\.cache\\huggingface\\hub\\models--suno--bark\\snapshots\\70a8a7d34168586dc5d028fa9666aceade177992
|
| 116 |
+
t5-base model a9723ea7f1b39c1eae772870f3b547bf6ef7e6c1 893.8M 4 7 months ago main C:\\Users\\admin\\.cache\\huggingface\\hub\\models--t5-base\\snapshots\\a9723ea7f1b39c1eae772870f3b547bf6ef7e6c1
|
| 117 |
+
t5-large model 150ebc2c4b72291e770f58e6057481c8d2ed331a 3.0G 4 5 months ago main C:\\Users\\admin\\.cache\\huggingface\\hub\\models--t5-large\\snapshots\\150ebc2c4b72291e770f58e6057481c8d2ed331a ```
|
| 118 |
+
```
|
| 119 |
+
|
| 120 |
+
Args:
|
| 121 |
+
hf_cache_info ([`HFCacheInfo`]):
|
| 122 |
+
The HFCacheInfo object to print.
|
| 123 |
+
verbosity (`int`, *optional*):
|
| 124 |
+
The verbosity level. Defaults to 0.
|
| 125 |
+
|
| 126 |
+
Returns:
|
| 127 |
+
`str`: The table as a string.
|
| 128 |
+
"""
|
| 129 |
+
if verbosity == 0:
|
| 130 |
+
return tabulate(
|
| 131 |
+
rows=[
|
| 132 |
+
[
|
| 133 |
+
repo.repo_id,
|
| 134 |
+
repo.repo_type,
|
| 135 |
+
"{:>12}".format(repo.size_on_disk_str),
|
| 136 |
+
repo.nb_files,
|
| 137 |
+
repo.last_accessed_str,
|
| 138 |
+
repo.last_modified_str,
|
| 139 |
+
", ".join(sorted(repo.refs)),
|
| 140 |
+
str(repo.repo_path),
|
| 141 |
+
]
|
| 142 |
+
for repo in sorted(hf_cache_info.repos, key=lambda repo: repo.repo_path)
|
| 143 |
+
],
|
| 144 |
+
headers=[
|
| 145 |
+
"REPO ID",
|
| 146 |
+
"REPO TYPE",
|
| 147 |
+
"SIZE ON DISK",
|
| 148 |
+
"NB FILES",
|
| 149 |
+
"LAST_ACCESSED",
|
| 150 |
+
"LAST_MODIFIED",
|
| 151 |
+
"REFS",
|
| 152 |
+
"LOCAL PATH",
|
| 153 |
+
],
|
| 154 |
+
)
|
| 155 |
+
else:
|
| 156 |
+
return tabulate(
|
| 157 |
+
rows=[
|
| 158 |
+
[
|
| 159 |
+
repo.repo_id,
|
| 160 |
+
repo.repo_type,
|
| 161 |
+
revision.commit_hash,
|
| 162 |
+
"{:>12}".format(revision.size_on_disk_str),
|
| 163 |
+
revision.nb_files,
|
| 164 |
+
revision.last_modified_str,
|
| 165 |
+
", ".join(sorted(revision.refs)),
|
| 166 |
+
str(revision.snapshot_path),
|
| 167 |
+
]
|
| 168 |
+
for repo in sorted(hf_cache_info.repos, key=lambda repo: repo.repo_path)
|
| 169 |
+
for revision in sorted(repo.revisions, key=lambda revision: revision.commit_hash)
|
| 170 |
+
],
|
| 171 |
+
headers=[
|
| 172 |
+
"REPO ID",
|
| 173 |
+
"REPO TYPE",
|
| 174 |
+
"REVISION",
|
| 175 |
+
"SIZE ON DISK",
|
| 176 |
+
"NB FILES",
|
| 177 |
+
"LAST_MODIFIED",
|
| 178 |
+
"REFS",
|
| 179 |
+
"LOCAL PATH",
|
| 180 |
+
],
|
| 181 |
+
)
|
.venv/lib/python3.11/site-packages/huggingface_hub/commands/tag.py
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# coding=utf-8
|
| 2 |
+
# Copyright 2024-present, the HuggingFace Inc. team.
|
| 3 |
+
#
|
| 4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
+
# you may not use this file except in compliance with the License.
|
| 6 |
+
# You may obtain a copy of the License at
|
| 7 |
+
#
|
| 8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
+
#
|
| 10 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
+
# See the License for the specific language governing permissions and
|
| 14 |
+
# limitations under the License.
|
| 15 |
+
|
| 16 |
+
"""Contains commands to perform tag management with the CLI.
|
| 17 |
+
|
| 18 |
+
Usage Examples:
|
| 19 |
+
- Create a tag:
|
| 20 |
+
$ huggingface-cli tag user/my-model 1.0 --message "First release"
|
| 21 |
+
$ huggingface-cli tag user/my-model 1.0 -m "First release" --revision develop
|
| 22 |
+
$ huggingface-cli tag user/my-dataset 1.0 -m "First release" --repo-type dataset
|
| 23 |
+
$ huggingface-cli tag user/my-space 1.0
|
| 24 |
+
- List all tags:
|
| 25 |
+
$ huggingface-cli tag -l user/my-model
|
| 26 |
+
$ huggingface-cli tag --list user/my-dataset --repo-type dataset
|
| 27 |
+
- Delete a tag:
|
| 28 |
+
$ huggingface-cli tag -d user/my-model 1.0
|
| 29 |
+
$ huggingface-cli tag --delete user/my-dataset 1.0 --repo-type dataset
|
| 30 |
+
$ huggingface-cli tag -d user/my-space 1.0 -y
|
| 31 |
+
"""
|
| 32 |
+
|
| 33 |
+
from argparse import Namespace, _SubParsersAction
|
| 34 |
+
|
| 35 |
+
from requests.exceptions import HTTPError
|
| 36 |
+
|
| 37 |
+
from huggingface_hub.commands import BaseHuggingfaceCLICommand
|
| 38 |
+
from huggingface_hub.constants import (
|
| 39 |
+
REPO_TYPES,
|
| 40 |
+
)
|
| 41 |
+
from huggingface_hub.hf_api import HfApi
|
| 42 |
+
|
| 43 |
+
from ..errors import HfHubHTTPError, RepositoryNotFoundError, RevisionNotFoundError
|
| 44 |
+
from ._cli_utils import ANSI
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
class TagCommands(BaseHuggingfaceCLICommand):
|
| 48 |
+
@staticmethod
|
| 49 |
+
def register_subcommand(parser: _SubParsersAction):
|
| 50 |
+
tag_parser = parser.add_parser("tag", help="(create, list, delete) tags for a repo in the hub")
|
| 51 |
+
|
| 52 |
+
tag_parser.add_argument("repo_id", type=str, help="The ID of the repo to tag (e.g. `username/repo-name`).")
|
| 53 |
+
tag_parser.add_argument("tag", nargs="?", type=str, help="The name of the tag for creation or deletion.")
|
| 54 |
+
tag_parser.add_argument("-m", "--message", type=str, help="The description of the tag to create.")
|
| 55 |
+
tag_parser.add_argument("--revision", type=str, help="The git revision to tag.")
|
| 56 |
+
tag_parser.add_argument(
|
| 57 |
+
"--token", type=str, help="A User Access Token generated from https://huggingface.co/settings/tokens."
|
| 58 |
+
)
|
| 59 |
+
tag_parser.add_argument(
|
| 60 |
+
"--repo-type",
|
| 61 |
+
choices=["model", "dataset", "space"],
|
| 62 |
+
default="model",
|
| 63 |
+
help="Set the type of repository (model, dataset, or space).",
|
| 64 |
+
)
|
| 65 |
+
tag_parser.add_argument("-y", "--yes", action="store_true", help="Answer Yes to prompts automatically.")
|
| 66 |
+
|
| 67 |
+
tag_parser.add_argument("-l", "--list", action="store_true", help="List tags for a repository.")
|
| 68 |
+
tag_parser.add_argument("-d", "--delete", action="store_true", help="Delete a tag for a repository.")
|
| 69 |
+
|
| 70 |
+
tag_parser.set_defaults(func=lambda args: handle_commands(args))
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
def handle_commands(args: Namespace):
|
| 74 |
+
if args.list:
|
| 75 |
+
return TagListCommand(args)
|
| 76 |
+
elif args.delete:
|
| 77 |
+
return TagDeleteCommand(args)
|
| 78 |
+
else:
|
| 79 |
+
return TagCreateCommand(args)
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
class TagCommand:
|
| 83 |
+
def __init__(self, args: Namespace):
|
| 84 |
+
self.args = args
|
| 85 |
+
self.api = HfApi(token=self.args.token)
|
| 86 |
+
self.repo_id = self.args.repo_id
|
| 87 |
+
self.repo_type = self.args.repo_type
|
| 88 |
+
if self.repo_type not in REPO_TYPES:
|
| 89 |
+
print("Invalid repo --repo-type")
|
| 90 |
+
exit(1)
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
class TagCreateCommand(TagCommand):
|
| 94 |
+
def run(self):
|
| 95 |
+
print(f"You are about to create tag {ANSI.bold(self.args.tag)} on {self.repo_type} {ANSI.bold(self.repo_id)}")
|
| 96 |
+
|
| 97 |
+
try:
|
| 98 |
+
self.api.create_tag(
|
| 99 |
+
repo_id=self.repo_id,
|
| 100 |
+
tag=self.args.tag,
|
| 101 |
+
tag_message=self.args.message,
|
| 102 |
+
revision=self.args.revision,
|
| 103 |
+
repo_type=self.repo_type,
|
| 104 |
+
)
|
| 105 |
+
except RepositoryNotFoundError:
|
| 106 |
+
print(f"{self.repo_type.capitalize()} {ANSI.bold(self.repo_id)} not found.")
|
| 107 |
+
exit(1)
|
| 108 |
+
except RevisionNotFoundError:
|
| 109 |
+
print(f"Revision {ANSI.bold(self.args.revision)} not found.")
|
| 110 |
+
exit(1)
|
| 111 |
+
except HfHubHTTPError as e:
|
| 112 |
+
if e.response.status_code == 409:
|
| 113 |
+
print(f"Tag {ANSI.bold(self.args.tag)} already exists on {ANSI.bold(self.repo_id)}")
|
| 114 |
+
exit(1)
|
| 115 |
+
raise e
|
| 116 |
+
|
| 117 |
+
print(f"Tag {ANSI.bold(self.args.tag)} created on {ANSI.bold(self.repo_id)}")
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
class TagListCommand(TagCommand):
|
| 121 |
+
def run(self):
|
| 122 |
+
try:
|
| 123 |
+
refs = self.api.list_repo_refs(
|
| 124 |
+
repo_id=self.repo_id,
|
| 125 |
+
repo_type=self.repo_type,
|
| 126 |
+
)
|
| 127 |
+
except RepositoryNotFoundError:
|
| 128 |
+
print(f"{self.repo_type.capitalize()} {ANSI.bold(self.repo_id)} not found.")
|
| 129 |
+
exit(1)
|
| 130 |
+
except HTTPError as e:
|
| 131 |
+
print(e)
|
| 132 |
+
print(ANSI.red(e.response.text))
|
| 133 |
+
exit(1)
|
| 134 |
+
if len(refs.tags) == 0:
|
| 135 |
+
print("No tags found")
|
| 136 |
+
exit(0)
|
| 137 |
+
print(f"Tags for {self.repo_type} {ANSI.bold(self.repo_id)}:")
|
| 138 |
+
for tag in refs.tags:
|
| 139 |
+
print(tag.name)
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
class TagDeleteCommand(TagCommand):
|
| 143 |
+
def run(self):
|
| 144 |
+
print(f"You are about to delete tag {ANSI.bold(self.args.tag)} on {self.repo_type} {ANSI.bold(self.repo_id)}")
|
| 145 |
+
|
| 146 |
+
if not self.args.yes:
|
| 147 |
+
choice = input("Proceed? [Y/n] ").lower()
|
| 148 |
+
if choice not in ("", "y", "yes"):
|
| 149 |
+
print("Abort")
|
| 150 |
+
exit()
|
| 151 |
+
try:
|
| 152 |
+
self.api.delete_tag(repo_id=self.repo_id, tag=self.args.tag, repo_type=self.repo_type)
|
| 153 |
+
except RepositoryNotFoundError:
|
| 154 |
+
print(f"{self.repo_type.capitalize()} {ANSI.bold(self.repo_id)} not found.")
|
| 155 |
+
exit(1)
|
| 156 |
+
except RevisionNotFoundError:
|
| 157 |
+
print(f"Tag {ANSI.bold(self.args.tag)} not found on {ANSI.bold(self.repo_id)}")
|
| 158 |
+
exit(1)
|
| 159 |
+
print(f"Tag {ANSI.bold(self.args.tag)} deleted on {ANSI.bold(self.repo_id)}")
|
.venv/lib/python3.11/site-packages/huggingface_hub/commands/upload.py
ADDED
|
@@ -0,0 +1,299 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# coding=utf-8
|
| 2 |
+
# Copyright 2023-present, the HuggingFace Inc. team.
|
| 3 |
+
#
|
| 4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
+
# you may not use this file except in compliance with the License.
|
| 6 |
+
# You may obtain a copy of the License at
|
| 7 |
+
#
|
| 8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
+
#
|
| 10 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
+
# See the License for the specific language governing permissions and
|
| 14 |
+
# limitations under the License.
|
| 15 |
+
"""Contains command to upload a repo or file with the CLI.
|
| 16 |
+
|
| 17 |
+
Usage:
|
| 18 |
+
# Upload file (implicit)
|
| 19 |
+
huggingface-cli upload my-cool-model ./my-cool-model.safetensors
|
| 20 |
+
|
| 21 |
+
# Upload file (explicit)
|
| 22 |
+
huggingface-cli upload my-cool-model ./my-cool-model.safetensors model.safetensors
|
| 23 |
+
|
| 24 |
+
# Upload directory (implicit). If `my-cool-model/` is a directory it will be uploaded, otherwise an exception is raised.
|
| 25 |
+
huggingface-cli upload my-cool-model
|
| 26 |
+
|
| 27 |
+
# Upload directory (explicit)
|
| 28 |
+
huggingface-cli upload my-cool-model ./models/my-cool-model .
|
| 29 |
+
|
| 30 |
+
# Upload filtered directory (example: tensorboard logs except for the last run)
|
| 31 |
+
huggingface-cli upload my-cool-model ./model/training /logs --include "*.tfevents.*" --exclude "*20230905*"
|
| 32 |
+
|
| 33 |
+
# Upload private dataset
|
| 34 |
+
huggingface-cli upload Wauplin/my-cool-dataset ./data . --repo-type=dataset --private
|
| 35 |
+
|
| 36 |
+
# Upload with token
|
| 37 |
+
huggingface-cli upload Wauplin/my-cool-model --token=hf_****
|
| 38 |
+
|
| 39 |
+
# Sync local Space with Hub (upload new files, delete removed files)
|
| 40 |
+
huggingface-cli upload Wauplin/space-example --repo-type=space --exclude="/logs/*" --delete="*" --commit-message="Sync local Space with Hub"
|
| 41 |
+
|
| 42 |
+
# Schedule commits every 30 minutes
|
| 43 |
+
huggingface-cli upload Wauplin/my-cool-model --every=30
|
| 44 |
+
"""
|
| 45 |
+
|
| 46 |
+
import os
|
| 47 |
+
import time
|
| 48 |
+
import warnings
|
| 49 |
+
from argparse import Namespace, _SubParsersAction
|
| 50 |
+
from typing import List, Optional
|
| 51 |
+
|
| 52 |
+
from huggingface_hub import logging
|
| 53 |
+
from huggingface_hub._commit_scheduler import CommitScheduler
|
| 54 |
+
from huggingface_hub.commands import BaseHuggingfaceCLICommand
|
| 55 |
+
from huggingface_hub.constants import HF_HUB_ENABLE_HF_TRANSFER
|
| 56 |
+
from huggingface_hub.errors import RevisionNotFoundError
|
| 57 |
+
from huggingface_hub.hf_api import HfApi
|
| 58 |
+
from huggingface_hub.utils import disable_progress_bars, enable_progress_bars
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
logger = logging.get_logger(__name__)
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
class UploadCommand(BaseHuggingfaceCLICommand):
|
| 65 |
+
@staticmethod
|
| 66 |
+
def register_subcommand(parser: _SubParsersAction):
|
| 67 |
+
upload_parser = parser.add_parser("upload", help="Upload a file or a folder to a repo on the Hub")
|
| 68 |
+
upload_parser.add_argument(
|
| 69 |
+
"repo_id", type=str, help="The ID of the repo to upload to (e.g. `username/repo-name`)."
|
| 70 |
+
)
|
| 71 |
+
upload_parser.add_argument(
|
| 72 |
+
"local_path", nargs="?", help="Local path to the file or folder to upload. Defaults to current directory."
|
| 73 |
+
)
|
| 74 |
+
upload_parser.add_argument(
|
| 75 |
+
"path_in_repo",
|
| 76 |
+
nargs="?",
|
| 77 |
+
help="Path of the file or folder in the repo. Defaults to the relative path of the file or folder.",
|
| 78 |
+
)
|
| 79 |
+
upload_parser.add_argument(
|
| 80 |
+
"--repo-type",
|
| 81 |
+
choices=["model", "dataset", "space"],
|
| 82 |
+
default="model",
|
| 83 |
+
help="Type of the repo to upload to (e.g. `dataset`).",
|
| 84 |
+
)
|
| 85 |
+
upload_parser.add_argument(
|
| 86 |
+
"--revision",
|
| 87 |
+
type=str,
|
| 88 |
+
help=(
|
| 89 |
+
"An optional Git revision to push to. It can be a branch name or a PR reference. If revision does not"
|
| 90 |
+
" exist and `--create-pr` is not set, a branch will be automatically created."
|
| 91 |
+
),
|
| 92 |
+
)
|
| 93 |
+
upload_parser.add_argument(
|
| 94 |
+
"--private",
|
| 95 |
+
action="store_true",
|
| 96 |
+
help=(
|
| 97 |
+
"Whether to create a private repo if repo doesn't exist on the Hub. Ignored if the repo already"
|
| 98 |
+
" exists."
|
| 99 |
+
),
|
| 100 |
+
)
|
| 101 |
+
upload_parser.add_argument("--include", nargs="*", type=str, help="Glob patterns to match files to upload.")
|
| 102 |
+
upload_parser.add_argument(
|
| 103 |
+
"--exclude", nargs="*", type=str, help="Glob patterns to exclude from files to upload."
|
| 104 |
+
)
|
| 105 |
+
upload_parser.add_argument(
|
| 106 |
+
"--delete",
|
| 107 |
+
nargs="*",
|
| 108 |
+
type=str,
|
| 109 |
+
help="Glob patterns for file to be deleted from the repo while committing.",
|
| 110 |
+
)
|
| 111 |
+
upload_parser.add_argument(
|
| 112 |
+
"--commit-message", type=str, help="The summary / title / first line of the generated commit."
|
| 113 |
+
)
|
| 114 |
+
upload_parser.add_argument("--commit-description", type=str, help="The description of the generated commit.")
|
| 115 |
+
upload_parser.add_argument(
|
| 116 |
+
"--create-pr", action="store_true", help="Whether to upload content as a new Pull Request."
|
| 117 |
+
)
|
| 118 |
+
upload_parser.add_argument(
|
| 119 |
+
"--every",
|
| 120 |
+
type=float,
|
| 121 |
+
help="If set, a background job is scheduled to create commits every `every` minutes.",
|
| 122 |
+
)
|
| 123 |
+
upload_parser.add_argument(
|
| 124 |
+
"--token", type=str, help="A User Access Token generated from https://huggingface.co/settings/tokens"
|
| 125 |
+
)
|
| 126 |
+
upload_parser.add_argument(
|
| 127 |
+
"--quiet",
|
| 128 |
+
action="store_true",
|
| 129 |
+
help="If True, progress bars are disabled and only the path to the uploaded files is printed.",
|
| 130 |
+
)
|
| 131 |
+
upload_parser.set_defaults(func=UploadCommand)
|
| 132 |
+
|
| 133 |
+
def __init__(self, args: Namespace) -> None:
|
| 134 |
+
self.repo_id: str = args.repo_id
|
| 135 |
+
self.repo_type: Optional[str] = args.repo_type
|
| 136 |
+
self.revision: Optional[str] = args.revision
|
| 137 |
+
self.private: bool = args.private
|
| 138 |
+
|
| 139 |
+
self.include: Optional[List[str]] = args.include
|
| 140 |
+
self.exclude: Optional[List[str]] = args.exclude
|
| 141 |
+
self.delete: Optional[List[str]] = args.delete
|
| 142 |
+
|
| 143 |
+
self.commit_message: Optional[str] = args.commit_message
|
| 144 |
+
self.commit_description: Optional[str] = args.commit_description
|
| 145 |
+
self.create_pr: bool = args.create_pr
|
| 146 |
+
self.api: HfApi = HfApi(token=args.token, library_name="huggingface-cli")
|
| 147 |
+
self.quiet: bool = args.quiet # disable warnings and progress bars
|
| 148 |
+
|
| 149 |
+
# Check `--every` is valid
|
| 150 |
+
if args.every is not None and args.every <= 0:
|
| 151 |
+
raise ValueError(f"`every` must be a positive value (got '{args.every}')")
|
| 152 |
+
self.every: Optional[float] = args.every
|
| 153 |
+
|
| 154 |
+
# Resolve `local_path` and `path_in_repo`
|
| 155 |
+
repo_name: str = args.repo_id.split("/")[-1] # e.g. "Wauplin/my-cool-model" => "my-cool-model"
|
| 156 |
+
self.local_path: str
|
| 157 |
+
self.path_in_repo: str
|
| 158 |
+
if args.local_path is None and os.path.isfile(repo_name):
|
| 159 |
+
# Implicit case 1: user provided only a repo_id which happen to be a local file as well => upload it with same name
|
| 160 |
+
self.local_path = repo_name
|
| 161 |
+
self.path_in_repo = repo_name
|
| 162 |
+
elif args.local_path is None and os.path.isdir(repo_name):
|
| 163 |
+
# Implicit case 2: user provided only a repo_id which happen to be a local folder as well => upload it at root
|
| 164 |
+
self.local_path = repo_name
|
| 165 |
+
self.path_in_repo = "."
|
| 166 |
+
elif args.local_path is None:
|
| 167 |
+
# Implicit case 3: user provided only a repo_id that does not match a local file or folder
|
| 168 |
+
# => the user must explicitly provide a local_path => raise exception
|
| 169 |
+
raise ValueError(f"'{repo_name}' is not a local file or folder. Please set `local_path` explicitly.")
|
| 170 |
+
elif args.path_in_repo is None and os.path.isfile(args.local_path):
|
| 171 |
+
# Explicit local path to file, no path in repo => upload it at root with same name
|
| 172 |
+
self.local_path = args.local_path
|
| 173 |
+
self.path_in_repo = os.path.basename(args.local_path)
|
| 174 |
+
elif args.path_in_repo is None:
|
| 175 |
+
# Explicit local path to folder, no path in repo => upload at root
|
| 176 |
+
self.local_path = args.local_path
|
| 177 |
+
self.path_in_repo = "."
|
| 178 |
+
else:
|
| 179 |
+
# Finally, if both paths are explicit
|
| 180 |
+
self.local_path = args.local_path
|
| 181 |
+
self.path_in_repo = args.path_in_repo
|
| 182 |
+
|
| 183 |
+
def run(self) -> None:
|
| 184 |
+
if self.quiet:
|
| 185 |
+
disable_progress_bars()
|
| 186 |
+
with warnings.catch_warnings():
|
| 187 |
+
warnings.simplefilter("ignore")
|
| 188 |
+
print(self._upload())
|
| 189 |
+
enable_progress_bars()
|
| 190 |
+
else:
|
| 191 |
+
logging.set_verbosity_info()
|
| 192 |
+
print(self._upload())
|
| 193 |
+
logging.set_verbosity_warning()
|
| 194 |
+
|
| 195 |
+
def _upload(self) -> str:
|
| 196 |
+
if os.path.isfile(self.local_path):
|
| 197 |
+
if self.include is not None and len(self.include) > 0:
|
| 198 |
+
warnings.warn("Ignoring `--include` since a single file is uploaded.")
|
| 199 |
+
if self.exclude is not None and len(self.exclude) > 0:
|
| 200 |
+
warnings.warn("Ignoring `--exclude` since a single file is uploaded.")
|
| 201 |
+
if self.delete is not None and len(self.delete) > 0:
|
| 202 |
+
warnings.warn("Ignoring `--delete` since a single file is uploaded.")
|
| 203 |
+
|
| 204 |
+
if not HF_HUB_ENABLE_HF_TRANSFER:
|
| 205 |
+
logger.info(
|
| 206 |
+
"Consider using `hf_transfer` for faster uploads. This solution comes with some limitations. See"
|
| 207 |
+
" https://huggingface.co/docs/huggingface_hub/hf_transfer for more details."
|
| 208 |
+
)
|
| 209 |
+
|
| 210 |
+
# Schedule commits if `every` is set
|
| 211 |
+
if self.every is not None:
|
| 212 |
+
if os.path.isfile(self.local_path):
|
| 213 |
+
# If file => watch entire folder + use allow_patterns
|
| 214 |
+
folder_path = os.path.dirname(self.local_path)
|
| 215 |
+
path_in_repo = (
|
| 216 |
+
self.path_in_repo[: -len(self.local_path)] # remove filename from path_in_repo
|
| 217 |
+
if self.path_in_repo.endswith(self.local_path)
|
| 218 |
+
else self.path_in_repo
|
| 219 |
+
)
|
| 220 |
+
allow_patterns = [self.local_path]
|
| 221 |
+
ignore_patterns = []
|
| 222 |
+
else:
|
| 223 |
+
folder_path = self.local_path
|
| 224 |
+
path_in_repo = self.path_in_repo
|
| 225 |
+
allow_patterns = self.include or []
|
| 226 |
+
ignore_patterns = self.exclude or []
|
| 227 |
+
if self.delete is not None and len(self.delete) > 0:
|
| 228 |
+
warnings.warn("Ignoring `--delete` when uploading with scheduled commits.")
|
| 229 |
+
|
| 230 |
+
scheduler = CommitScheduler(
|
| 231 |
+
folder_path=folder_path,
|
| 232 |
+
repo_id=self.repo_id,
|
| 233 |
+
repo_type=self.repo_type,
|
| 234 |
+
revision=self.revision,
|
| 235 |
+
allow_patterns=allow_patterns,
|
| 236 |
+
ignore_patterns=ignore_patterns,
|
| 237 |
+
path_in_repo=path_in_repo,
|
| 238 |
+
private=self.private,
|
| 239 |
+
every=self.every,
|
| 240 |
+
hf_api=self.api,
|
| 241 |
+
)
|
| 242 |
+
print(f"Scheduling commits every {self.every} minutes to {scheduler.repo_id}.")
|
| 243 |
+
try: # Block main thread until KeyboardInterrupt
|
| 244 |
+
while True:
|
| 245 |
+
time.sleep(100)
|
| 246 |
+
except KeyboardInterrupt:
|
| 247 |
+
scheduler.stop()
|
| 248 |
+
return "Stopped scheduled commits."
|
| 249 |
+
|
| 250 |
+
# Otherwise, create repo and proceed with the upload
|
| 251 |
+
if not os.path.isfile(self.local_path) and not os.path.isdir(self.local_path):
|
| 252 |
+
raise FileNotFoundError(f"No such file or directory: '{self.local_path}'.")
|
| 253 |
+
repo_id = self.api.create_repo(
|
| 254 |
+
repo_id=self.repo_id,
|
| 255 |
+
repo_type=self.repo_type,
|
| 256 |
+
exist_ok=True,
|
| 257 |
+
private=self.private,
|
| 258 |
+
space_sdk="gradio" if self.repo_type == "space" else None,
|
| 259 |
+
# ^ We don't want it to fail when uploading to a Space => let's set Gradio by default.
|
| 260 |
+
# ^ I'd rather not add CLI args to set it explicitly as we already have `huggingface-cli repo create` for that.
|
| 261 |
+
).repo_id
|
| 262 |
+
|
| 263 |
+
# Check if branch already exists and if not, create it
|
| 264 |
+
if self.revision is not None and not self.create_pr:
|
| 265 |
+
try:
|
| 266 |
+
self.api.repo_info(repo_id=repo_id, repo_type=self.repo_type, revision=self.revision)
|
| 267 |
+
except RevisionNotFoundError:
|
| 268 |
+
logger.info(f"Branch '{self.revision}' not found. Creating it...")
|
| 269 |
+
self.api.create_branch(repo_id=repo_id, repo_type=self.repo_type, branch=self.revision, exist_ok=True)
|
| 270 |
+
# ^ `exist_ok=True` to avoid race concurrency issues
|
| 271 |
+
|
| 272 |
+
# File-based upload
|
| 273 |
+
if os.path.isfile(self.local_path):
|
| 274 |
+
return self.api.upload_file(
|
| 275 |
+
path_or_fileobj=self.local_path,
|
| 276 |
+
path_in_repo=self.path_in_repo,
|
| 277 |
+
repo_id=repo_id,
|
| 278 |
+
repo_type=self.repo_type,
|
| 279 |
+
revision=self.revision,
|
| 280 |
+
commit_message=self.commit_message,
|
| 281 |
+
commit_description=self.commit_description,
|
| 282 |
+
create_pr=self.create_pr,
|
| 283 |
+
)
|
| 284 |
+
|
| 285 |
+
# Folder-based upload
|
| 286 |
+
else:
|
| 287 |
+
return self.api.upload_folder(
|
| 288 |
+
folder_path=self.local_path,
|
| 289 |
+
path_in_repo=self.path_in_repo,
|
| 290 |
+
repo_id=repo_id,
|
| 291 |
+
repo_type=self.repo_type,
|
| 292 |
+
revision=self.revision,
|
| 293 |
+
commit_message=self.commit_message,
|
| 294 |
+
commit_description=self.commit_description,
|
| 295 |
+
create_pr=self.create_pr,
|
| 296 |
+
allow_patterns=self.include,
|
| 297 |
+
ignore_patterns=self.exclude,
|
| 298 |
+
delete_patterns=self.delete,
|
| 299 |
+
)
|
.venv/lib/python3.11/site-packages/huggingface_hub/commands/upload_large_folder.py
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# coding=utf-8
|
| 2 |
+
# Copyright 2023-present, the HuggingFace Inc. team.
|
| 3 |
+
#
|
| 4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
+
# you may not use this file except in compliance with the License.
|
| 6 |
+
# You may obtain a copy of the License at
|
| 7 |
+
#
|
| 8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
+
#
|
| 10 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
+
# See the License for the specific language governing permissions and
|
| 14 |
+
# limitations under the License.
|
| 15 |
+
"""Contains command to upload a large folder with the CLI."""
|
| 16 |
+
|
| 17 |
+
import os
|
| 18 |
+
from argparse import Namespace, _SubParsersAction
|
| 19 |
+
from typing import List, Optional
|
| 20 |
+
|
| 21 |
+
from huggingface_hub import logging
|
| 22 |
+
from huggingface_hub.commands import BaseHuggingfaceCLICommand
|
| 23 |
+
from huggingface_hub.hf_api import HfApi
|
| 24 |
+
from huggingface_hub.utils import disable_progress_bars
|
| 25 |
+
|
| 26 |
+
from ._cli_utils import ANSI
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
logger = logging.get_logger(__name__)
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
class UploadLargeFolderCommand(BaseHuggingfaceCLICommand):
|
| 33 |
+
@staticmethod
|
| 34 |
+
def register_subcommand(parser: _SubParsersAction):
|
| 35 |
+
subparser = parser.add_parser("upload-large-folder", help="Upload a large folder to a repo on the Hub")
|
| 36 |
+
subparser.add_argument(
|
| 37 |
+
"repo_id", type=str, help="The ID of the repo to upload to (e.g. `username/repo-name`)."
|
| 38 |
+
)
|
| 39 |
+
subparser.add_argument("local_path", type=str, help="Local path to the file or folder to upload.")
|
| 40 |
+
subparser.add_argument(
|
| 41 |
+
"--repo-type",
|
| 42 |
+
choices=["model", "dataset", "space"],
|
| 43 |
+
help="Type of the repo to upload to (e.g. `dataset`).",
|
| 44 |
+
)
|
| 45 |
+
subparser.add_argument(
|
| 46 |
+
"--revision",
|
| 47 |
+
type=str,
|
| 48 |
+
help=("An optional Git revision to push to. It can be a branch name or a PR reference."),
|
| 49 |
+
)
|
| 50 |
+
subparser.add_argument(
|
| 51 |
+
"--private",
|
| 52 |
+
action="store_true",
|
| 53 |
+
help=(
|
| 54 |
+
"Whether to create a private repo if repo doesn't exist on the Hub. Ignored if the repo already exists."
|
| 55 |
+
),
|
| 56 |
+
)
|
| 57 |
+
subparser.add_argument("--include", nargs="*", type=str, help="Glob patterns to match files to upload.")
|
| 58 |
+
subparser.add_argument("--exclude", nargs="*", type=str, help="Glob patterns to exclude from files to upload.")
|
| 59 |
+
subparser.add_argument(
|
| 60 |
+
"--token", type=str, help="A User Access Token generated from https://huggingface.co/settings/tokens"
|
| 61 |
+
)
|
| 62 |
+
subparser.add_argument(
|
| 63 |
+
"--num-workers", type=int, help="Number of workers to use to hash, upload and commit files."
|
| 64 |
+
)
|
| 65 |
+
subparser.add_argument("--no-report", action="store_true", help="Whether to disable regular status report.")
|
| 66 |
+
subparser.add_argument("--no-bars", action="store_true", help="Whether to disable progress bars.")
|
| 67 |
+
subparser.set_defaults(func=UploadLargeFolderCommand)
|
| 68 |
+
|
| 69 |
+
def __init__(self, args: Namespace) -> None:
|
| 70 |
+
self.repo_id: str = args.repo_id
|
| 71 |
+
self.local_path: str = args.local_path
|
| 72 |
+
self.repo_type: str = args.repo_type
|
| 73 |
+
self.revision: Optional[str] = args.revision
|
| 74 |
+
self.private: bool = args.private
|
| 75 |
+
|
| 76 |
+
self.include: Optional[List[str]] = args.include
|
| 77 |
+
self.exclude: Optional[List[str]] = args.exclude
|
| 78 |
+
|
| 79 |
+
self.api: HfApi = HfApi(token=args.token, library_name="huggingface-cli")
|
| 80 |
+
|
| 81 |
+
self.num_workers: Optional[int] = args.num_workers
|
| 82 |
+
self.no_report: bool = args.no_report
|
| 83 |
+
self.no_bars: bool = args.no_bars
|
| 84 |
+
|
| 85 |
+
if not os.path.isdir(self.local_path):
|
| 86 |
+
raise ValueError("Large upload is only supported for folders.")
|
| 87 |
+
|
| 88 |
+
def run(self) -> None:
|
| 89 |
+
logging.set_verbosity_info()
|
| 90 |
+
|
| 91 |
+
print(
|
| 92 |
+
ANSI.yellow(
|
| 93 |
+
"You are about to upload a large folder to the Hub using `huggingface-cli upload-large-folder`. "
|
| 94 |
+
"This is a new feature so feedback is very welcome!\n"
|
| 95 |
+
"\n"
|
| 96 |
+
"A few things to keep in mind:\n"
|
| 97 |
+
" - Repository limits still apply: https://huggingface.co/docs/hub/repositories-recommendations\n"
|
| 98 |
+
" - Do not start several processes in parallel.\n"
|
| 99 |
+
" - You can interrupt and resume the process at any time. "
|
| 100 |
+
"The script will pick up where it left off except for partially uploaded files that would have to be entirely reuploaded.\n"
|
| 101 |
+
" - Do not upload the same folder to several repositories. If you need to do so, you must delete the `./.cache/huggingface/` folder first.\n"
|
| 102 |
+
"\n"
|
| 103 |
+
f"Some temporary metadata will be stored under `{self.local_path}/.cache/huggingface`.\n"
|
| 104 |
+
" - You must not modify those files manually.\n"
|
| 105 |
+
" - You must not delete the `./.cache/huggingface/` folder while a process is running.\n"
|
| 106 |
+
" - You can delete the `./.cache/huggingface/` folder to reinitialize the upload state when process is not running. Files will have to be hashed and preuploaded again, except for already committed files.\n"
|
| 107 |
+
"\n"
|
| 108 |
+
"If the process output is too verbose, you can disable the progress bars with `--no-bars`. "
|
| 109 |
+
"You can also entirely disable the status report with `--no-report`.\n"
|
| 110 |
+
"\n"
|
| 111 |
+
"For more details, run `huggingface-cli upload-large-folder --help` or check the documentation at "
|
| 112 |
+
"https://huggingface.co/docs/huggingface_hub/guides/upload#upload-a-large-folder."
|
| 113 |
+
)
|
| 114 |
+
)
|
| 115 |
+
|
| 116 |
+
if self.no_bars:
|
| 117 |
+
disable_progress_bars()
|
| 118 |
+
|
| 119 |
+
self.api.upload_large_folder(
|
| 120 |
+
repo_id=self.repo_id,
|
| 121 |
+
folder_path=self.local_path,
|
| 122 |
+
repo_type=self.repo_type,
|
| 123 |
+
revision=self.revision,
|
| 124 |
+
private=self.private,
|
| 125 |
+
allow_patterns=self.include,
|
| 126 |
+
ignore_patterns=self.exclude,
|
| 127 |
+
num_workers=self.num_workers,
|
| 128 |
+
print_report=not self.no_report,
|
| 129 |
+
)
|
.venv/lib/python3.11/site-packages/huggingface_hub/commands/user.py
ADDED
|
@@ -0,0 +1,304 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2020 The HuggingFace Team. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
"""Contains commands to authenticate to the Hugging Face Hub and interact with your repositories.
|
| 15 |
+
|
| 16 |
+
Usage:
|
| 17 |
+
# login and save token locally.
|
| 18 |
+
huggingface-cli login --token=hf_*** --add-to-git-credential
|
| 19 |
+
|
| 20 |
+
# switch between tokens
|
| 21 |
+
huggingface-cli auth switch
|
| 22 |
+
|
| 23 |
+
# list all tokens
|
| 24 |
+
huggingface-cli auth list
|
| 25 |
+
|
| 26 |
+
# logout from a specific token, if no token-name is provided, all tokens will be deleted from your machine.
|
| 27 |
+
huggingface-cli logout --token-name=your_token_name
|
| 28 |
+
|
| 29 |
+
# find out which huggingface.co account you are logged in as
|
| 30 |
+
huggingface-cli whoami
|
| 31 |
+
|
| 32 |
+
# create a new dataset repo on the Hub
|
| 33 |
+
huggingface-cli repo create mydataset --type=dataset
|
| 34 |
+
|
| 35 |
+
"""
|
| 36 |
+
|
| 37 |
+
import subprocess
|
| 38 |
+
from argparse import _SubParsersAction
|
| 39 |
+
from typing import List, Optional
|
| 40 |
+
|
| 41 |
+
from requests.exceptions import HTTPError
|
| 42 |
+
|
| 43 |
+
from huggingface_hub.commands import BaseHuggingfaceCLICommand
|
| 44 |
+
from huggingface_hub.constants import ENDPOINT, REPO_TYPES, REPO_TYPES_URL_PREFIXES, SPACES_SDK_TYPES
|
| 45 |
+
from huggingface_hub.hf_api import HfApi
|
| 46 |
+
|
| 47 |
+
from .._login import ( # noqa: F401 # for backward compatibility # noqa: F401 # for backward compatibility
|
| 48 |
+
NOTEBOOK_LOGIN_PASSWORD_HTML,
|
| 49 |
+
NOTEBOOK_LOGIN_TOKEN_HTML_END,
|
| 50 |
+
NOTEBOOK_LOGIN_TOKEN_HTML_START,
|
| 51 |
+
auth_list,
|
| 52 |
+
auth_switch,
|
| 53 |
+
login,
|
| 54 |
+
logout,
|
| 55 |
+
notebook_login,
|
| 56 |
+
)
|
| 57 |
+
from ..utils import get_stored_tokens, get_token, logging
|
| 58 |
+
from ._cli_utils import ANSI
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
logger = logging.get_logger(__name__)
|
| 62 |
+
|
| 63 |
+
try:
|
| 64 |
+
from InquirerPy import inquirer
|
| 65 |
+
from InquirerPy.base.control import Choice
|
| 66 |
+
|
| 67 |
+
_inquirer_py_available = True
|
| 68 |
+
except ImportError:
|
| 69 |
+
_inquirer_py_available = False
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
class UserCommands(BaseHuggingfaceCLICommand):
|
| 73 |
+
@staticmethod
|
| 74 |
+
def register_subcommand(parser: _SubParsersAction):
|
| 75 |
+
login_parser = parser.add_parser("login", help="Log in using a token from huggingface.co/settings/tokens")
|
| 76 |
+
login_parser.add_argument(
|
| 77 |
+
"--token",
|
| 78 |
+
type=str,
|
| 79 |
+
help="Token generated from https://huggingface.co/settings/tokens",
|
| 80 |
+
)
|
| 81 |
+
login_parser.add_argument(
|
| 82 |
+
"--add-to-git-credential",
|
| 83 |
+
action="store_true",
|
| 84 |
+
help="Optional: Save token to git credential helper.",
|
| 85 |
+
)
|
| 86 |
+
login_parser.set_defaults(func=lambda args: LoginCommand(args))
|
| 87 |
+
whoami_parser = parser.add_parser("whoami", help="Find out which huggingface.co account you are logged in as.")
|
| 88 |
+
whoami_parser.set_defaults(func=lambda args: WhoamiCommand(args))
|
| 89 |
+
|
| 90 |
+
logout_parser = parser.add_parser("logout", help="Log out")
|
| 91 |
+
logout_parser.add_argument(
|
| 92 |
+
"--token-name",
|
| 93 |
+
type=str,
|
| 94 |
+
help="Optional: Name of the access token to log out from.",
|
| 95 |
+
)
|
| 96 |
+
logout_parser.set_defaults(func=lambda args: LogoutCommand(args))
|
| 97 |
+
|
| 98 |
+
auth_parser = parser.add_parser("auth", help="Other authentication related commands")
|
| 99 |
+
auth_subparsers = auth_parser.add_subparsers(help="Authentication subcommands")
|
| 100 |
+
auth_switch_parser = auth_subparsers.add_parser("switch", help="Switch between access tokens")
|
| 101 |
+
auth_switch_parser.add_argument(
|
| 102 |
+
"--token-name",
|
| 103 |
+
type=str,
|
| 104 |
+
help="Optional: Name of the access token to switch to.",
|
| 105 |
+
)
|
| 106 |
+
auth_switch_parser.add_argument(
|
| 107 |
+
"--add-to-git-credential",
|
| 108 |
+
action="store_true",
|
| 109 |
+
help="Optional: Save token to git credential helper.",
|
| 110 |
+
)
|
| 111 |
+
auth_switch_parser.set_defaults(func=lambda args: AuthSwitchCommand(args))
|
| 112 |
+
auth_list_parser = auth_subparsers.add_parser("list", help="List all stored access tokens")
|
| 113 |
+
auth_list_parser.set_defaults(func=lambda args: AuthListCommand(args))
|
| 114 |
+
# new system: git-based repo system
|
| 115 |
+
repo_parser = parser.add_parser("repo", help="{create} Commands to interact with your huggingface.co repos.")
|
| 116 |
+
repo_subparsers = repo_parser.add_subparsers(help="huggingface.co repos related commands")
|
| 117 |
+
repo_create_parser = repo_subparsers.add_parser("create", help="Create a new repo on huggingface.co")
|
| 118 |
+
repo_create_parser.add_argument(
|
| 119 |
+
"name",
|
| 120 |
+
type=str,
|
| 121 |
+
help="Name for your repo. Will be namespaced under your username to build the repo id.",
|
| 122 |
+
)
|
| 123 |
+
repo_create_parser.add_argument(
|
| 124 |
+
"--type",
|
| 125 |
+
type=str,
|
| 126 |
+
help='Optional: repo_type: set to "dataset" or "space" if creating a dataset or space, default is model.',
|
| 127 |
+
)
|
| 128 |
+
repo_create_parser.add_argument("--organization", type=str, help="Optional: organization namespace.")
|
| 129 |
+
repo_create_parser.add_argument(
|
| 130 |
+
"--space_sdk",
|
| 131 |
+
type=str,
|
| 132 |
+
help='Optional: Hugging Face Spaces SDK type. Required when --type is set to "space".',
|
| 133 |
+
choices=SPACES_SDK_TYPES,
|
| 134 |
+
)
|
| 135 |
+
repo_create_parser.add_argument(
|
| 136 |
+
"-y",
|
| 137 |
+
"--yes",
|
| 138 |
+
action="store_true",
|
| 139 |
+
help="Optional: answer Yes to the prompt",
|
| 140 |
+
)
|
| 141 |
+
repo_create_parser.set_defaults(func=lambda args: RepoCreateCommand(args))
|
| 142 |
+
|
| 143 |
+
|
| 144 |
+
class BaseUserCommand:
|
| 145 |
+
def __init__(self, args):
|
| 146 |
+
self.args = args
|
| 147 |
+
self._api = HfApi()
|
| 148 |
+
|
| 149 |
+
|
| 150 |
+
class LoginCommand(BaseUserCommand):
|
| 151 |
+
def run(self):
|
| 152 |
+
logging.set_verbosity_info()
|
| 153 |
+
login(
|
| 154 |
+
token=self.args.token,
|
| 155 |
+
add_to_git_credential=self.args.add_to_git_credential,
|
| 156 |
+
)
|
| 157 |
+
|
| 158 |
+
|
| 159 |
+
class LogoutCommand(BaseUserCommand):
|
| 160 |
+
def run(self):
|
| 161 |
+
logging.set_verbosity_info()
|
| 162 |
+
logout(token_name=self.args.token_name)
|
| 163 |
+
|
| 164 |
+
|
| 165 |
+
class AuthSwitchCommand(BaseUserCommand):
|
| 166 |
+
def run(self):
|
| 167 |
+
logging.set_verbosity_info()
|
| 168 |
+
token_name = self.args.token_name
|
| 169 |
+
if token_name is None:
|
| 170 |
+
token_name = self._select_token_name()
|
| 171 |
+
|
| 172 |
+
if token_name is None:
|
| 173 |
+
print("No token name provided. Aborting.")
|
| 174 |
+
exit()
|
| 175 |
+
auth_switch(token_name, add_to_git_credential=self.args.add_to_git_credential)
|
| 176 |
+
|
| 177 |
+
def _select_token_name(self) -> Optional[str]:
|
| 178 |
+
token_names = list(get_stored_tokens().keys())
|
| 179 |
+
|
| 180 |
+
if not token_names:
|
| 181 |
+
logger.error("No stored tokens found. Please login first.")
|
| 182 |
+
return None
|
| 183 |
+
|
| 184 |
+
if _inquirer_py_available:
|
| 185 |
+
return self._select_token_name_tui(token_names)
|
| 186 |
+
# if inquirer is not available, use a simpler terminal UI
|
| 187 |
+
print("Available stored tokens:")
|
| 188 |
+
for i, token_name in enumerate(token_names, 1):
|
| 189 |
+
print(f"{i}. {token_name}")
|
| 190 |
+
while True:
|
| 191 |
+
try:
|
| 192 |
+
choice = input("Enter the number of the token to switch to (or 'q' to quit): ")
|
| 193 |
+
if choice.lower() == "q":
|
| 194 |
+
return None
|
| 195 |
+
index = int(choice) - 1
|
| 196 |
+
if 0 <= index < len(token_names):
|
| 197 |
+
return token_names[index]
|
| 198 |
+
else:
|
| 199 |
+
print("Invalid selection. Please try again.")
|
| 200 |
+
except ValueError:
|
| 201 |
+
print("Invalid input. Please enter a number or 'q' to quit.")
|
| 202 |
+
|
| 203 |
+
def _select_token_name_tui(self, token_names: List[str]) -> Optional[str]:
|
| 204 |
+
choices = [Choice(token_name, name=token_name) for token_name in token_names]
|
| 205 |
+
try:
|
| 206 |
+
return inquirer.select(
|
| 207 |
+
message="Select a token to switch to:",
|
| 208 |
+
choices=choices,
|
| 209 |
+
default=None,
|
| 210 |
+
).execute()
|
| 211 |
+
except KeyboardInterrupt:
|
| 212 |
+
logger.info("Token selection cancelled.")
|
| 213 |
+
return None
|
| 214 |
+
|
| 215 |
+
|
| 216 |
+
class AuthListCommand(BaseUserCommand):
|
| 217 |
+
def run(self):
|
| 218 |
+
logging.set_verbosity_info()
|
| 219 |
+
auth_list()
|
| 220 |
+
|
| 221 |
+
|
| 222 |
+
class WhoamiCommand(BaseUserCommand):
|
| 223 |
+
def run(self):
|
| 224 |
+
token = get_token()
|
| 225 |
+
if token is None:
|
| 226 |
+
print("Not logged in")
|
| 227 |
+
exit()
|
| 228 |
+
try:
|
| 229 |
+
info = self._api.whoami(token)
|
| 230 |
+
print(info["name"])
|
| 231 |
+
orgs = [org["name"] for org in info["orgs"]]
|
| 232 |
+
if orgs:
|
| 233 |
+
print(ANSI.bold("orgs: "), ",".join(orgs))
|
| 234 |
+
|
| 235 |
+
if ENDPOINT != "https://huggingface.co":
|
| 236 |
+
print(f"Authenticated through private endpoint: {ENDPOINT}")
|
| 237 |
+
except HTTPError as e:
|
| 238 |
+
print(e)
|
| 239 |
+
print(ANSI.red(e.response.text))
|
| 240 |
+
exit(1)
|
| 241 |
+
|
| 242 |
+
|
| 243 |
+
class RepoCreateCommand(BaseUserCommand):
|
| 244 |
+
def run(self):
|
| 245 |
+
token = get_token()
|
| 246 |
+
if token is None:
|
| 247 |
+
print("Not logged in")
|
| 248 |
+
exit(1)
|
| 249 |
+
try:
|
| 250 |
+
stdout = subprocess.check_output(["git", "--version"]).decode("utf-8")
|
| 251 |
+
print(ANSI.gray(stdout.strip()))
|
| 252 |
+
except FileNotFoundError:
|
| 253 |
+
print("Looks like you do not have git installed, please install.")
|
| 254 |
+
|
| 255 |
+
try:
|
| 256 |
+
stdout = subprocess.check_output(["git-lfs", "--version"]).decode("utf-8")
|
| 257 |
+
print(ANSI.gray(stdout.strip()))
|
| 258 |
+
except FileNotFoundError:
|
| 259 |
+
print(
|
| 260 |
+
ANSI.red(
|
| 261 |
+
"Looks like you do not have git-lfs installed, please install."
|
| 262 |
+
" You can install from https://git-lfs.github.com/."
|
| 263 |
+
" Then run `git lfs install` (you only have to do this once)."
|
| 264 |
+
)
|
| 265 |
+
)
|
| 266 |
+
print("")
|
| 267 |
+
|
| 268 |
+
user = self._api.whoami(token)["name"]
|
| 269 |
+
namespace = self.args.organization if self.args.organization is not None else user
|
| 270 |
+
|
| 271 |
+
repo_id = f"{namespace}/{self.args.name}"
|
| 272 |
+
|
| 273 |
+
if self.args.type not in REPO_TYPES:
|
| 274 |
+
print("Invalid repo --type")
|
| 275 |
+
exit(1)
|
| 276 |
+
|
| 277 |
+
if self.args.type in REPO_TYPES_URL_PREFIXES:
|
| 278 |
+
prefixed_repo_id = REPO_TYPES_URL_PREFIXES[self.args.type] + repo_id
|
| 279 |
+
else:
|
| 280 |
+
prefixed_repo_id = repo_id
|
| 281 |
+
|
| 282 |
+
print(f"You are about to create {ANSI.bold(prefixed_repo_id)}")
|
| 283 |
+
|
| 284 |
+
if not self.args.yes:
|
| 285 |
+
choice = input("Proceed? [Y/n] ").lower()
|
| 286 |
+
if not (choice == "" or choice == "y" or choice == "yes"):
|
| 287 |
+
print("Abort")
|
| 288 |
+
exit()
|
| 289 |
+
try:
|
| 290 |
+
url = self._api.create_repo(
|
| 291 |
+
repo_id=repo_id,
|
| 292 |
+
token=token,
|
| 293 |
+
repo_type=self.args.type,
|
| 294 |
+
space_sdk=self.args.space_sdk,
|
| 295 |
+
)
|
| 296 |
+
except HTTPError as e:
|
| 297 |
+
print(e)
|
| 298 |
+
print(ANSI.red(e.response.text))
|
| 299 |
+
exit(1)
|
| 300 |
+
print("\nYour repo now lives at:")
|
| 301 |
+
print(f" {ANSI.bold(url)}")
|
| 302 |
+
print("\nYou can clone it locally with the command below, and commit/push as usual.")
|
| 303 |
+
print(f"\n git clone {url}")
|
| 304 |
+
print("")
|
.venv/lib/python3.11/site-packages/huggingface_hub/commands/version.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2022 The HuggingFace Team. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
"""Contains command to print information about the version.
|
| 15 |
+
|
| 16 |
+
Usage:
|
| 17 |
+
huggingface-cli version
|
| 18 |
+
"""
|
| 19 |
+
|
| 20 |
+
from argparse import _SubParsersAction
|
| 21 |
+
|
| 22 |
+
from huggingface_hub import __version__
|
| 23 |
+
|
| 24 |
+
from . import BaseHuggingfaceCLICommand
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
class VersionCommand(BaseHuggingfaceCLICommand):
|
| 28 |
+
def __init__(self, args):
|
| 29 |
+
self.args = args
|
| 30 |
+
|
| 31 |
+
@staticmethod
|
| 32 |
+
def register_subcommand(parser: _SubParsersAction):
|
| 33 |
+
version_parser = parser.add_parser("version", help="Print information about the huggingface-cli version.")
|
| 34 |
+
version_parser.set_defaults(func=VersionCommand)
|
| 35 |
+
|
| 36 |
+
def run(self) -> None:
|
| 37 |
+
print(f"huggingface_hub version: {__version__}")
|
.venv/lib/python3.11/site-packages/huggingface_hub/serialization/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (836 Bytes). View file
|
|
|
.venv/lib/python3.11/site-packages/huggingface_hub/serialization/__pycache__/_base.cpython-311.pyc
ADDED
|
Binary file (8.25 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/huggingface_hub/serialization/__pycache__/_tensorflow.cpython-311.pyc
ADDED
|
Binary file (3.9 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/huggingface_hub/serialization/__pycache__/_torch.cpython-311.pyc
ADDED
|
Binary file (50.3 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/huggingface_hub/utils/__init__.py
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# coding=utf-8
|
| 2 |
+
# Copyright 2021 The HuggingFace Inc. team. All rights reserved.
|
| 3 |
+
#
|
| 4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
+
# you may not use this file except in compliance with the License.
|
| 6 |
+
# You may obtain a copy of the License at
|
| 7 |
+
#
|
| 8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
+
#
|
| 10 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
+
# See the License for the specific language governing permissions and
|
| 14 |
+
# limitations under the License
|
| 15 |
+
|
| 16 |
+
# ruff: noqa: F401
|
| 17 |
+
|
| 18 |
+
from huggingface_hub.errors import (
|
| 19 |
+
BadRequestError,
|
| 20 |
+
CacheNotFound,
|
| 21 |
+
CorruptedCacheException,
|
| 22 |
+
DisabledRepoError,
|
| 23 |
+
EntryNotFoundError,
|
| 24 |
+
FileMetadataError,
|
| 25 |
+
GatedRepoError,
|
| 26 |
+
HfHubHTTPError,
|
| 27 |
+
HFValidationError,
|
| 28 |
+
LocalEntryNotFoundError,
|
| 29 |
+
LocalTokenNotFoundError,
|
| 30 |
+
NotASafetensorsRepoError,
|
| 31 |
+
OfflineModeIsEnabled,
|
| 32 |
+
RepositoryNotFoundError,
|
| 33 |
+
RevisionNotFoundError,
|
| 34 |
+
SafetensorsParsingError,
|
| 35 |
+
)
|
| 36 |
+
|
| 37 |
+
from . import tqdm as _tqdm # _tqdm is the module
|
| 38 |
+
from ._auth import get_stored_tokens, get_token
|
| 39 |
+
from ._cache_assets import cached_assets_path
|
| 40 |
+
from ._cache_manager import (
|
| 41 |
+
CachedFileInfo,
|
| 42 |
+
CachedRepoInfo,
|
| 43 |
+
CachedRevisionInfo,
|
| 44 |
+
DeleteCacheStrategy,
|
| 45 |
+
HFCacheInfo,
|
| 46 |
+
scan_cache_dir,
|
| 47 |
+
)
|
| 48 |
+
from ._chunk_utils import chunk_iterable
|
| 49 |
+
from ._datetime import parse_datetime
|
| 50 |
+
from ._experimental import experimental
|
| 51 |
+
from ._fixes import SoftTemporaryDirectory, WeakFileLock, yaml_dump
|
| 52 |
+
from ._git_credential import list_credential_helpers, set_git_credential, unset_git_credential
|
| 53 |
+
from ._headers import build_hf_headers, get_token_to_send
|
| 54 |
+
from ._hf_folder import HfFolder
|
| 55 |
+
from ._http import (
|
| 56 |
+
configure_http_backend,
|
| 57 |
+
fix_hf_endpoint_in_url,
|
| 58 |
+
get_session,
|
| 59 |
+
hf_raise_for_status,
|
| 60 |
+
http_backoff,
|
| 61 |
+
reset_sessions,
|
| 62 |
+
)
|
| 63 |
+
from ._pagination import paginate
|
| 64 |
+
from ._paths import DEFAULT_IGNORE_PATTERNS, FORBIDDEN_FOLDERS, filter_repo_objects
|
| 65 |
+
from ._runtime import (
|
| 66 |
+
dump_environment_info,
|
| 67 |
+
get_aiohttp_version,
|
| 68 |
+
get_fastai_version,
|
| 69 |
+
get_fastapi_version,
|
| 70 |
+
get_fastcore_version,
|
| 71 |
+
get_gradio_version,
|
| 72 |
+
get_graphviz_version,
|
| 73 |
+
get_hf_hub_version,
|
| 74 |
+
get_hf_transfer_version,
|
| 75 |
+
get_jinja_version,
|
| 76 |
+
get_numpy_version,
|
| 77 |
+
get_pillow_version,
|
| 78 |
+
get_pydantic_version,
|
| 79 |
+
get_pydot_version,
|
| 80 |
+
get_python_version,
|
| 81 |
+
get_tensorboard_version,
|
| 82 |
+
get_tf_version,
|
| 83 |
+
get_torch_version,
|
| 84 |
+
is_aiohttp_available,
|
| 85 |
+
is_colab_enterprise,
|
| 86 |
+
is_fastai_available,
|
| 87 |
+
is_fastapi_available,
|
| 88 |
+
is_fastcore_available,
|
| 89 |
+
is_google_colab,
|
| 90 |
+
is_gradio_available,
|
| 91 |
+
is_graphviz_available,
|
| 92 |
+
is_hf_transfer_available,
|
| 93 |
+
is_jinja_available,
|
| 94 |
+
is_notebook,
|
| 95 |
+
is_numpy_available,
|
| 96 |
+
is_package_available,
|
| 97 |
+
is_pillow_available,
|
| 98 |
+
is_pydantic_available,
|
| 99 |
+
is_pydot_available,
|
| 100 |
+
is_safetensors_available,
|
| 101 |
+
is_tensorboard_available,
|
| 102 |
+
is_tf_available,
|
| 103 |
+
is_torch_available,
|
| 104 |
+
)
|
| 105 |
+
from ._safetensors import SafetensorsFileMetadata, SafetensorsRepoMetadata, TensorInfo
|
| 106 |
+
from ._subprocess import capture_output, run_interactive_subprocess, run_subprocess
|
| 107 |
+
from ._telemetry import send_telemetry
|
| 108 |
+
from ._typing import is_jsonable, is_simple_optional_type, unwrap_simple_optional_type
|
| 109 |
+
from ._validators import smoothly_deprecate_use_auth_token, validate_hf_hub_args, validate_repo_id
|
| 110 |
+
from .tqdm import are_progress_bars_disabled, disable_progress_bars, enable_progress_bars, tqdm, tqdm_stream_file
|
.venv/lib/python3.11/site-packages/huggingface_hub/utils/__pycache__/_chunk_utils.cpython-311.pyc
ADDED
|
Binary file (2.23 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/huggingface_hub/utils/__pycache__/_fixes.cpython-311.pyc
ADDED
|
Binary file (6.41 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/huggingface_hub/utils/__pycache__/_git_credential.cpython-311.pyc
ADDED
|
Binary file (5.68 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/huggingface_hub/utils/__pycache__/_headers.cpython-311.pyc
ADDED
|
Binary file (9.56 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/huggingface_hub/utils/__pycache__/_hf_folder.cpython-311.pyc
ADDED
|
Binary file (4.01 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/huggingface_hub/utils/__pycache__/_http.cpython-311.pyc
ADDED
|
Binary file (24.8 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/huggingface_hub/utils/__pycache__/_lfs.cpython-311.pyc
ADDED
|
Binary file (5.46 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/huggingface_hub/utils/__pycache__/_pagination.cpython-311.pyc
ADDED
|
Binary file (2.25 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/huggingface_hub/utils/__pycache__/_paths.cpython-311.pyc
ADDED
|
Binary file (5.89 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/huggingface_hub/utils/__pycache__/_runtime.cpython-311.pyc
ADDED
|
Binary file (15 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/huggingface_hub/utils/__pycache__/_safetensors.cpython-311.pyc
ADDED
|
Binary file (6.55 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/huggingface_hub/utils/__pycache__/_subprocess.cpython-311.pyc
ADDED
|
Binary file (5.4 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/huggingface_hub/utils/__pycache__/_telemetry.cpython-311.pyc
ADDED
|
Binary file (6.36 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/huggingface_hub/utils/__pycache__/_typing.cpython-311.pyc
ADDED
|
Binary file (3.97 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/huggingface_hub/utils/__pycache__/_validators.cpython-311.pyc
ADDED
|
Binary file (9.42 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/huggingface_hub/utils/__pycache__/sha.cpython-311.pyc
ADDED
|
Binary file (2.93 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/huggingface_hub/utils/_auth.py
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2023 The HuggingFace Team. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
"""Contains an helper to get the token from machine (env variable, secret or config file)."""
|
| 15 |
+
|
| 16 |
+
import configparser
|
| 17 |
+
import logging
|
| 18 |
+
import os
|
| 19 |
+
import warnings
|
| 20 |
+
from pathlib import Path
|
| 21 |
+
from threading import Lock
|
| 22 |
+
from typing import Dict, Optional
|
| 23 |
+
|
| 24 |
+
from .. import constants
|
| 25 |
+
from ._runtime import is_colab_enterprise, is_google_colab
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
_IS_GOOGLE_COLAB_CHECKED = False
|
| 29 |
+
_GOOGLE_COLAB_SECRET_LOCK = Lock()
|
| 30 |
+
_GOOGLE_COLAB_SECRET: Optional[str] = None
|
| 31 |
+
|
| 32 |
+
logger = logging.getLogger(__name__)
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
def get_token() -> Optional[str]:
|
| 36 |
+
"""
|
| 37 |
+
Get token if user is logged in.
|
| 38 |
+
|
| 39 |
+
Note: in most cases, you should use [`huggingface_hub.utils.build_hf_headers`] instead. This method is only useful
|
| 40 |
+
if you want to retrieve the token for other purposes than sending an HTTP request.
|
| 41 |
+
|
| 42 |
+
Token is retrieved in priority from the `HF_TOKEN` environment variable. Otherwise, we read the token file located
|
| 43 |
+
in the Hugging Face home folder. Returns None if user is not logged in. To log in, use [`login`] or
|
| 44 |
+
`huggingface-cli login`.
|
| 45 |
+
|
| 46 |
+
Returns:
|
| 47 |
+
`str` or `None`: The token, `None` if it doesn't exist.
|
| 48 |
+
"""
|
| 49 |
+
return _get_token_from_google_colab() or _get_token_from_environment() or _get_token_from_file()
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
def _get_token_from_google_colab() -> Optional[str]:
|
| 53 |
+
"""Get token from Google Colab secrets vault using `google.colab.userdata.get(...)`.
|
| 54 |
+
|
| 55 |
+
Token is read from the vault only once per session and then stored in a global variable to avoid re-requesting
|
| 56 |
+
access to the vault.
|
| 57 |
+
"""
|
| 58 |
+
# If it's not a Google Colab or it's Colab Enterprise, fallback to environment variable or token file authentication
|
| 59 |
+
if not is_google_colab() or is_colab_enterprise():
|
| 60 |
+
return None
|
| 61 |
+
|
| 62 |
+
# `google.colab.userdata` is not thread-safe
|
| 63 |
+
# This can lead to a deadlock if multiple threads try to access it at the same time
|
| 64 |
+
# (typically when using `snapshot_download`)
|
| 65 |
+
# => use a lock
|
| 66 |
+
# See https://github.com/huggingface/huggingface_hub/issues/1952 for more details.
|
| 67 |
+
with _GOOGLE_COLAB_SECRET_LOCK:
|
| 68 |
+
global _GOOGLE_COLAB_SECRET
|
| 69 |
+
global _IS_GOOGLE_COLAB_CHECKED
|
| 70 |
+
|
| 71 |
+
if _IS_GOOGLE_COLAB_CHECKED: # request access only once
|
| 72 |
+
return _GOOGLE_COLAB_SECRET
|
| 73 |
+
|
| 74 |
+
try:
|
| 75 |
+
from google.colab import userdata # type: ignore
|
| 76 |
+
from google.colab.errors import Error as ColabError # type: ignore
|
| 77 |
+
except ImportError:
|
| 78 |
+
return None
|
| 79 |
+
|
| 80 |
+
try:
|
| 81 |
+
token = userdata.get("HF_TOKEN")
|
| 82 |
+
_GOOGLE_COLAB_SECRET = _clean_token(token)
|
| 83 |
+
except userdata.NotebookAccessError:
|
| 84 |
+
# Means the user has a secret call `HF_TOKEN` and got a popup "please grand access to HF_TOKEN" and refused it
|
| 85 |
+
# => warn user but ignore error => do not re-request access to user
|
| 86 |
+
warnings.warn(
|
| 87 |
+
"\nAccess to the secret `HF_TOKEN` has not been granted on this notebook."
|
| 88 |
+
"\nYou will not be requested again."
|
| 89 |
+
"\nPlease restart the session if you want to be prompted again."
|
| 90 |
+
)
|
| 91 |
+
_GOOGLE_COLAB_SECRET = None
|
| 92 |
+
except userdata.SecretNotFoundError:
|
| 93 |
+
# Means the user did not define a `HF_TOKEN` secret => warn
|
| 94 |
+
warnings.warn(
|
| 95 |
+
"\nThe secret `HF_TOKEN` does not exist in your Colab secrets."
|
| 96 |
+
"\nTo authenticate with the Hugging Face Hub, create a token in your settings tab "
|
| 97 |
+
"(https://huggingface.co/settings/tokens), set it as secret in your Google Colab and restart your session."
|
| 98 |
+
"\nYou will be able to reuse this secret in all of your notebooks."
|
| 99 |
+
"\nPlease note that authentication is recommended but still optional to access public models or datasets."
|
| 100 |
+
)
|
| 101 |
+
_GOOGLE_COLAB_SECRET = None
|
| 102 |
+
except ColabError as e:
|
| 103 |
+
# Something happen but we don't know what => recommend to open a GitHub issue
|
| 104 |
+
warnings.warn(
|
| 105 |
+
f"\nError while fetching `HF_TOKEN` secret value from your vault: '{str(e)}'."
|
| 106 |
+
"\nYou are not authenticated with the Hugging Face Hub in this notebook."
|
| 107 |
+
"\nIf the error persists, please let us know by opening an issue on GitHub "
|
| 108 |
+
"(https://github.com/huggingface/huggingface_hub/issues/new)."
|
| 109 |
+
)
|
| 110 |
+
_GOOGLE_COLAB_SECRET = None
|
| 111 |
+
|
| 112 |
+
_IS_GOOGLE_COLAB_CHECKED = True
|
| 113 |
+
return _GOOGLE_COLAB_SECRET
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
def _get_token_from_environment() -> Optional[str]:
|
| 117 |
+
# `HF_TOKEN` has priority (keep `HUGGING_FACE_HUB_TOKEN` for backward compatibility)
|
| 118 |
+
return _clean_token(os.environ.get("HF_TOKEN") or os.environ.get("HUGGING_FACE_HUB_TOKEN"))
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
def _get_token_from_file() -> Optional[str]:
|
| 122 |
+
try:
|
| 123 |
+
return _clean_token(Path(constants.HF_TOKEN_PATH).read_text())
|
| 124 |
+
except FileNotFoundError:
|
| 125 |
+
return None
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
def get_stored_tokens() -> Dict[str, str]:
|
| 129 |
+
"""
|
| 130 |
+
Returns the parsed INI file containing the access tokens.
|
| 131 |
+
The file is located at `HF_STORED_TOKENS_PATH`, defaulting to `~/.cache/huggingface/stored_tokens`.
|
| 132 |
+
If the file does not exist, an empty dictionary is returned.
|
| 133 |
+
|
| 134 |
+
Returns: `Dict[str, str]`
|
| 135 |
+
Key is the token name and value is the token.
|
| 136 |
+
"""
|
| 137 |
+
tokens_path = Path(constants.HF_STORED_TOKENS_PATH)
|
| 138 |
+
if not tokens_path.exists():
|
| 139 |
+
stored_tokens = {}
|
| 140 |
+
config = configparser.ConfigParser()
|
| 141 |
+
try:
|
| 142 |
+
config.read(tokens_path)
|
| 143 |
+
stored_tokens = {token_name: config.get(token_name, "hf_token") for token_name in config.sections()}
|
| 144 |
+
except configparser.Error as e:
|
| 145 |
+
logger.error(f"Error parsing stored tokens file: {e}")
|
| 146 |
+
stored_tokens = {}
|
| 147 |
+
return stored_tokens
|
| 148 |
+
|
| 149 |
+
|
| 150 |
+
def _save_stored_tokens(stored_tokens: Dict[str, str]) -> None:
|
| 151 |
+
"""
|
| 152 |
+
Saves the given configuration to the stored tokens file.
|
| 153 |
+
|
| 154 |
+
Args:
|
| 155 |
+
stored_tokens (`Dict[str, str]`):
|
| 156 |
+
The stored tokens to save. Key is the token name and value is the token.
|
| 157 |
+
"""
|
| 158 |
+
stored_tokens_path = Path(constants.HF_STORED_TOKENS_PATH)
|
| 159 |
+
|
| 160 |
+
# Write the stored tokens into an INI file
|
| 161 |
+
config = configparser.ConfigParser()
|
| 162 |
+
for token_name in sorted(stored_tokens.keys()):
|
| 163 |
+
config.add_section(token_name)
|
| 164 |
+
config.set(token_name, "hf_token", stored_tokens[token_name])
|
| 165 |
+
|
| 166 |
+
stored_tokens_path.parent.mkdir(parents=True, exist_ok=True)
|
| 167 |
+
with stored_tokens_path.open("w") as config_file:
|
| 168 |
+
config.write(config_file)
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
def _get_token_by_name(token_name: str) -> Optional[str]:
|
| 172 |
+
"""
|
| 173 |
+
Get the token by name.
|
| 174 |
+
|
| 175 |
+
Args:
|
| 176 |
+
token_name (`str`):
|
| 177 |
+
The name of the token to get.
|
| 178 |
+
|
| 179 |
+
Returns:
|
| 180 |
+
`str` or `None`: The token, `None` if it doesn't exist.
|
| 181 |
+
|
| 182 |
+
"""
|
| 183 |
+
stored_tokens = get_stored_tokens()
|
| 184 |
+
if token_name not in stored_tokens:
|
| 185 |
+
return None
|
| 186 |
+
return _clean_token(stored_tokens[token_name])
|
| 187 |
+
|
| 188 |
+
|
| 189 |
+
def _save_token(token: str, token_name: str) -> None:
|
| 190 |
+
"""
|
| 191 |
+
Save the given token.
|
| 192 |
+
|
| 193 |
+
If the stored tokens file does not exist, it will be created.
|
| 194 |
+
Args:
|
| 195 |
+
token (`str`):
|
| 196 |
+
The token to save.
|
| 197 |
+
token_name (`str`):
|
| 198 |
+
The name of the token.
|
| 199 |
+
"""
|
| 200 |
+
tokens_path = Path(constants.HF_STORED_TOKENS_PATH)
|
| 201 |
+
stored_tokens = get_stored_tokens()
|
| 202 |
+
stored_tokens[token_name] = token
|
| 203 |
+
_save_stored_tokens(stored_tokens)
|
| 204 |
+
logger.info(f"The token `{token_name}` has been saved to {tokens_path}")
|
| 205 |
+
|
| 206 |
+
|
| 207 |
+
def _clean_token(token: Optional[str]) -> Optional[str]:
|
| 208 |
+
"""Clean token by removing trailing and leading spaces and newlines.
|
| 209 |
+
|
| 210 |
+
If token is an empty string, return None.
|
| 211 |
+
"""
|
| 212 |
+
if token is None:
|
| 213 |
+
return None
|
| 214 |
+
return token.replace("\r", "").replace("\n", "").strip() or None
|