codekingpro/portable-devtools
114k
1# Copyright 2022-present, the HuggingFace Inc. team.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 at6#7# http://www.apache.org/licenses/LICENSE-2.08#9# Unless required by applicable law or agreed to in writing, software10# 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 and13# limitations under the License.14"""Check presence of installed packages at runtime."""15 16import importlib.metadata17import importlib.util18import os19import platform20import sys21import warnings22from pathlib import Path23from typing import Any, Literal24 25from .. import __version__, constants26 27 28_PY_VERSION: str = sys.version.split()[0].rstrip("+")29 30_package_versions = {}31 32_CANDIDATES = {33 "aiohttp": {"aiohttp"},34 "fastai": {"fastai"},35 "fastapi": {"fastapi"},36 "fastcore": {"fastcore"},37 "gradio": {"gradio"},38 "graphviz": {"graphviz"},39 "hf_xet": {"hf_xet"},40 "jinja": {"Jinja2"},41 "httpx": {"httpx"},42 "keras": {"keras"},43 "numpy": {"numpy"},44 "pillow": {"Pillow"},45 "pydantic": {"pydantic"},46 "pydot": {"pydot"},47 "safetensors": {"safetensors"},48 "tensorboard": {"tensorboardX"},49 "tensorflow": (50 "tensorflow",51 "tensorflow-cpu",52 "tensorflow-gpu",53 "tf-nightly",54 "tf-nightly-cpu",55 "tf-nightly-gpu",56 "intel-tensorflow",57 "intel-tensorflow-avx512",58 "tensorflow-rocm",59 "tensorflow-macos",60 ),61 "torch": {"torch"},62}63 64# Check once at runtime65for candidate_name, package_names in _CANDIDATES.items():66 _package_versions[candidate_name] = "N/A"67 for name in package_names:68 try:69 _package_versions[candidate_name] = importlib.metadata.version(name)70 break71 except importlib.metadata.PackageNotFoundError:72 pass73 74 75def _get_version(package_name: str) -> str:76 return _package_versions.get(package_name, "N/A")77 78 79def is_package_available(package_name: str) -> bool:80 return _get_version(package_name) != "N/A"81 82 83# Python84def get_python_version() -> str:85 return _PY_VERSION86 87 88# Huggingface Hub89def get_hf_hub_version() -> str:90 return __version__91 92 93# aiohttp94def is_aiohttp_available() -> bool:95 return is_package_available("aiohttp")96 97 98def get_aiohttp_version() -> str:99 return _get_version("aiohttp")100 101 102# FastAI103def is_fastai_available() -> bool:104 return is_package_available("fastai")105 106 107def get_fastai_version() -> str:108 return _get_version("fastai")109 110 111# FastAPI112def is_fastapi_available() -> bool:113 return is_package_available("fastapi")114 115 116def get_fastapi_version() -> str:117 return _get_version("fastapi")118 119 120# Fastcore121def is_fastcore_available() -> bool:122 return is_package_available("fastcore")123 124 125def get_fastcore_version() -> str:126 return _get_version("fastcore")127 128 129# FastAI130def is_gradio_available() -> bool:131 return is_package_available("gradio")132 133 134def get_gradio_version() -> str:135 return _get_version("gradio")136 137 138# Graphviz139def is_graphviz_available() -> bool:140 return is_package_available("graphviz")141 142 143def get_graphviz_version() -> str:144 return _get_version("graphviz")145 146 147# httpx148def is_httpx_available() -> bool:149 return is_package_available("httpx")150 151 152def get_httpx_version() -> str:153 return _get_version("httpx")154 155 156# xet157def is_xet_available() -> bool:158 # since hf_xet is automatically used if available, allow explicit disabling via environment variable159 if constants.HF_HUB_DISABLE_XET:160 return False161 162 return is_package_available("hf_xet")163 164 165def get_xet_version() -> str:166 return _get_version("hf_xet")167 168 169# keras170def is_keras_available() -> bool:171 return is_package_available("keras")172 173 174def get_keras_version() -> str:175 return _get_version("keras")176 177 178# Numpy179def is_numpy_available() -> bool:180 return is_package_available("numpy")181 182 183def get_numpy_version() -> str:184 return _get_version("numpy")185 186 187# Jinja188def is_jinja_available() -> bool:189 return is_package_available("jinja")190 191 192def get_jinja_version() -> str:193 return _get_version("jinja")194 195 196# Pillow197def is_pillow_available() -> bool:198 return is_package_available("pillow")199 200 201def get_pillow_version() -> str:202 return _get_version("pillow")203 204 205# Pydantic206def is_pydantic_available() -> bool:207 if not is_package_available("pydantic"):208 return False209 # For Pydantic, we add an extra check to test whether it is correctly installed or not. If both pydantic 2.x and210 # typing_extensions<=4.5.0 are installed, then pydantic will fail at import time. This should not happen when211 # it is installed with `pip install huggingface_hub[inference]` but it can happen when it is installed manually212 # by the user in an environment that we don't control.213 #214 # Usually we won't need to do this kind of check on optional dependencies. However, pydantic is a special case215 # as it is automatically imported when doing `from huggingface_hub import ...` even if the user doesn't use it.216 #217 # See https://github.com/huggingface/huggingface_hub/pull/1829 for more details.218 try:219 from pydantic import validator # noqa: F401220 except ImportError:221 # Example: "ImportError: cannot import name 'TypeAliasType' from 'typing_extensions'"222 warnings.warn(223 "Pydantic is installed but cannot be imported. Please check your installation. `huggingface_hub` will "224 "default to not using Pydantic. Error message: '{e}'"225 )226 return False227 return True228 229 230def get_pydantic_version() -> str:231 return _get_version("pydantic")232 233 234# Pydot235def is_pydot_available() -> bool:236 return is_package_available("pydot")237 238 239def get_pydot_version() -> str:240 return _get_version("pydot")241 242 243# Tensorboard244def is_tensorboard_available() -> bool:245 return is_package_available("tensorboard")246 247 248def get_tensorboard_version() -> str:249 return _get_version("tensorboard")250 251 252# Tensorflow253def is_tf_available() -> bool:254 return is_package_available("tensorflow")255 256 257def get_tf_version() -> str:258 return _get_version("tensorflow")259 260 261# Torch262def is_torch_available() -> bool:263 return is_package_available("torch")264 265 266def get_torch_version() -> str:267 return _get_version("torch")268 269 270# Safetensors271def is_safetensors_available() -> bool:272 return is_package_available("safetensors")273 274 275# Shell-related helpers276try:277 # Set to `True` if script is running in a Google Colab notebook.278 # If running in Google Colab, git credential store is set globally which makes the279 # warning disappear. See https://github.com/huggingface/huggingface_hub/issues/1043280 #281 # Taken from https://stackoverflow.com/a/63519730.282 _is_google_colab = "google.colab" in str(get_ipython()) # type: ignore # noqa: F821283except NameError:284 _is_google_colab = False285 286 287def is_notebook() -> bool:288 """Return `True` if code is executed in a notebook (Jupyter, Colab, QTconsole).289 290 Taken from https://stackoverflow.com/a/39662359.291 Adapted to make it work with Google colab as well.292 """293 try:294 shell_class = get_ipython().__class__ # type: ignore # noqa: F821295 for parent_class in shell_class.__mro__: # e.g. "is subclass of"296 if parent_class.__name__ == "ZMQInteractiveShell":297 return True # Jupyter notebook, Google colab or qtconsole298 return False299 except NameError:300 return False # Probably standard Python interpreter301 302 303def is_google_colab() -> bool:304 """Return `True` if code is executed in a Google colab.305 306 Taken from https://stackoverflow.com/a/63519730.307 """308 return _is_google_colab309 310 311def is_colab_enterprise() -> bool:312 """Return `True` if code is executed in a Google Colab Enterprise environment."""313 return os.environ.get("VERTEX_PRODUCT") == "COLAB_ENTERPRISE"314 315 316# Check how huggingface_hub has been installed317 318 319def installation_method() -> Literal["brew", "hf_installer", "pip", "unknown"]:320 """Return the installation method of the current environment.321 322 - "hf_installer" if installed via the official installer script323 - "brew" if installed via Homebrew324 - "pip" if pip is available (default fallback for standard Python environments)325 - "unknown" otherwise326 """327 # hf_installer check must come first: the installer creates a venv using the328 # system Python, which may be Homebrew's. Checking brew first would false-positive329 # when the resolved sys.executable points to /opt/homebrew/... inside a venv.330 if _is_hf_installer_installation():331 return "hf_installer"332 if _is_brew_installation():333 return "brew"334 if _is_pip_available():335 return "pip"336 return "unknown"337 338 339def _is_brew_installation() -> bool:340 """Check if running from a Homebrew installation.341 342 Homebrew installs the `hf` formula into a Cellar directory and creates a343 libexec virtualenv at e.g. /opt/homebrew/Cellar/hf/0.30.0/libexec/.344 We check `sys.prefix` (the venv/prefix root) for "/Cellar/hf/" rather345 than checking `sys.executable` — the latter resolves to Homebrew's Python346 (e.g. /opt/homebrew/Cellar/python@3.12/...) even for non-brew installs347 when the system Python happens to come from Homebrew.348 """349 prefix = str(Path(sys.prefix).resolve())350 return "/Cellar/hf/" in prefix351 352 353def _is_hf_installer_installation() -> bool:354 """Return `True` if the current environment was set up via the official hf installer script.355 356 i.e. using one of357 curl -LsSf https://hf.co/cli/install.sh | bash358 powershell -ExecutionPolicy ByPass -c "irm https://hf.co/cli/install.ps1 | iex"359 """360 venv = sys.prefix # points to venv root if active361 marker = Path(venv) / ".hf_installer_marker"362 return marker.exists()363 364 365def _is_pip_available() -> bool:366 """Return `True` if pip is importable in the current environment."""367 return importlib.util.find_spec("pip") is not None368 369 370def dump_environment_info() -> dict[str, Any]:371 """Dump information about the machine to help debugging issues.372 373 Similar helper exist in:374 - `datasets` (https://github.com/huggingface/datasets/blob/main/src/datasets/commands/env.py)375 - `diffusers` (https://github.com/huggingface/diffusers/blob/main/src/diffusers/commands/env.py)376 - `transformers` (https://github.com/huggingface/transformers/blob/main/src/transformers/commands/env.py)377 """378 from huggingface_hub import get_token, whoami379 from huggingface_hub.utils import is_agent, list_credential_helpers380 381 token = get_token()382 383 # Generic machine info384 info: dict[str, Any] = {385 "huggingface_hub version": get_hf_hub_version(),386 "Platform": platform.platform(),387 "Python version": get_python_version(),388 }389 390 # Interpreter info391 try:392 shell_class = get_ipython().__class__ # type: ignore # noqa: F821393 info["Running in iPython ?"] = "Yes"394 info["iPython shell"] = shell_class.__name__395 except NameError:396 info["Running in iPython ?"] = "No"397 info["Running in notebook ?"] = "Yes" if is_notebook() else "No"398 info["Running in Google Colab ?"] = "Yes" if is_google_colab() else "No"399 info["Running in Google Colab Enterprise ?"] = "Yes" if is_colab_enterprise() else "No"400 # Login info401 info["Token path ?"] = constants.HF_TOKEN_PATH402 info["Has saved token ?"] = token is not None403 if token is not None:404 try:405 info["Who am I ?"] = whoami()["name"]406 except Exception:407 pass408 409 try:410 info["Configured git credential helpers"] = ", ".join(list_credential_helpers())411 except Exception:412 pass413 414 info["Run by AI agent ?"] = "Yes" if is_agent() else "No"415 416 # How huggingface_hub has been installed?417 info["Installation method"] = installation_method()418 419 # Installed dependencies420 info["httpx"] = get_httpx_version()421 info["hf_xet"] = get_xet_version()422 info["gradio"] = get_gradio_version()423 info["tensorboard"] = get_tensorboard_version()424 425 # Environment variables426 info["ENDPOINT"] = constants.ENDPOINT427 info["HF_HUB_CACHE"] = constants.HF_HUB_CACHE428 info["HF_ASSETS_CACHE"] = constants.HF_ASSETS_CACHE429 info["HF_TOKEN_PATH"] = constants.HF_TOKEN_PATH430 info["HF_STORED_TOKENS_PATH"] = constants.HF_STORED_TOKENS_PATH431 info["HF_HUB_OFFLINE"] = constants.HF_HUB_OFFLINE432 info["HF_HUB_DISABLE_TELEMETRY"] = constants.HF_HUB_DISABLE_TELEMETRY433 info["HF_HUB_DISABLE_PROGRESS_BARS"] = constants.HF_HUB_DISABLE_PROGRESS_BARS434 info["HF_HUB_DISABLE_SYMLINKS"] = constants.HF_HUB_DISABLE_SYMLINKS435 info["HF_HUB_DISABLE_SYMLINKS_WARNING"] = constants.HF_HUB_DISABLE_SYMLINKS_WARNING436 info["HF_HUB_DISABLE_EXPERIMENTAL_WARNING"] = constants.HF_HUB_DISABLE_EXPERIMENTAL_WARNING437 info["HF_HUB_DISABLE_IMPLICIT_TOKEN"] = constants.HF_HUB_DISABLE_IMPLICIT_TOKEN438 info["HF_HUB_DISABLE_XET"] = constants.HF_HUB_DISABLE_XET439 info["HF_HUB_ETAG_TIMEOUT"] = constants.HF_HUB_ETAG_TIMEOUT440 info["HF_HUB_DOWNLOAD_TIMEOUT"] = constants.HF_HUB_DOWNLOAD_TIMEOUT441 info["HF_XET_HIGH_PERFORMANCE"] = constants.HF_XET_HIGH_PERFORMANCE442 443 print("\nCopy-and-paste the text below in your GitHub issue.\n")444 print("\n".join([f"- {prop}: {val}" for prop, val in info.items()]) + "\n")445 return info446 