|
13 | 13 | # limitations under the License.
|
14 | 14 | """Contains CLI utilities (styling, helpers)."""
|
15 | 15 |
|
| 16 | +import importlib.metadata |
16 | 17 | import os
|
| 18 | +import time |
17 | 19 | from enum import Enum
|
18 | 20 | from typing import Annotated, Optional, Union
|
19 | 21 |
|
20 | 22 | import click
|
21 | 23 | import typer
|
22 | 24 |
|
23 |
| -from huggingface_hub import __version__ |
| 25 | +from huggingface_hub import __version__, constants |
24 | 26 | from huggingface_hub.hf_api import HfApi
|
| 27 | +from huggingface_hub.utils import get_session, hf_raise_for_status, installation_method, logging |
| 28 | + |
| 29 | + |
| 30 | +logger = logging.get_logger() |
25 | 31 |
|
26 | 32 |
|
27 | 33 | class ANSI:
|
@@ -144,3 +150,79 @@ class RepoType(str, Enum):
|
144 | 150 |
|
145 | 151 | def get_hf_api(token: Optional[str] = None) -> HfApi:
|
146 | 152 | return HfApi(token=token, library_name="hf", library_version=__version__)
|
| 153 | + |
| 154 | + |
| 155 | +### PyPI VERSION CHECKER |
| 156 | + |
| 157 | + |
| 158 | +def check_cli_update() -> None: |
| 159 | + """ |
| 160 | + Check whether a newer version of `huggingface_hub` is available on PyPI. |
| 161 | +
|
| 162 | + If a newer version is found, notify the user and suggest updating. |
| 163 | + The latest PyPI version is cached locally in `$HF_HOME/pypi_latest_version` for 24 hours to prevent repeated notifications. |
| 164 | + If current version is a pre-release (e.g. `1.0.0.rc1`), or a dev version (e.g. `1.0.0.dev1`), no check is performed. |
| 165 | +
|
| 166 | + This function is called at the entry point of the CLI. |
| 167 | + """ |
| 168 | + try: |
| 169 | + _check_cli_update() |
| 170 | + except Exception: |
| 171 | + # We don't want the CLI to fail on version checks, no matter the reason. |
| 172 | + logger.debug("Error while checking for CLI update.", exc_info=True) |
| 173 | + |
| 174 | + |
| 175 | +def _check_cli_update() -> None: |
| 176 | + current_version = importlib.metadata.version("huggingface_hub") |
| 177 | + |
| 178 | + if any(tag in current_version for tag in ["a", "b", "rc", "dev"]): |
| 179 | + # Don't check for pre-releases or dev versions |
| 180 | + return |
| 181 | + |
| 182 | + cached_version = _get_cached_pypi_version() |
| 183 | + if cached_version is None: |
| 184 | + latest_version = _get_pypi_version() |
| 185 | + _cache_pypi_version(latest_version) |
| 186 | + else: |
| 187 | + latest_version = cached_version |
| 188 | + |
| 189 | + if current_version != latest_version: |
| 190 | + method = installation_method() |
| 191 | + if method == "brew": |
| 192 | + update_command = "brew upgrade huggingface-cli" |
| 193 | + elif method == "hf_installer" and os.name == "nt": |
| 194 | + update_command = "curl -LsSf https://hf.co/cli/install.ps1 | pwsh -" |
| 195 | + elif method == "hf_installer": |
| 196 | + update_command = "curl -LsSf https://hf.co/cli/install.sh | sh -" |
| 197 | + else: # unknown => likely pip |
| 198 | + update_command = "pip install -U huggingface_hub" |
| 199 | + |
| 200 | + click.echo( |
| 201 | + ANSI.yellow( |
| 202 | + f"A new version of huggingface_hub ({latest_version}) is available! " |
| 203 | + f"You are using version {current_version}.\n" |
| 204 | + f"To update, run: {ANSI.bold(update_command)}\n", |
| 205 | + ) |
| 206 | + ) |
| 207 | + |
| 208 | + |
| 209 | +def _get_pypi_version() -> str: |
| 210 | + response = get_session().get("https://pypi.org/pypi/huggingface_hub/json", timeout=2) |
| 211 | + hf_raise_for_status(response) |
| 212 | + data = response.json() |
| 213 | + return data["info"]["version"] |
| 214 | + |
| 215 | + |
| 216 | +def _get_cached_pypi_version() -> Optional[str]: |
| 217 | + if os.path.exists(constants.PYPI_LATEST_VERSION_PATH): |
| 218 | + mtime = os.path.getmtime(constants.PYPI_LATEST_VERSION_PATH) |
| 219 | + # If the file is older than 24h, we don't use it |
| 220 | + if (time.time() - mtime) < 24 * 3600: |
| 221 | + with open(constants.PYPI_LATEST_VERSION_PATH, "r", encoding="utf-8") as f: |
| 222 | + return f.read().strip() |
| 223 | + return None |
| 224 | + |
| 225 | + |
| 226 | +def _cache_pypi_version(version: str) -> None: |
| 227 | + with open(constants.PYPI_LATEST_VERSION_PATH, "w", encoding="utf-8") as f: |
| 228 | + f.write(version) |
0 commit comments