Merge remote-tracking branch 'origin/main' into r5748

# Conflicts:
#	install.ps1
#	install.sh
#	studio/install_python_stack.py
This commit is contained in:
Daniel Han 2026-07-21 02:22:53 +00:00
commit fe8fd5c999
123 changed files with 15209 additions and 1586 deletions

View file

@ -104,3 +104,93 @@ def test_chat_template_does_not_leak_sentinel_when_section_starts_with_it(chat_t
)
assert "{INPUT}" not in jinja_template
assert "{OUTPUT}" not in jinja_template
_SYSTEM_CHAT_TEMPLATE = (
"{SYSTEM}\n"
"### User: {INPUT}\n### Assistant: {OUTPUT}</s>"
"### User: {INPUT}\n### Assistant: {OUTPUT}</s>"
)
def _render(jinja_template, messages):
from jinja2.sandbox import ImmutableSandboxedEnvironment
env = ImmutableSandboxedEnvironment()
env.globals["raise_exception"] = lambda message: (_ for _ in ()).throw(RuntimeError(message))
return env.from_string(jinja_template).render(
messages = messages,
bos_token = "<s>",
eos_token = "</s>",
add_generation_prompt = False,
)
@pytest.mark.parametrize("default_system_message", [None, "You are helpful."])
def test_system_message_is_consumed_by_the_system_part(default_system_message):
"""A caller-supplied system message must be rendered by the system part and
skipped by the message loop, whatever `default_system_message` is.
With `default_system_message = None` the generated template used to bind
`loop_messages` only inside the `{% if %}` arm. The `Fix missing
loop_messages` step then saw no unconditional binding, rewrote the loop back
to `messages`, and the system message reached the loop and tripped
`raise_exception`.
"""
_, jinja_template, _, _ = construct_chat_template(
tokenizer = _SuccessFakeTokenizer(),
chat_template = _SYSTEM_CHAT_TEMPLATE,
default_system_message = default_system_message,
extra_eos_tokens = ["</s>"],
)
rendered = _render(
jinja_template,
[
{"role": "system", "content": "Be terse."},
{"role": "user", "content": "Hi"},
],
)
assert rendered.count("Be terse.") == 1
assert rendered.count("Hi") == 1
# A caller system message overrides the default; the default must not leak in.
if default_system_message is not None:
assert default_system_message not in rendered
def test_absent_system_message_still_renders_without_default():
"""`default_system_message = None` with no system message in the input must
keep working -- the `{% else %}` arm has to bind `loop_messages = messages`."""
_, jinja_template, _, _ = construct_chat_template(
tokenizer = _SuccessFakeTokenizer(),
chat_template = _SYSTEM_CHAT_TEMPLATE,
default_system_message = None,
extra_eos_tokens = ["</s>"],
)
rendered = _render(jinja_template, [{"role": "user", "content": "Hi"}])
assert "Hi" in rendered
_NO_SYSTEM_CHAT_TEMPLATE = (
"PREAMBLE\n"
"### User: {INPUT}\n### Assistant: {OUTPUT}</s>"
"### User: {INPUT}\n### Assistant: {OUTPUT}</s>"
)
def test_static_prefix_without_system_still_rejects_system_message():
"""A template with a static prefix but no {SYSTEM} placeholder cannot render a
caller system message, so it must still raise rather than silently drop it."""
_, jinja_template, _, _ = construct_chat_template(
tokenizer = _SuccessFakeTokenizer(),
chat_template = _NO_SYSTEM_CHAT_TEMPLATE,
default_system_message = None,
extra_eos_tokens = ["</s>"],
)
with pytest.raises(RuntimeError, match = "Only user and assistant roles are supported!"):
_render(
jinja_template,
[
{"role": "system", "content": "Be terse."},
{"role": "user", "content": "Hi"},
],
)

View file

