Files
unslothai--unsloth/studio/backend/tests/test_hf_xet_fallback.py
T
wehub-resource-sync e93507a09c
Lockfile supply-chain audit / lockfile supply-chain audit (push) Has been cancelled
Windows Studio GGUF CI / GPU prebuilt resolves without Visual Studio (push) Has been cancelled
Windows Studio GGUF CI / setup.ps1 unit tests (VS 2026 / CMake guard) (push) Has been cancelled
Windows Studio GGUF CI / real-VS detection (VS 2022) (push) Has been cancelled
Windows Studio GGUF CI / real-VS detection (VS 2026) (push) Has been cancelled
Windows Studio GGUF CI / VC++ runtime detect + install round-trip (windows-2025-vs2026) (push) Has been cancelled
Windows Studio GGUF CI / VC++ runtime detect + install round-trip (windows-latest) (push) Has been cancelled
Windows Studio Update CI / Studio Updating Tests (push) Has been cancelled
Wheel CI / Wheel build + content sanity + import smoke (push) Has been cancelled
Lint CI / Source lint (Python + shell + YAML + JSON + safety nets) (push) Has been cancelled
MLX CI on Mac M1 / dispatch (push) Has been cancelled
Security audit / advisory audit (pip + npm + cargo) (push) Has been cancelled
Security audit / pip scan-packages :: extras (push) Has been cancelled
Security audit / pip scan-packages :: studio (push) Has been cancelled
Security audit / pip scan-packages :: hf-stack (push) Has been cancelled
Security audit / npm scan-packages (Studio frontend tarballs) (push) Has been cancelled
Security audit / workflow-trigger lint (pull_request_target / cache-poisoning) (push) Has been cancelled
Security audit / pytest tests/security (push) Has been cancelled
Security audit / npm provenance + new install-script diff (push) Has been cancelled
Studio API CI / Studio API & Auth Tests (push) Has been cancelled
Backend CI / (Python 3.10) (push) Has been cancelled
Backend CI / (Python 3.11) (push) Has been cancelled
Backend CI / (Python 3.12) (push) Has been cancelled
Backend CI / (Python 3.13) (push) Has been cancelled
Backend CI / Repo tests (CPU) (push) Has been cancelled
Frontend CI / Frontend build + bundle sanity (push) Has been cancelled
Studio GGUF CI / OpenAI, Anthropic API tests (push) Has been cancelled
Studio GGUF CI / Tool calling Tests (push) Has been cancelled
Studio GGUF CI / JSON, images (push) Has been cancelled
Mac Studio GGUF CI / OpenAI, Anthropic API tests (push) Has been cancelled
Mac Studio GGUF CI / Tool calling Tests (push) Has been cancelled
Mac Studio GGUF CI / JSON, images (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-14) (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-15) (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-26) (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-15-intel) (push) Has been cancelled
Mac Studio API CI / Studio API & Auth Tests (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-26-intel) (push) Has been cancelled
Mac Studio UI CI / Chat UI Tests (push) Has been cancelled
Studio Tauri CI / Tauri Linux debug build (no codesign) (push) Has been cancelled
Mac Studio Update CI / Studio Updating Tests (push) Has been cancelled
Studio UI CI / Chat UI Tests (push) Has been cancelled
Windows Studio API CI / Studio API & Auth Tests (push) Has been cancelled
Windows Studio UI CI / Chat UI Tests (push) Has been cancelled
Studio Update CI / Studio Updating Tests (push) Has been cancelled
Core / Core (HF=default + TRL=default) (push) Has been cancelled
Core / Core (HF=4.57.6 + TRL<1) (push) Has been cancelled
Core / Core (HF=latest + TRL=latest) (push) Has been cancelled
Core / llama.cpp build + smoke (push) Has been cancelled
Windows Studio GGUF CI / OpenAI, Anthropic API tests (push) Has been cancelled
Windows Studio GGUF CI / Tool calling Tests (push) Has been cancelled
Windows Studio GGUF CI / JSON, images (push) Has been cancelled
Windows Studio GGUF CI / Studio install + inference without Visual Studio (push) Has been cancelled
Studio export capability / capability (macos-latest) (push) Has been cancelled
Studio export capability / capability (ubuntu-latest) (push) Has been cancelled
Studio export capability / capability (windows-latest) (push) Has been cancelled
Cross-platform parity / parity (macos-latest) (push) Has been cancelled
Cross-platform parity / parity (windows-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
Studio load-orchestrator CI / test (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:59:56 +08:00

370 lines
14 KiB
Python

# SPDX-License-Identifier: AGPL-3.0-only
# Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0
"""Tests for the Studio shim over the shared unsloth_zoo Xet -> HTTP fallback.
The transport-policy matrix is tested once in unsloth_zoo; here we assert only the
Studio seam: re-exporting the shared API and injecting the marker-aware
prepare_cache_for_transport on the HTTP retry. CPU-only, no network, no real subprocess.
"""
from __future__ import annotations
import sys
import types as _types
from pathlib import Path
import pytest
_BACKEND_DIR = str(Path(__file__).resolve().parent.parent)
if _BACKEND_DIR not in sys.path:
sys.path.insert(0, _BACKEND_DIR)
# Stub heavy/unavailable deps before importing the module under test. Use real structlog when present;
# a bare stub would break later modules that log at import time.
_loggers_stub = _types.ModuleType("loggers")
_loggers_stub.get_logger = lambda name: __import__("logging").getLogger(name)
sys.modules.setdefault("loggers", _loggers_stub)
try:
import structlog # noqa: F401
except ImportError:
sys.modules["structlog"] = _types.ModuleType("structlog")
import huggingface_hub
try:
import unsloth_zoo.hf_xet_fallback as _shared_mod
shared = _shared_mod
except Exception: # noqa: BLE001 - still collect degraded-path tests when unsloth_zoo is unavailable
shared = None
import utils.hf_xet_fallback as xf
DL_REPO, FILE = "ztest/xet-dl", "model-Q4_K_XL.gguf"
def _requires_shared():
if shared is None:
pytest.skip("unsloth_zoo.hf_xet_fallback is not installed in this environment")
def test_shim_reexports_shared_api():
_requires_shared()
assert xf.DownloadStallError is shared.DownloadStallError
for name in (
"start_watchdog",
"get_hf_download_state",
"child_should_disable_xet",
"hf_hub_download_with_xet_fallback",
"snapshot_download_with_xet_fallback",
):
assert hasattr(xf, name), f"shim missing {name}"
def test_child_should_disable_xet_truth_table():
assert xf.child_should_disable_xet({"disable_xet": True}) is True
assert xf.child_should_disable_xet({"disable_xet": False}) is False
assert xf.child_should_disable_xet({}) is False
def test_shim_injects_studio_prepare_on_http_retry(monkeypatch):
"""A Xet stall retries over HTTP and the shim runs Studio's marker-aware
``prepare_cache_for_transport(..., 'http')`` before the retry."""
_requires_shared()
for var in ("UNSLOTH_DISABLE_XET", "UNSLOTH_STABLE_DOWNLOADS", "HF_HUB_DISABLE_XET"):
monkeypatch.delenv(var, raising = False)
monkeypatch.setattr(huggingface_hub, "try_to_load_from_cache", lambda *a, **k: None)
seen_disable_xet = []
def fake_attempt(
repo_id,
*,
kind,
params,
token,
repo_type,
disable_xet,
cancel_event,
stall_timeout,
interval,
grace_period,
on_status,
force_download = False,
):
seen_disable_xet.append(disable_xet)
return ("ok", "/cache/model.gguf") if disable_xet else ("stall", None)
monkeypatch.setattr(shared, "_run_download_attempt", fake_attempt)
prepared = []
monkeypatch.setattr(
"hub.utils.download_registry.prepare_cache_for_transport",
lambda repo_type, repo_id, mode, *a, **k: prepared.append((repo_type, repo_id, mode)),
)
out = xf.hf_hub_download_with_xet_fallback(DL_REPO, FILE, None)
assert out == "/cache/model.gguf"
assert seen_disable_xet == [False, True] # Xet first, then HTTP
assert prepared == [("model", DL_REPO, "http")], "shim must run Studio's marker-aware prep"
def test_shim_snapshot_injects_studio_prepare(monkeypatch):
"""The snapshot wrapper forwards Studio's marker-aware prep, like the file wrapper."""
captured = {}
def fake_snapshot(repo_id, **kwargs):
captured["repo_id"] = repo_id
captured["prepare_for_http_fn"] = kwargs.get("prepare_for_http_fn")
return "/tmp/snap-dir"
monkeypatch.setattr(xf, "_shared_snapshot_download_with_xet_fallback", fake_snapshot)
out = xf.snapshot_download_with_xet_fallback("org/model")
assert out == "/tmp/snap-dir"
assert captured["repo_id"] == "org/model"
assert captured["prepare_for_http_fn"] is xf._studio_prepare_for_http
def test_degrades_gracefully_without_shared_helper(monkeypatch):
"""On an older unsloth_zoo lacking the shared helper, the shim still imports (Studio
boots) and exposes stub API doing plain HF downloads with the watchdog disabled."""
import importlib
class _BlockShared:
def find_spec(
self,
name,
path = None,
target = None,
):
if name == "unsloth_zoo.hf_xet_fallback":
raise ModuleNotFoundError(f"No module named '{name}'", name = name)
return None
finder = _BlockShared()
saved_shared = sys.modules.pop("unsloth_zoo.hf_xet_fallback", None)
saved_shim = sys.modules.pop("utils.hf_xet_fallback", None)
sys.meta_path.insert(0, finder)
try:
degraded = importlib.import_module("utils.hf_xet_fallback")
# Boots without raising and mirrors the shared API surface.
assert issubclass(degraded.DownloadStallError, RuntimeError)
assert degraded.child_should_disable_xet({"disable_xet": True}) is True
assert degraded.get_hf_download_state(["x"]) is None # unmeasurable
event = degraded.start_watchdog(repo_ids = ["x"], on_stall = lambda m: None)
assert hasattr(event, "set") and not event.is_set() # never fires
# Degraded mode still emits heartbeats so the inactivity deadline is not tripped.
import time as _time
beats = []
hb_stop = degraded.start_watchdog(
repo_ids = ["x"],
on_stall = lambda m: None,
on_heartbeat = beats.append,
interval = 0.02,
)
try:
deadline = _time.monotonic() + 2.0
while not beats and _time.monotonic() < deadline:
_time.sleep(0.02)
assert beats, "degraded watchdog emitted no heartbeat"
finally:
hb_stop.set()
# Downloads fall back to plain huggingface_hub (no watchdog, no crash).
called = {}
def _fake_snapshot(repo_id, **kwargs):
called["repo_id"] = repo_id
return "/snap-dir"
monkeypatch.setattr(huggingface_hub, "snapshot_download", _fake_snapshot)
assert degraded.snapshot_download_with_xet_fallback("org/model") == "/snap-dir"
assert called["repo_id"] == "org/model"
# Cancellation still holds: an already-set cancel_event aborts before the HF download.
import threading as _threading
cancelled = _threading.Event()
cancelled.set()
called.clear()
with pytest.raises(RuntimeError, match = "Cancelled"):
degraded.snapshot_download_with_xet_fallback("org/model", cancel_event = cancelled)
assert "repo_id" not in called, "degraded download ran despite cancellation"
finally:
sys.meta_path.remove(finder)
sys.modules.pop("utils.hf_xet_fallback", None)
if saved_shared is not None:
sys.modules["unsloth_zoo.hf_xet_fallback"] = saved_shared
if saved_shim is not None:
sys.modules["utils.hf_xet_fallback"] = saved_shim
def test_degrades_when_unsloth_zoo_entirely_absent():
"""When unsloth_zoo is absent entirely, the import raises
ModuleNotFoundError(name='unsloth_zoo') (top-level package). Guard that the shim still
degrades and does not re-raise, breaking every Studio import that pulls it in."""
import importlib
class _BlockZoo:
def find_spec(
self,
name,
path = None,
target = None,
):
# Whole package absent, so ModuleNotFoundError.name is the top-level 'unsloth_zoo'.
if name == "unsloth_zoo" or name.startswith("unsloth_zoo."):
raise ModuleNotFoundError("No module named 'unsloth_zoo'", name = "unsloth_zoo")
return None
finder = _BlockZoo()
saved = {
k: v
for k, v in list(sys.modules.items())
if k == "unsloth_zoo" or k.startswith("unsloth_zoo.")
}
for k in saved:
del sys.modules[k]
saved_shim = sys.modules.pop("utils.hf_xet_fallback", None)
sys.meta_path.insert(0, finder)
try:
degraded = importlib.import_module("utils.hf_xet_fallback")
# Boots without raising and exposes the stub API.
assert issubclass(degraded.DownloadStallError, RuntimeError)
assert degraded.get_hf_download_state(["x"]) is None
event = degraded.start_watchdog(repo_ids = ["x"], on_stall = lambda m: None)
assert hasattr(event, "set") and not event.is_set()
finally:
sys.meta_path.remove(finder)
sys.modules.pop("utils.hf_xet_fallback", None)
sys.modules.update(saved)
if saved_shim is not None:
sys.modules["utils.hf_xet_fallback"] = saved_shim
def test_degrades_when_shared_helper_import_raises_importerror():
"""unsloth_zoo can be installed yet fail to import when torch is missing (llama.cpp/GGUF-only
Studio), raising ImportError not ModuleNotFoundError. The shim must degrade for that too."""
import importlib
class _BlockWithImportError:
def find_spec(
self,
name,
path = None,
target = None,
):
if name == "unsloth_zoo.hf_xet_fallback":
# Mirror a torch-less install: a plain ImportError with no .name.
raise ImportError("Unsloth: Pytorch is not installed.")
return None
finder = _BlockWithImportError()
saved_shared = sys.modules.pop("unsloth_zoo.hf_xet_fallback", None)
saved_zoo = sys.modules.pop("unsloth_zoo", None)
saved_shim = sys.modules.pop("utils.hf_xet_fallback", None)
sys.meta_path.insert(0, finder)
try:
degraded = importlib.import_module("utils.hf_xet_fallback")
assert issubclass(degraded.DownloadStallError, RuntimeError)
assert degraded.get_hf_download_state(["x"]) is None
event = degraded.start_watchdog(repo_ids = ["x"], on_stall = lambda m: None)
assert hasattr(event, "set") and not event.is_set()
finally:
sys.meta_path.remove(finder)
sys.modules.pop("utils.hf_xet_fallback", None)
if saved_shared is not None:
sys.modules["unsloth_zoo.hf_xet_fallback"] = saved_shared
if saved_zoo is not None:
sys.modules["unsloth_zoo"] = saved_zoo
if saved_shim is not None:
sys.modules["utils.hf_xet_fallback"] = saved_shim
def test_retries_under_light_gpu_init_when_import_fails(monkeypatch):
"""GPU detection in unsloth_zoo's __init__ raises NotImplementedError on a GPU-less host. The shim
retries under UNSLOTH_ZOO_DISABLE_GPU_INIT=1, restores the env, and degrades if the retry fails.
The backend loads lazily (first use of a heavy helper), so this triggers the load explicitly
before asserting the retry/degrade behavior."""
import importlib
import os
monkeypatch.delenv("UNSLOTH_ZOO_DISABLE_GPU_INIT", raising = False)
seen_env = []
class _GpuGatedBlocker:
def find_spec(
self,
name,
path = None,
target = None,
):
# Crash is in unsloth_zoo's __init__, so intercept "unsloth_zoo" itself (the parent).
if name == "unsloth_zoo":
# Record the env each attempt sees; raise the no-GPU error both times so the shim
# degrades.
seen_env.append(os.environ.get("UNSLOTH_ZOO_DISABLE_GPU_INIT"))
raise NotImplementedError("Unsloth cannot find any torch accelerator")
return None
finder = _GpuGatedBlocker()
saved = {
k: v
for k, v in list(sys.modules.items())
if k == "unsloth_zoo" or k.startswith("unsloth_zoo.")
}
for k in saved:
del sys.modules[k]
saved_shim = sys.modules.pop("utils.hf_xet_fallback", None)
sys.meta_path.insert(0, finder)
try:
degraded = importlib.import_module("utils.hf_xet_fallback")
# Import is light (lazy backend); unsloth_zoo not loaded yet.
assert seen_env == [], seen_env
# First use of a heavy helper triggers the load (attempt without the light env, then a retry
# with it set); accessing DownloadStallError drives it via __getattr__.
stall_error = degraded.DownloadStallError
assert seen_env == [None, "1"], seen_env
# Both attempts raised -> Studio still boots in degraded mode.
assert issubclass(stall_error, RuntimeError)
# The env override must not leak past the load.
assert os.environ.get("UNSLOTH_ZOO_DISABLE_GPU_INIT") is None
finally:
sys.meta_path.remove(finder)
sys.modules.pop("utils.hf_xet_fallback", None)
sys.modules.update(saved)
if saved_shim is not None:
sys.modules["utils.hf_xet_fallback"] = saved_shim
def test_importing_child_should_disable_xet_stays_light(monkeypatch):
"""Regression guard for the stale-transformers-sidecar bug: importing the shim (and
``child_should_disable_xet``) must NOT pull in ``transformers``/``unsloth_zoo``. The worker calls
this at startup to decide the Xet env flip BEFORE activating the sidecar; an eager import here
would cache the default transformers 4.57.x in sys.modules, defeating the sidecar sys.path prepend
and breaking 5.x models (Qwen3.5/GLM/gemma-4)."""
import importlib
for name in [
m
for m in list(sys.modules)
if m == "transformers"
or m.startswith("transformers.")
or m == "unsloth_zoo"
or m.startswith("unsloth_zoo.")
or m == "utils.hf_xet_fallback"
]:
monkeypatch.delitem(sys.modules, name, raising = False)
mod = importlib.import_module("utils.hf_xet_fallback")
# The lightweight decision works without the heavy backend.
assert mod.child_should_disable_xet({"disable_xet": True}) is True
assert mod.child_should_disable_xet({}) is False
# And nothing heavy was imported as a side effect.
assert "transformers" not in sys.modules, "importing the shim must not import transformers"
assert "unsloth_zoo" not in sys.modules, "importing the shim must not import unsloth_zoo"