@ -10,6 +10,8 @@ import pytest
REPO_ROOT = Path(__file__).resolve().parents[2]
INSTALL_SH = REPO_ROOT / "install.sh"
INSTALL_PS1 = REPO_ROOT / "install.ps1"
SETUP_PS1 = REPO_ROOT / "studio" / "setup.ps1"
STACK_PY = REPO_ROOT / "studio" / "install_python_stack.py"
class TestNoTorchBackendAutoInInstallSh:
@ -180,3 +182,607 @@ class TestUvBytecodeCompileTimeout:
assert (
'$env:UV_COMPILE_BYTECODE_TIMEOUT = "180"' in text
), "install.ps1 should default UV_COMPILE_BYTECODE_TIMEOUT"
class TestTorchIndexOverrideParity:
"""Every installer must honor UNSLOTH_TORCH_INDEX_URL / _FAMILY so a pinned wheel
index wins over GPU probing on all platforms (no asymmetric, per-OS coverage)."""
@pytest.mark.parametrize(
"path",
[INSTALL_SH, INSTALL_PS1, SETUP_PS1, STACK_PY],
ids = ["install.sh", "install.ps1", "setup.ps1", "install_python_stack.py"],
)
def test_installer_reads_override_env(self, path):
text = path.read_text(encoding = "utf-8")
for var in ("UNSLOTH_TORCH_INDEX_URL", "UNSLOTH_TORCH_INDEX_FAMILY"):
assert var in text, f"{path.name} does not honor {var}"
@pytest.mark.parametrize(
"path",
[INSTALL_PS1, SETUP_PS1],
ids = ["install.ps1", "setup.ps1"],
)
def test_amd_reroute_guarded_when_pinned(self, path):
# The AMD ROCm reroute must be skipped when the index is explicitly pinned,
# so an explicit cpu / cu* / rocm pin on an AMD host is not overwritten.
text = path.read_text(encoding = "utf-8")
assert (
"TorchIndexPinned" in text
), f"{path.name} should gate the AMD ROCm reroute on a pinned-index flag"
def test_cuda_pin_overrides_cvd_hide_gate(self):
# A pinned cu* index skips ALL host-GPU probing, so the CUDA repair must clear the
# CUDA_VISIBLE_DEVICES hide gate too (else the GPU-less CI case bails).
text = STACK_PY.read_text(encoding = "utf-8")
m = re.search(r"def _ensure_cuda_torch\(\).*?(?=\ndef )", text, re.DOTALL)
assert m, "could not locate _ensure_cuda_torch"
body = m.group(0)
assert "_cuda_pinned" in body, (
"_ensure_cuda_torch should compute a CUDA-pin flag so the pin can "
"override the CVD hide gate"
)
assert re.search(
r"if not _cuda_pinned and _cvd is not None", body
), "the CVD hide gate must be bypassed when a CUDA index is pinned"
def test_cpu_repair_pins_supported_torch_range(self):
# The explicit-CPU repair must use the bounded CPU/CUDA spec, not a bare trio (the
# /cpu index serves torch 2.11+, so a bare install could resolve out of range).
text = STACK_PY.read_text(encoding = "utf-8")
m = re.search(r"def _ensure_cpu_torch\(\).*?(?=\ndef )", text, re.DOTALL)
assert m, "could not locate _ensure_cpu_torch"
body = m.group(0)
assert "_CPU_TORCH_PKG_SPEC" in body, (
"_ensure_cpu_torch should install the bounded _CPU_TORCH_PKG_SPEC, "
"not a bare torch/torchvision/torchaudio trio"
)
def test_setup_ps1_stale_check_gates_rocm_on_supported_arch(self):
# The stale check must expect ROCm torch only for arches the install path maps to a
# repo.amd.com index; expecting "rocm" for an unmapped arch marks a good CPU venv stale.
text = SETUP_PS1.read_text(encoding = "utf-8")
assert "_rocmWheelArches" in text, (
"setup.ps1 stale check should restrict the ROCm expected-tag to the "
"supported gfx wheel arches"
)
class TestGfx211AllowlistParity:
"""The gfx per-arch 2.11-floor leaves (gfx120X-all / gfx1151 / gfx1150) must be the
SAME set in every installer and its stale/mismatch check. When they diverged, a
pinned gfx110X-all / gfx90a / gfx908 wheel (<2.11) was force-reinstalled every update."""
EXPECTED = {"gfx120x-all", "gfx1151", "gfx1150"}
def test_install_sh_allowlist(self):
text = INSTALL_SH.read_text(encoding = "utf-8").lower()
# install.sh: the TORCH_CONSTRAINT case (rocm7.2|gfx120x-all|gfx1151|gfx1150).
m = re.search(r"rocm7\.2\|gfx120x-all\|gfx1151\|gfx1150", text)
assert m, "install.sh gfx-2.11 allowlist case not found / changed"
def test_install_ps1_allowlist(self):
text = INSTALL_PS1.read_text(encoding = "utf-8").lower()
m = re.search(r"@\('gfx120x-all',\s*'gfx1151',\s*'gfx1150'\)", text)
assert m, "install.ps1 $_pinGfx211 allowlist not found / changed"
def test_setup_ps1_defines_single_allowlist_helper(self):
# setup.ps1 must define the allowlist once (Test-RocmGfx211Leaf) and reuse it, so
# the stale check and install spec can't disagree.
text = SETUP_PS1.read_text(encoding = "utf-8")
assert (
"function Test-RocmGfx211Leaf" in text
), "setup.ps1 should define a single Test-RocmGfx211Leaf allowlist helper"
assert re.search(
r"@\('gfx120x-all',\s*'gfx1151',\s*'gfx1150'\)", text.lower()
), "Test-RocmGfx211Leaf should hold the gfx-2.11 allowlist"
assert "$_pinGfx211 = Test-RocmGfx211Leaf" in text, (
"setup.ps1 install-spec path should reuse Test-RocmGfx211Leaf, not "
"re-hardcode the allowlist (they must not diverge)"
)
def test_stack_py_allowlist(self):
text = STACK_PY.read_text(encoding = "utf-8").lower()
assert (
'"gfx120x-all", "gfx1151", "gfx1150"' in text
), "install_python_stack.py _ROCM_GFX_TORCH211_LEAVES not found / changed"
class TestCudaLeafDigitParity:
"""A wheel-family leaf is CUDA only when it is "cu" + digits (cu118/cu128/...).
A bare cu* glob wrongly catches mirror leaves like /custom or /current; when
that happened the venv was marked stale and rebuilt on every run. Every
installer must require a digit after "cu" in its family/CUDA classification."""
def test_stack_py_requires_cu_digit(self):
text = STACK_PY.read_text(encoding = "utf-8")
# EXACT cu+digits: a custom leaf like cu128-private must route to the
# verbatim/unknown path, not be compared against the installed +cu128 tag.
assert re.search(
r'r"cu\[0-9\]\+"', text
), "install_python_stack.py _is_cuda_family_leaf must fullmatch cu[0-9]+"
def test_setup_ps1_requires_cu_digit(self):
text = SETUP_PS1.read_text(encoding = "utf-8")
# EXACT cu+digits: cu128-private must not classify as CUDA (it would become
# the expected tag and rebuild the venv on every update).
assert re.search(
r"'\^cu\[0-9\]\+\$'", text
), "setup.ps1 Test-CudaFamilyLeaf must match ^cu[0-9]+$, not a cu* prefix"
# The stale-venv branch must go through the digit-guarded helper.
assert (
"Test-CudaFamilyLeaf $_pinLeaf" in text
), "setup.ps1 stale check should classify CUDA via Test-CudaFamilyLeaf"
def test_install_ps1_requires_cu_digit_in_gpu_branch(self):
text = INSTALL_PS1.read_text(encoding = "utf-8")
assert re.search(
r"'\^cu\[0-9\]'", text
), "install.ps1 Get-TauriGpuBranch must require a digit after cu"
def test_install_sh_requires_cu_digit_in_gpu_branch(self):
text = INSTALL_SH.read_text(encoding = "utf-8")
# The _tauri_gpu_branch cuda case must be cu[0-9]*, not a bare cu*.
assert re.search(
r"cu\[0-9\]\*\)\s*echo \"cuda\"", text
), "install.sh _tauri_gpu_branch cuda case must be cu[0-9]*, not cu*"
def test_install_sh_backend_export_requires_cu_digit(self):
text = INSTALL_SH.read_text(encoding = "utf-8")
# Brand CUDA only on cu[0-9]*; a bare catch-all *) -> cuda would mis-brand
# /current, /custom pins and skip ROCm repair on AMD hosts.
assert re.search(
r'cu\[0-9\]\*\)\s*export UNSLOTH_TORCH_BACKEND="cuda"', text
), "install.sh backend export must brand cuda only on cu[0-9]*"
# An unknown leaf must NOT commit a cuda backend (it unsets instead).
assert re.search(
r"\*\)\s*unset UNSLOTH_TORCH_BACKEND", text
), "install.sh backend export must unset (not force cuda) on an unknown leaf"
def test_install_sh_lowercases_backend_leaf(self):
text = INSTALL_SH.read_text(encoding = "utf-8")
# The leaf feeding both the backend case and the 2.11 floor case must be
# lowercased so the canonical gfx120X-all (capital X) matches.
assert re.search(
r"_torch_index_leaf=\$\(printf '%s' \"\$_torch_index_leaf\" \| tr '\[:upper:\]' '\[:lower:\]'\)",
text,
), "install.sh must lowercase _torch_index_leaf before the gfx/rocm/cu case matches"
class TestKnown211SetParity:
"""The KNOWN-2.11 rocm/gfx set must be identical across all four installers:
exactly {rocm7.2} plus the gfx allowlist {gfx120x-all, gfx1151, gfx1150}.
rocm7.3 / torch 2.12 do not exist, so no side may floor them speculatively."""
def test_install_sh_known_211_leaf_is_rocm72_and_gfx_allowlist(self):
text = INSTALL_SH.read_text(encoding = "utf-8")
# The 2.11 floor case matches exactly rocm7.2 + the three gfx leaves.
assert re.search(
r"rocm7\.2\|gfx120x-all\|gfx1151\|gfx1150\)", text
), "install.sh 2.11 floor must be exactly rocm7.2|gfx120x-all|gfx1151|gfx1150"
# No speculative rocm7.3 anywhere.
assert "rocm7.3" not in text, "install.sh must not reference a non-existent rocm7.3"
def test_python_known_211_versions_is_only_rocm72(self):
text = STACK_PY.read_text(encoding = "utf-8")
assert "_ROCM_KNOWN_TORCH211_VERSIONS" in text
# The frozenset literal is exactly {(7, 2)}.
m = re.search(r"_ROCM_KNOWN_TORCH211_VERSIONS[^=]*=\s*frozenset\(\{([^}]*)\}\)", text)
assert m is not None, "install_python_stack.py must define _ROCM_KNOWN_TORCH211_VERSIONS"
assert "(7, 2)" in m.group(1)
assert "7, 3" not in m.group(1) and "7, 1" not in m.group(1)
def test_setup_ps1_known_211_helper_is_only_rocm72(self):
text = SETUP_PS1.read_text(encoding = "utf-8")
assert "Test-RocmKnown211Version" in text
# The predicate is Major -eq 7 -and Minor -eq 2 (only rocm7.2).
assert re.search(
r"Test-RocmKnown211Version[\s\S]{0,400}\$Major -eq 7 -and \$Minor -eq 2", text
), "setup.ps1 Test-RocmKnown211Version must accept only rocm7.2"
def test_install_ps1_pin_floor_is_only_rocm72(self):
text = INSTALL_PS1.read_text(encoding = "utf-8")
# The pinned-ROCm install-spec floor must be Major -eq 7 -and Minor -eq 2,
# not the speculative >= 2 that would floor a non-existent rocm7.3.
assert re.search(
r"\$_pinRocm211 = \(\[int\]\$Matches\[1\] -eq 7 -and \[int\]\$Matches\[2\] -eq 2\)",
text,
), "install.ps1 pinned-ROCm floor must be rocm7.2 only (no speculative >= 2)"
def test_ps1_pin_floor_gate_is_anchored(self):
"""The floor-selection gate that reads $_pinRocm211 from the raw leaf must anchor
the rocm match ($), or a suffixed custom leaf (rocm7.2-private) matches the rocm7.2
prefix, takes the 2.11-floor branch, and is force-routed through the ROCm path
before the exact-match elseif can send it to the verbatim install (Codex P2)."""
for path, label in ((INSTALL_PS1, "install.ps1"), (SETUP_PS1, "setup.ps1")):
text = path.read_text(encoding = "utf-8")
assert "-match '^rocm(\\d+)\\.(\\d+)$'" in text, (
f"{label} floor gate must anchor the rocm match (^rocm(\\d+)\\.(\\d+)$) so a "
"suffixed custom leaf is not floored/routed as rocm7.2"
)
assert (
"-match '^rocm(\\d+)\\.(\\d+)'\n" not in text
), f"{label} floor gate must not use the unanchored ^rocm(\\d+)\\.(\\d+) prefix"
def test_install_ps1_bounds_unknown_leaf_pinned_torch(self):
"""install.ps1's pinned-torch install must bound BOTH companions on EVERY
index, cu<digits> families included: torchaudio 2.11 dropped its exact torch
pin from the wheel metadata, so a bare companion beside torch<2.11 can
resolve a mismatched 2.11.0 build (Codex P2, then unconditional per the
torchaudio 2.11 unpinning)."""
text = INSTALL_PS1.read_text(encoding = "utf-8")
assert (
'$_pinVisionSpec = "torchvision>=0.19,<0.26.0"' in text
), "install.ps1 custom-pin install must bound torchvision (>=0.19,<0.26.0)"
assert (
'$_pinAudioSpec = "torchaudio>=2.4,<2.11.0"' in text
), "install.ps1 custom-pin install must bound torchaudio (>=2.4,<2.11.0)"
# No cu-family exemption: the bounds apply unconditionally.
assert (
"$_pinCuLeaf" not in text
), "install.ps1 must bound companions on every index (no cu-family exemption)"
# The bounded companions must actually be passed to the install command.
assert re.search(
r'"torch>=2\.4,<2\.11\.0" \$_pinVisionSpec \$_pinAudioSpec --default-index \$TorchIndexUrl',
text,
), "install.ps1 custom-pin install must pass the bounded companion specs to uv"
def test_gfx_allowlist_matches_across_installers(self):
# The gfx 2.11 allowlist {gfx120x-all, gfx1151, gfx1150} must appear in each.
gfx = ("gfx120x-all", "gfx1151", "gfx1150")
for path, label in (
(INSTALL_SH, "install.sh"),
(INSTALL_PS1, "install.ps1"),
(SETUP_PS1, "setup.ps1"),
(STACK_PY, "install_python_stack.py"),
):
low = path.read_text(encoding = "utf-8").lower()
for g in gfx:
assert g in low, f"{label} missing gfx 2.11 allowlist member {g}"
class TestPinnedRocmLeafDigitParity:
"""A pinned index is a pip ROCm --default-index family only when its leaf is an
EXACT rocm+digits (rocm7 / rocm7.2) or gfx*. A ^rocm[0-9] PREFIX (or a bare rocm*
glob) wrongly catches a custom mirror / find-links leaf (rocm-current /
rocm-rel-7.2.1) AND a suffixed private-mirror leaf (rocm7.2-private / rocm7-current),
routing it through the ROCm install path (which silently falls back to CPU on
failure) or skipping the custom-index companion bounds, instead of the verbatim
--default-index install. All installers must match the family EXACTLY: Python and
install.sh via a shared _is_pip_rocm_family_leaf, setup.ps1 via Test-PipRocmFamilyLeaf,
install.ps1 via an anchored ^rocm[0-9]+(\\.[0-9]+)?$ reroute."""
def test_install_ps1_pinned_reroute_requires_rocm_digit(self):
text = INSTALL_PS1.read_text(encoding = "utf-8")
# The pinned gfx*/rocm reroute must match rocm EXACTLY (anchored), so a suffixed
# rocm7.2-private / rocm-current falls through to the verbatim --default-index path.
assert "-match '^rocm[0-9]+(\\.[0-9]+)?$'" in text, (
"install.ps1 pinned-index reroute must anchor the rocm match "
"(^rocm[0-9]+(\\.[0-9]+)?$), not a bare -like 'rocm*' or an unanchored ^rocm\\d"
)
# Neither the broad glob nor the unanchored prefix may drive that reroute.
assert (
"-like 'rocm*'" not in text
), "install.ps1 must not route a pinned index on a bare -like 'rocm*' glob"
assert (
"-match '^rocm\\d'" not in text
), "install.ps1 must not route a pinned index on an unanchored -match '^rocm\\d'"
def test_setup_ps1_pinned_reroute_requires_rocm_digit(self):
text = SETUP_PS1.read_text(encoding = "utf-8")
# setup.ps1 routes every family decision through Test-PipRocmFamilyLeaf, which
# anchors the rocm match so a suffixed custom leaf stays on the verbatim path.
assert (
"function Test-PipRocmFamilyLeaf" in text
), "setup.ps1 must define Test-PipRocmFamilyLeaf (the exact rocm/gfx family gate)"
assert "'^rocm[0-9]+(\\.[0-9]+)?$'" in text, (
"setup.ps1 Test-PipRocmFamilyLeaf must anchor the rocm match "
"(^rocm[0-9]+(\\.[0-9]+)?$) so rocm7.2-private / rocm-current stay verbatim"
)
pinned_block = text[text.find("$_pinGfx211 = Test-RocmGfx211Leaf") :][:2000]
assert (
"-like 'rocm*'" not in pinned_block
), "setup.ps1 pinned reroute must not route on a bare -like 'rocm*' glob"
def test_install_sh_repairable_requires_rocm_digit(self):
text = INSTALL_SH.read_text(encoding = "utf-8")
# _torch_index_repairable routes rocm/gfx through the exact-match helper.
assert (
"_is_pip_rocm_family_leaf" in text
), "install.sh must define/use _is_pip_rocm_family_leaf for the exact rocm gate"
# gfx needs a following digit: gfx-private / gfxfoo are custom verbatim pins.
assert re.search(
r'case "\$1" in\n\s*gfx\[0-9\]\*\) return 0', text
), "install.sh _is_pip_rocm_family_leaf must treat only gfx<digit>* as a family"
assert not re.search(
r'case "\$1" in\n\s*gfx\*\) return 0', text
), "install.sh _is_pip_rocm_family_leaf must not family-match a bare gfx* glob"
def test_stack_py_pip_rocm_family_requires_digit(self):
text = STACK_PY.read_text(encoding = "utf-8")
assert re.search(
r'fullmatch\(r"rocm\\d\+\(\?:\\\.\\d\+\)\?", leaf\)', text
), "install_python_stack.py _is_pip_rocm_family_leaf must fullmatch rocm\\d+(?:\\.\\d+)?"
# The unanchored prefix must be gone from the family/flavor gates.
assert (
're.match(r"^rocm\\d"' not in text
), "install_python_stack.py must not gate a family on an unanchored re.match(^rocm\\d)"
def test_install_sh_rocm_side_effects_digit_gated(self):
"""The AMD bitsandbytes + 'repair ROCm torch' side effects must fire only on
an EXACT ROCm family (rocm7.2/gfx*), not a bare */rocm* whole-URL glob nor a
^rocm[0-9] prefix that catches a custom CPU/CUDA index like /rocm-current or a
suffixed /rocm7.2-private and force-repairs it from the wrong --default-index."""
text = INSTALL_SH.read_text(encoding = "utf-8")
assert (
'if _is_pip_rocm_family_leaf "$_torch_index_leaf"; then\n _torch_index_is_rocm_family=true'
in text
), "install.sh must set _torch_index_is_rocm_family from the exact-match helper"
assert (
'[ "$_torch_index_is_rocm_family" = true ]' in text
), "install.sh ROCm bnb/repair hooks must gate on _torch_index_is_rocm_family"
assert (
"*/rocm*|*/gfx*)\n _install_bnb_rocm" not in text
), "install.sh must not gate _install_bnb_rocm on a bare */rocm* whole-URL glob"
class TestPinnedIndexClearsUvEnvParity:
"""Every installer must neutralise the uv index env vars for a pinned torch
install (#6898). uv treats the default index (--index-url / --default-index) as
lowest priority, so an inherited UV_INDEX / UV_EXTRA_INDEX_URL mirror would win
under uv's first-index strategy and pull torch from the wrong index -- after
which the pinned wheel index is silently never used."""
UV_VARS = ("UV_DEFAULT_INDEX", "UV_INDEX_URL", "UV_INDEX", "UV_EXTRA_INDEX_URL")
def test_install_sh_clears_uv_index_vars(self):
text = INSTALL_SH.read_text(encoding = "utf-8")
assert (
"env -u UV_DEFAULT_INDEX -u UV_INDEX_URL -u UV_INDEX -u UV_EXTRA_INDEX_URL" in text
), "install.sh run_install_cmd must clear the uv index vars for --default-index installs"
def test_install_ps1_clears_uv_index_vars(self):
text = INSTALL_PS1.read_text(encoding = "utf-8")
for var in self.UV_VARS:
assert var in text, f"install.ps1 must clear {var} for pinned installs"
def test_setup_ps1_clears_uv_index_vars(self):
text = SETUP_PS1.read_text(encoding = "utf-8")
for var in self.UV_VARS:
assert var in text, f"setup.ps1 must clear {var} for pinned installs"
def test_stack_py_clears_uv_index_vars(self):
text = STACK_PY.read_text(encoding = "utf-8")
assert "_install_env_for_cmd" in text, (
"install_python_stack.py must scrub inherited uv index vars for pinned "
"installs via _install_env_for_cmd (parity with install.sh #6898)"
)
for var in self.UV_VARS:
assert var in text, f"install_python_stack.py must clear {var} for pinned installs"
def test_all_installers_clear_uv_torch_backend(self):
"""uv's torch backend redirects torch resolution to its own per-backend
index even against an explicit pin, so every installer's pinned-install
scrub must clear UV_TORCH_BACKEND too."""
sh = INSTALL_SH.read_text(encoding = "utf-8")
assert "-u UV_TORCH_BACKEND" in sh, "install.sh pinned scrub must clear UV_TORCH_BACKEND"
for path in (INSTALL_PS1, SETUP_PS1):
text = path.read_text(encoding = "utf-8")
assert (
"'UV_TORCH_BACKEND'" in text
), f"{path.name} pinned scrub must clear UV_TORCH_BACKEND"
stack = STACK_PY.read_text(encoding = "utf-8")
assert (
'"UV_TORCH_BACKEND",' in stack
), "install_python_stack.py strip tuple must include UV_TORCH_BACKEND"
def test_stack_py_strips_pip_extra_index_for_pip_fallback(self):
"""The pip fallback honours PIP_EXTRA_INDEX_URL (pip adds it IN ADDITION
to --index-url), so the pinned-command scrub must strip it."""
stack = STACK_PY.read_text(encoding = "utf-8")
assert (
'"PIP_EXTRA_INDEX_URL",' in stack
), "install_python_stack.py strip tuple must include PIP_EXTRA_INDEX_URL"
def test_all_installers_scrub_find_links(self):
"""uv's --find-links (env UV_FIND_LINKS) adds candidate locations that can
satisfy torch off a pinned index; every pinned-install scrub must clear it."""
sh = INSTALL_SH.read_text(encoding = "utf-8")
assert "-u UV_FIND_LINKS" in sh
for path in (INSTALL_PS1, SETUP_PS1):
assert "'UV_FIND_LINKS'" in path.read_text(encoding = "utf-8"), path.name
stack = STACK_PY.read_text(encoding = "utf-8")
assert '"UV_FIND_LINKS",' in stack and '"PIP_FIND_LINKS",' in stack
def test_setup_ps1_scrub_covers_pip_fallback(self):
"""setup.ps1's Fast-Install must keep the scrub active through the pip
fallback (pip honours PIP_EXTRA_INDEX_URL / PIP_FIND_LINKS in addition to
--index-url); restoring the vars before the fallback reopens the hole."""
text = SETUP_PS1.read_text(encoding = "utf-8")
fi = text[text.find("function Fast-Install") :][:2500]
assert "'PIP_EXTRA_INDEX_URL'" in fi and "'PIP_FIND_LINKS'" in fi
# the pip fallback must sit INSIDE the try whose finally restores the vars
assert fi.find("python -m pip install") < fi.find(
"finally"
), "pip fallback must run before the scrub is restored"
def test_all_installers_disable_uv_config_for_pinned_installs(self):
"""A DISCOVERED uv.toml / pyproject [tool.uv] outranks the CLI pin
(verified with uv 0.10: [pip] torch-backend = "cpu" and a non-default
[[index]] both resolve torch+cpu against an explicit --index-url /
--default-index cu126 pin; UV_NO_CONFIG=1 restores the pin). Every
installer's pinned scrub must set UV_NO_CONFIG=1 and drop UV_CONFIG_FILE."""
sh = INSTALL_SH.read_text(encoding = "utf-8")
assert "-u UV_CONFIG_FILE UV_NO_CONFIG=1" in sh, (
"install.sh run_install_cmd must set UV_NO_CONFIG=1 and drop "
"UV_CONFIG_FILE for --default-index installs"
)
for path in (INSTALL_PS1, SETUP_PS1):
text = path.read_text(encoding = "utf-8")
assert "'UV_CONFIG_FILE'" in text, f"{path.name} must drop UV_CONFIG_FILE"
assert (
"$env:UV_NO_CONFIG = '1'" in text
), f"{path.name} must set UV_NO_CONFIG=1 for pinned installs"
stack = STACK_PY.read_text(encoding = "utf-8")
assert (
'"UV_CONFIG_FILE",' in stack
), "install_python_stack.py strip tuple must include UV_CONFIG_FILE"
assert (
'env["UV_NO_CONFIG"] = "1"' in stack
), "_install_env_for_cmd must set UV_NO_CONFIG=1 for pinned installs"
def test_pip_fallbacks_disable_pip_config_files(self):
"""The pip FALLBACK (uv missing/failed) honours user/site pip config files
even with the PIP_* env vars stripped: `pip config set
global.extra-index-url` still adds indexes to a pinned install. pip loads
NO configuration files when PIP_CONFIG_FILE is the platform devnull, so
the two installers that HAVE a pip fallback (install_python_stack.py and
setup.ps1's Fast-Install) must set it in their pinned scrub. install.sh
and install.ps1 are uv-only (no python -m pip fallback) and need no
equivalent."""
stack = STACK_PY.read_text(encoding = "utf-8")
assert 'env["PIP_CONFIG_FILE"] = os.devnull' in stack, (
"_install_env_for_cmd must point PIP_CONFIG_FILE at os.devnull for "
"pinned installs (pip fallback isolation)"
)
setup = SETUP_PS1.read_text(encoding = "utf-8")
assert "$env:PIP_CONFIG_FILE = 'nul'" in setup, (
"setup.ps1 Fast-Install pinned scrub must point PIP_CONFIG_FILE at nul "
"(Windows devnull) so the pip fallback ignores user/site pip config"
)
assert (
"'PIP_CONFIG_FILE'" in setup
), "setup.ps1 must save/restore PIP_CONFIG_FILE around the pinned scrub"
def test_setup_ps1_bounds_unknown_leaf_pinned_torch(self):
"""A first-time/changed unknown-leaf custom pin routes through setup.ps1's
CUDA branch; install.ps1's fresh pinned install, install.sh, and the Python
verbatim path bound the WHOLE trio, so the Windows update path must too -- a
private mirror serving newer torch OR newer companions must not lift the venv
above the supported range under the pin."""
text = SETUP_PS1.read_text(encoding = "utf-8")
# The custom-leaf branch bounds torch AND both companions (parity with the
# other installers' custom-pin trio bounds), gated on a non-cu-family leaf.
for spec in (
'$cudaTorchSpec = "torch>=2.4,<2.11.0"',
'$cudaVisionSpec = "torchvision>=0.19,<0.26.0"',
'$cudaAudioSpec = "torchaudio>=2.4,<2.11.0"',
):
assert spec in text, f"setup.ps1 must bound the custom-leaf trio: {spec}"
assert (
"if ($TorchIndexPinned -and -not (Test-CudaFamilyLeaf $CuTag)) {" in text
), "the custom-leaf trio bounds must be gated on a pinned non-cu-family leaf"
assert (
"Fast-Install $cudaTorchSpec $cudaVisionSpec $cudaAudioSpec" in text
), "setup.ps1's CUDA branch must install via the bounded spec variables"
def test_setup_ps1_bounds_pinned_cpu_torch(self):
"""setup.ps1's CPU branch must bound the trio under an explicit pin (parity with
_CPU_TORCH_PKG_SPEC): the /cpu index serves newer torch, and _ensure_cpu_torch
keeps any CPU build, so a bare pinned trio could land an unsupported version.
An unpinned CPU host keeps the bare trio (pre-pin behavior unchanged)."""
text = SETUP_PS1.read_text(encoding = "utf-8")
for spec in (
'$cpuTorchSpec = "torch>=2.4,<2.12.0"',
'$cpuVisionSpec = "torchvision>=0.19,<0.27.0"',
'$cpuAudioSpec = "torchaudio>=2.4,<2.12.0"',
):
assert spec in text, f"setup.ps1 must bound the pinned CPU trio: {spec}"
assert (
"if ($TorchIndexPinned) {" in text
), "the CPU trio bounds must be gated on an explicit pin"
assert (
"Fast-Install $cpuTorchSpec $cpuVisionSpec $cpuAudioSpec @cpuForce" in text
), "setup.ps1's CPU branch must install via the spec variables"
# The ceilings mirror the Python repair spec exactly.
stack = STACK_PY.read_text(encoding = "utf-8")
spec_block = re.search(r"_CUDA_TORCH_PKG_SPEC[^(]*\(\s*(.*?)\)", stack, re.DOTALL)
assert spec_block and '"torch>=2.4,<2.12.0"' in spec_block.group(1), (
"_CPU_TORCH_PKG_SPEC (via _CUDA_TORCH_PKG_SPEC) must keep the torch<2.12 "
"ceiling the setup.ps1 pinned CPU branch mirrors"
)
def test_setup_ps1_stale_check_requires_rocm_digit(self):
"""The stale-venv check must use the same EXACT rocm/gfx gate as the install
selection (Test-PipRocmFamilyLeaf), or a custom rocm-* / suffixed rocm7.2-private
leaf is stale-compared as a family and force-reinstalls on every studio update."""
text = SETUP_PS1.read_text(encoding = "utf-8")
anchor = text.find("$_pinLeaf = Get-TorchIndexLeaf $_pinnedIdx")
assert anchor >= 0, "setup.ps1 stale check must classify the pinned leaf"
stale = text[anchor:][:2500]
assert (
"Test-PipRocmFamilyLeaf" in stale
), "setup.ps1 stale check must gate rocm leaves via the exact Test-PipRocmFamilyLeaf"
assert (
stale.count("-like 'rocm*'") == 0
), "setup.ps1 stale check must not use a bare -like 'rocm*' glob"
assert (
"-match '^rocm\\d'" not in stale
), "setup.ps1 stale check must not use an unanchored -match '^rocm\\d'"
class TestIndexPathSlashTrimParity:
"""Every installer must trim trailing PATH slashes only on the verbatim
UNSLOTH_TORCH_INDEX_URL override, preserving a ?query/#fragment token: a whole-URL
strip corrupts a base64 token ending in "/", a single strip leaves a double-slash leaf
empty. The helper must be DEFINED and WIRED into the override return in all four."""
def test_helper_defined_in_all_installers(self):
assert "def _trim_index_path_slashes(" in STACK_PY.read_text(encoding = "utf-8")
assert "_trim_index_path_slashes()" in INSTALL_SH.read_text(encoding = "utf-8")
assert "function Trim-IndexPathSlashes" in INSTALL_PS1.read_text(encoding = "utf-8")
assert "function Trim-IndexPathSlashes" in SETUP_PS1.read_text(encoding = "utf-8")
def test_helper_wired_into_override_in_all_installers(self):
assert "_trim_index_path_slashes(url)" in STACK_PY.read_text(encoding = "utf-8")
assert '_url=$(_trim_index_path_slashes "$_url")' in INSTALL_SH.read_text(encoding = "utf-8")
assert "Trim-IndexPathSlashes $env:UNSLOTH_TORCH_INDEX_URL" in INSTALL_PS1.read_text(
encoding = "utf-8"
)
assert "Trim-IndexPathSlashes $env:UNSLOTH_TORCH_INDEX_URL" in SETUP_PS1.read_text(
encoding = "utf-8"
)
class TestInstallOutputRedactionParity:
"""uv/pip failure text embeds the failing --index-url verbatim, so a captured install
log dumped on error can leak a user:token@ or ?token= secret. Every installer must
DEFINE a redaction helper and WIRE it into the captured-output print path."""
def test_helper_defined_in_all_installers(self):
assert "def _redact_install_output(" in STACK_PY.read_text(encoding = "utf-8")
assert "_redact_install_output()" in INSTALL_SH.read_text(encoding = "utf-8")
assert "function Redact-InstallOutput" in INSTALL_PS1.read_text(encoding = "utf-8")
assert "function Redact-InstallOutput" in SETUP_PS1.read_text(encoding = "utf-8")
def test_helper_wired_into_failure_print(self):
# install.sh dumps the captured log through the redactor on failure.
assert '_redact_install_output "$_log"' in INSTALL_SH.read_text(encoding = "utf-8")
# Both ps1 installers redact the captured $output before Write-Host on non-zero exit.
assert (
"Write-Host (Redact-InstallOutput $output) -ForegroundColor Red"
in INSTALL_PS1.read_text(encoding = "utf-8")
)
assert (
"Write-Host (Redact-InstallOutput $output) -ForegroundColor Red"
in SETUP_PS1.read_text(encoding = "utf-8")
)
# Python redacts the captured stdout before printing.
assert "_redact_install_output(" in STACK_PY.read_text(encoding = "utf-8")
class TestPipNoIndexScrubParity:
"""The plain-pip fallback honours PIP_*: PIP_NO_INDEX=1 makes it ignore ALL indexes
(defeating the pinned --index-url) and PIP_INDEX_URL replaces the pin. The two installers
that HAVE a plain-pip fallback (Python + setup.ps1) must scrub both for a pinned install.
install.sh / install.ps1 are uv-only (--default-index), which ignores pip config/env."""
def test_python_scrubs_pip_no_index_and_pip_index_url(self):
text = STACK_PY.read_text(encoding = "utf-8")
assert '"PIP_NO_INDEX"' in text
assert '"PIP_INDEX_URL"' in text
def test_setup_ps1_scrubs_pip_no_index_and_pip_index_url(self):
text = SETUP_PS1.read_text(encoding = "utf-8")
assert "'PIP_NO_INDEX'" in text
assert "'PIP_INDEX_URL'" in text

View file

@ -54,6 +54,24 @@ class TestBuildUvCmdTorchBackend:
a.startswith("--torch-backend") for a in cmd
), f"Empty UV_TORCH_BACKEND should not add flag, got: {cmd}"
def test_uv_torch_backend_skipped_for_pinned_index(self):
"""A pinned-index command must NOT get --torch-backend: uv's torch backend
redirects torch resolution to its own per-backend index even when
--index-url is given (verified: cu128 pin + backend cpu installs
torch+cpu), defeating the pin."""
for pin_flag in ("--index-url", "--default-index"):
with mock.patch.dict(os.environ, {"UV_TORCH_BACKEND": "cpu"}):
cmd = self._call(("torch", pin_flag, "https://download.pytorch.org/whl/cu128"))
assert not any(
a.startswith("--torch-backend") for a in cmd
), f"{pin_flag} command must not carry --torch-backend, got: {cmd}"
def test_uv_torch_backend_kept_for_unpinned(self):
"""Non-pinned commands still honour UV_TORCH_BACKEND."""
with mock.patch.dict(os.environ, {"UV_TORCH_BACKEND": "cpu"}):
cmd = self._call(("somepackage",))
assert "--torch-backend=cpu" in cmd
class TestUvSafePath:
"""_uv_safe_path hands uv a space-free `-c`/`-r` path (issue #6503)."""
@ -148,3 +166,119 @@ class TestUvSafePathHardening:
assert " " not in value
assert Path(value).read_text() == "transformers>=4.57.6\n"
class TestPinnedIndexClearsUvEnv:
"""A pinned torch install (--index-url / --default-index) must neutralise an
inherited UV_INDEX / UV_EXTRA_INDEX_URL so the pinned wheel index wins.
uv treats the default index (--index-url / --default-index) as LOWEST priority,
so an inherited UV_INDEX / UV_EXTRA_INDEX_URL (a corporate/CPU mirror) would be
searched first and, under uv's default first-index strategy, resolve torch from
the wrong mirror -- after which the marker records a wheel index that was never
used. install.sh (#6898), install.ps1 and setup.ps1 already clear these for
pinned installs; install_python_stack must match (parity across all installers).
"""
UV_VARS = ("UV_DEFAULT_INDEX", "UV_INDEX_URL", "UV_INDEX", "UV_EXTRA_INDEX_URL")
def test_pinned_index_url_strips_uv_index_vars(self):
cmd = [
"uv",
"pip",
"install",
"--force-reinstall",
"torch",
"torchvision",
"torchaudio",
"--index-url",
"https://download.pytorch.org/whl/cu128",
]
with mock.patch.dict(
os.environ,
{
"UV_INDEX": "https://mirror.corp/simple",
"UV_EXTRA_INDEX_URL": "https://mirror.corp/extra",
"UV_INDEX_URL": "https://mirror.corp/root",
"UV_DEFAULT_INDEX": "https://mirror.corp/default",
},
):
env = ips._install_env_for_cmd(cmd)
assert env is not None, "a --index-url install must run with a scrubbed env"
for var in self.UV_VARS:
assert var not in env, f"{var} must be cleared for a pinned-index install"
def test_pinned_default_index_strips_uv_index_vars(self):
# --default-index must be gated too (matches install.sh / install.ps1).
cmd = ["uv", "pip", "install", "torch", "--default-index", "https://x/cu126"]
with mock.patch.dict(os.environ, {"UV_INDEX": "https://mirror.corp/simple"}):
env = ips._install_env_for_cmd(cmd)
assert env is not None
assert "UV_INDEX" not in env
def test_non_pinned_install_keeps_user_mirror(self):
# A plain install (no --index-url) must NOT scrub the env, so a user's mirror
# still applies to base packages.
cmd = ["uv", "pip", "install", "unsloth", "unsloth-zoo"]
with mock.patch.dict(os.environ, {"UV_INDEX": "https://mirror.corp/simple"}):
env = ips._install_env_for_cmd(cmd)
assert env is None, "non-pinned installs must inherit the caller env unchanged"
def test_scrubbed_env_preserves_other_vars(self):
cmd = ["uv", "pip", "install", "torch", "--index-url", "https://x/cu128"]
with mock.patch.dict(
os.environ,
{"UV_INDEX": "https://mirror.corp/simple", "PATH_SENTINEL_XYZ": "keepme"},
):
env = ips._install_env_for_cmd(cmd)
assert env is not None
assert env.get("PATH_SENTINEL_XYZ") == "keepme", "only uv index vars are removed"
def test_pinned_cmd_strips_pip_extra_index_url(self):
"""PIP_EXTRA_INDEX_URL is stripped for pinned commands so the pip
fallback cannot satisfy torch from an inherited extra index."""
with mock.patch.dict(os.environ, {"PIP_EXTRA_INDEX_URL": "https://mirror/simple"}):
env = ips._install_env_for_cmd(
["pip", "install", "torch", "--index-url", "https://x/cu128"]
)
assert env is not None and "PIP_EXTRA_INDEX_URL" not in env
def test_pinned_cmd_strips_uv_torch_backend(self):
"""UV_TORCH_BACKEND is stripped for pinned commands so uv cannot read it
from the environment and reroute torch off the pinned index."""
with mock.patch.dict(os.environ, {"UV_TORCH_BACKEND": "cpu"}):
env = ips._install_env_for_cmd(
["uv", "pip", "install", "torch", "--index-url", "https://x/cu128"]
)
assert env is not None and "UV_TORCH_BACKEND" not in env
def test_pinned_cmd_disables_uv_config_discovery(self):
"""A DISCOVERED uv.toml / pyproject [tool.uv] outranks the CLI pin too
(verified with uv 0.10: [pip] torch-backend = "cpu" and a non-default
[[index]] both resolve torch+cpu against an explicit --index-url /
--default-index cu126 pin). Pinned commands must run with UV_NO_CONFIG=1
and without an inherited UV_CONFIG_FILE."""
with mock.patch.dict(os.environ, {"UV_CONFIG_FILE": "/etc/uv/uv.toml"}):
env = ips._install_env_for_cmd(
["uv", "pip", "install", "torch", "--index-url", "https://x/cu128"]
)
assert env is not None
assert env.get("UV_NO_CONFIG") == "1"
assert "UV_CONFIG_FILE" not in env
def test_pinned_cmd_disables_pip_config_files(self):
"""The pip FALLBACK honours user/site pip config files (pip config set
global.extra-index-url) even with the PIP_* env vars stripped; pip loads
NO configuration files when PIP_CONFIG_FILE is os.devnull. Harmless for
uv, decisive for the fallback."""
env = ips._install_env_for_cmd(
["uv", "pip", "install", "torch", "--index-url", "https://x/cu128"]
)
assert env is not None
assert env.get("PIP_CONFIG_FILE") == os.devnull
def test_non_pinned_cmd_keeps_uv_config_discovery(self):
"""Non-pinned installs inherit the caller env unchanged, so a user's uv
configuration still applies to base packages."""
env = ips._install_env_for_cmd(["uv", "pip", "install", "unsloth"])
assert env is None