diff --git a/docker/unsloth_pip_shim.py b/docker/unsloth_pip_shim.py index 5d793303c7..35bc2ffe29 100644 --- a/docker/unsloth_pip_shim.py +++ b/docker/unsloth_pip_shim.py @@ -11,9 +11,15 @@ carefully-resolved cu128 torch/vLLM/transformers stack: * `transformers==X` -> NOT installed into the base venv. The version X is recorded so the sidecar mechanism (unsloth_nb_compat) activates it for the model cells. The base stack stays intact. - * torch / torchvision / torchaudio / triton / xformers / vllm / bitsandbytes / - flashinfer / nvidia-* -> SKIPPED (the baked, ABI-matched versions are kept; - a notebook reinstall here only ever breaks the GPU stack). + * torch / torchvision / torchaudio / torchao / torchcodec / triton / xformers / + vllm / bitsandbytes / flashinfer / nvidia-* -> SKIPPED (the baked, + ABI-matched versions are kept; a notebook reinstall here only ever breaks + the GPU stack). + * trl / peft / datasets / accelerate / huggingface_hub / tokenizers / + safetensors -> SKIPPED for the same reason one level up: 382 of the shipped + notebooks end their install cell with `pip install --no-deps trl==0.22.2`, + which used to walk straight past this shim and downgrade the tested + trl 0.24.0 / peft 0.19.1 / datasets 4.3.0 on every single run. * everything else (omegaconf, snac, causal-conv1d, ...) -> passed through to the real tool unchanged, so notebooks that genuinely need extra packages still get them. @@ -30,10 +36,32 @@ REAL = {"pip": "/opt/unsloth-venv/bin/pip", "uv": "/opt/unsloth-venv/bin/uv"} MARKER = os.environ.get("UNSLOTH_NB_TF_MARKER", "/tmp/unsloth_nb/requested_transformers") # Packages whose baked version must never be changed by a notebook install cell. +# +# Membership criterion: replacing this package silently invalidates the stack the +# image was BUILT and TESTED against, or breaks unsloth outright. That is either +# (a) an ABI/CUDA-matched wheel the Dockerfile resolved deliberately (a PyPI +# reinstall swaps a +cu128 build for a generic or cu13 one), or (b) a library +# unsloth/unsloth_zoo monkey-patches by version at import time. Anything else -- +# including packages the notebook genuinely needs and the image does not bake +# (snac, causal-conv1d, omegaconf, mamba-ssm, ...) -- installs normally. +# +# Measured over the 433 shipped notebooks (probe_notebook_pins.py), the entries +# below the original torch/vLLM group cover: +# trl 382 notebooks pin an older release (0.22.2 x378, 0.15.2 x4) vs baked 0.24.0 +# torchao 2 pin 0.15.0, and 271 more reinstall it, replacing 0.17.0+cu128 +# torchcodec 26 pin 0.5 / 0.7.0, replacing the 0.11.0+cu128 wheel paired with torch 2.11 +# datasets 254 reinstall it; a trl 0.22.2 resolve pulled it back to 3.0.0 from 4.3.0 +# peft 225 reinstall it; observed dropping 0.19.1 -> 0.14.0 +# accelerate 225 reinstall it (Trainer/torch glue, patched by unsloth_zoo) +# hf hub 240 reinstall it; tokenizers 64. Both are version-locked to +# transformers, and the sidecars ship their own matched copies, so a +# base-venv swap desynchronises every sidecar at once. _KEEP = { "torch", "torchvision", "torchaudio", + "torchao", + "torchcodec", "triton", "triton-rocm", "pytorch-triton", @@ -45,6 +73,14 @@ _KEEP = { "unsloth", "unsloth-zoo", "unsloth_zoo", + "trl", + "peft", + "datasets", + "accelerate", + "huggingface-hub", + "huggingface_hub", + "tokenizers", + "safetensors", } _KEEP_PREFIX = ("nvidia-", "nvidia_") # pip/uv flags that consume the next token as a value (not a requirement). diff --git a/tests/python/test_docker_pip_shim_training_stack.py b/tests/python/test_docker_pip_shim_training_stack.py new file mode 100644 index 0000000000..0633814458 --- /dev/null +++ b/tests/python/test_docker_pip_shim_training_stack.py @@ -0,0 +1,196 @@ +# SPDX-License-Identifier: AGPL-3.0-only +# Copyright 2026-Present the Unsloth team. See /studio/LICENSE.AGPL-3.0 + +"""Regression guard for what the Docker pip shim protects. + +The shim fronts pip/uv inside the notebook kernel so a `!pip install` cell cannot +replace the baked, ABI-matched stack. It protected torch/vLLM/unsloth and stopped +there, which left the training stack wide open. Measured over the 433 shipped +notebooks (probe_notebook_pins.py against the baked image): + + trl 382 notebooks pin an older release -- 378 of them end their + install cell with `!pip install --no-deps trl==0.22.2`, against + a baked and tested trl 0.24.0 + torchao 273 reinstall it, 2 pin 0.15.0, replacing 0.17.0+cu128 with a + generic PyPI build + torchcodec 92 reinstall it, 26 pin 0.5 / 0.7.0, replacing the 0.11.0+cu128 + wheel the Dockerfile deliberately paired with torch 2.11 + datasets 254 reinstall it; a trl 0.22.2 resolve was observed pulling it + back from 4.3.0 to 3.0.0 + peft 225 reinstall it; observed dropping 0.19.1 -> 0.14.0 + accelerate 225 reinstall it + hf_hub 240 reinstall it, tokenizers 64 -- both version-locked to + transformers, and the sidecars ship their own matched copies + +So EVERY notebook run silently mutated the stack the image was validated with, +and printed "Successfully installed trl-0.22.2 peft-0.14.0 datasets-3.0.0" while +the shim reported it was keeping the baked versions. + +The criterion for _KEEP is "replacing this invalidates the tested stack or breaks +unsloth", not "any package a notebook mentions": a package the notebook genuinely +needs and the image does not bake still has to install normally. + +Static: drives the shim's main() with os.execv captured. No docker, no GPU, no +network. +""" + +from __future__ import annotations + +import importlib.util +import os +from pathlib import Path + +import pytest + +REPO_ROOT = Path(__file__).resolve().parents[2] +SHIM_PATH = REPO_ROOT / "docker" / "unsloth_pip_shim.py" + +# The install cell 378 of the 433 shipped notebooks actually end on. +SHIPPED_TRL_CELL = ["--no-deps", "trl==0.22.2"] +# A package the image does NOT bake: must keep installing normally. +UNBAKED = "snac" + + +class _Exec(Exception): + def __init__(self, path, argv): + self.path = path + self.argv = list(argv) + + +@pytest.fixture() +def shim(tmp_path, monkeypatch): + monkeypatch.setenv("UNSLOTH_NB_TF_MARKER", str(tmp_path / "requested_transformers")) + monkeypatch.setenv("UNSLOTH_NB_SHIM", "1") + assert SHIM_PATH.is_file(), f"missing shim: {SHIM_PATH}" + spec = importlib.util.spec_from_file_location("unsloth_pip_shim_stack_test", SHIM_PATH) + mod = importlib.util.module_from_spec(spec) + spec.loader.exec_module(mod) + + def _fake_execv(path, argv): + raise _Exec(path, argv) + + monkeypatch.setattr(mod.os, "execv", _fake_execv) + return mod + + +def _run(shim, args, tool = "pip"): + """Return the args that reached the real tool after `install`, or None when + the shim no-op'd. The always-injected protected-constraints pair is dropped.""" + argv = ["uv", "pip", "install", *args] if tool == "uv" else ["pip", "install", *args] + with pytest.MonkeyPatch.context() as mp: + mp.setattr(shim.sys, "argv", argv) + try: + shim.main() + return None + except _Exec as exc: + i = exc.argv.index("install") + execd = exc.argv[i + 1 :] + if ( + len(execd) >= 2 + and execd[-2] == "--constraint" + and os.path.basename(execd[-1]).startswith("unsloth-nb-protected-") + ): + execd = execd[:-2] + return execd + + +# -------------------------------------------------------------------------- +# Membership +# -------------------------------------------------------------------------- +@pytest.mark.parametrize( + "pkg", + ["trl", "peft", "datasets", "accelerate", "torchao", "torchcodec", + "huggingface-hub", "tokenizers", "safetensors"], +) +def test_training_stack_is_protected(shim, pkg): + assert pkg in shim._KEEP, ( + f"{pkg} is baked and tested; a notebook pin replacing it invalidates the image" + ) + + +def test_the_original_gpu_stack_is_still_protected(shim): + for pkg in ["torch", "torchvision", "torchaudio", "triton", "xformers", + "vllm", "bitsandbytes", "unsloth", "unsloth-zoo"]: + assert pkg in shim._KEEP + + +def test_unrelated_packages_are_not_swept_in(shim): + # The criterion is "invalidates the tested stack", not "a notebook mentions + # it". These are all installed by shipped notebooks and must stay installable. + for pkg in ["snac", "causal-conv1d", "mamba-ssm", "omegaconf", "timm", + "librosa", "trackio", "open-spiel", "protobuf", "sentencepiece"]: + assert pkg not in shim._KEEP, f"{pkg} must still install for the notebooks that need it" + + +# -------------------------------------------------------------------------- +# Behaviour +# -------------------------------------------------------------------------- +def test_the_shipped_trl_cell_installs_nothing(shim): + # `!pip install --no-deps trl==0.22.2` is the last line of 378 notebooks. + assert _run(shim, SHIPPED_TRL_CELL) is None + + +def test_a_mixed_cell_keeps_only_the_unbaked_package(shim): + execd = _run( + shim, + ["--no-deps", "trl==0.22.2", "peft==0.14.0", "datasets==3.0.0", + "accelerate==1.0.0", UNBAKED], + ) + assert execd == ["--no-deps", UNBAKED], execd + + +def test_cuda_matched_wheels_are_not_replaced_by_pypi_builds(shim): + # torchao 0.17.0+cu128 and torchcodec 0.11.0+cu128 are resolved from the + # cu128 index; a PyPI pin swaps in a generic (or cu13) build. + assert _run(shim, ["torchao==0.15.0", "torchcodec==0.5"]) is None + + +def test_transformers_companions_cannot_desynchronise_the_sidecars(shim): + # Each sidecar ships its own matched huggingface_hub/tokenizers/safetensors; + # replacing the base-venv copies desynchronises every sidecar at once. + assert _run(shim, ["huggingface_hub==0.30.0", "tokenizers==0.20.0", + "safetensors==0.4.0"]) is None + + +def test_an_unbaked_package_still_installs(shim): + assert _run(shim, [UNBAKED]) == [UNBAKED] + assert _run(shim, [UNBAKED], tool = "uv") == [UNBAKED] + + +def test_protection_survives_a_requirements_file(shim, tmp_path): + req = tmp_path / "requirements.txt" + req.write_text(f"trl==0.22.2\npeft==0.14.0\ndatasets==3.0.0\n{UNBAKED}\n") + execd = _run(shim, ["-r", str(req)]) + assert execd is not None and execd[0] == "-r" + filtered = Path(execd[1]).read_text() + assert UNBAKED in filtered + for dropped in ("trl", "peft", "datasets"): + assert dropped not in filtered, f"{dropped} slipped through the requirements file" + + +def test_protection_survives_a_direct_wheel_url(shim): + url = "https://files.pythonhosted.org/x/trl-0.22.2-py3-none-any.whl" + assert _run(shim, [url, UNBAKED]) == [UNBAKED] + + +def test_protection_survives_an_editable_vcs_install(shim): + assert _run(shim, ["-e", "git+https://github.com/huggingface/trl.git", UNBAKED]) == [UNBAKED] + + +def test_forwarded_installs_pin_the_protected_set_for_the_resolver(shim): + # Argument filtering alone does not stop a dependency of the kept target from + # dragging peft/datasets back down -- which is how peft 0.19.1 became 0.14.0 + # with no notebook ever naming peft. Every forwarded install carries pins. + with pytest.MonkeyPatch.context() as mp: + mp.setattr(shim.sys, "argv", ["pip", "install", UNBAKED]) + with pytest.raises(_Exec) as exc: + shim.main() + argv = exc.value.argv + assert "--constraint" in argv + pins = Path(argv[argv.index("--constraint") + 1]).read_text() + names = {line.split("==")[0].lower().replace("_", "-") for line in pins.splitlines() if line} + # only the installed subset is pinned, but nothing outside the protected set + assert names, "the constraints file must not be empty" + assert all( + n in shim._KEEP or n == "transformers" or n.startswith("nvidia-") for n in names + ), sorted(names) diff --git a/tests/python/test_unsloth_pip_shim.py b/tests/python/test_unsloth_pip_shim.py index 7a9d818bed..0573efcf31 100644 --- a/tests/python/test_unsloth_pip_shim.py +++ b/tests/python/test_unsloth_pip_shim.py @@ -102,7 +102,7 @@ def _run(shim, tool, args): # -------------------------------------------------------------------------- # Item 3541142907 -- pair -e/--editable with its target. A protected editable -# drops the flag WITH its value (never `pip install -e peft`); an unprotected +# drops the flag WITH its value (never `pip install -e snac`); an unprotected # editable is forwarded verbatim. # -------------------------------------------------------------------------- UNSLOTH_VCS = "git+https://github.com/unslothai/unsloth.git#egg=unsloth" @@ -114,13 +114,13 @@ KEPT = object() @pytest.mark.parametrize( "args, expected", [ - pytest.param(["-e", UNSLOTH_VCS, "peft"], ["peft"], id = "sep-protected"), + pytest.param(["-e", UNSLOTH_VCS, "snac"], ["snac"], id = "sep-protected"), # nothing left to install -> no-op, no dangling -e pytest.param(["-e", UNSLOTH_VCS], None, id = "sep-only-protected-noop"), pytest.param(["-e", "./localpkg"], KEPT, id = "sep-unprotected-kept"), - pytest.param(["--editable=" + UNSLOTH_VCS, "peft"], ["peft"], id = "inline-protected"), + pytest.param(["--editable=" + UNSLOTH_VCS, "snac"], ["snac"], id = "inline-protected"), pytest.param(["--editable=./localpkg"], KEPT, id = "inline-unprotected-kept"), - pytest.param(["-e" + UNSLOTH_VCS, "peft"], ["peft"], id = "attached-protected"), + pytest.param(["-e" + UNSLOTH_VCS, "snac"], ["snac"], id = "attached-protected"), ], ) def test_editable_forms(shim, args, expected): @@ -130,15 +130,15 @@ def test_editable_forms(shim, args, expected): # -------------------------------------------------------------------------- # Item 3541142906 -- filter uv -P/--upgrade-package values. `uv pip install -# -P torch peft` must not let uv refresh baked torch; a pinned transformers +# -P torch snac` must not let uv refresh baked torch; a pinned transformers # upgrade selector still feeds the sidecar marker. # -------------------------------------------------------------------------- @pytest.mark.parametrize( "args, expected, expected_marker", [ - pytest.param(["-P", "torch", "peft"], ["peft"], None, id = "protected-dropped"), - pytest.param(["--upgrade-package=transformers", "peft"], ["peft"], None, id = "inline"), - pytest.param(["-P", "transformers==4.55.0", "peft"], ["peft"], "4.55.0", id = "tf-pin"), + pytest.param(["-P", "torch", "snac"], ["snac"], None, id = "protected-dropped"), + pytest.param(["--upgrade-package=transformers", "snac"], ["snac"], None, id = "inline"), + pytest.param(["-P", "transformers==4.55.0", "snac"], ["snac"], "4.55.0", id = "tf-pin"), pytest.param(["-P", "requests", "requests"], KEPT, None, id = "unprotected-kept"), # -P is not itself a target pytest.param(["-P", "torch"], None, None, id = "only-protected-noop"), @@ -300,16 +300,16 @@ def test_attached_short_requirement_file_filtered(shim, tmp_path): def test_attached_short_constraint_file_filtered(shim, tmp_path): constraints = tmp_path / "constraints.txt" constraints.write_text("torch==2.11.0\n", encoding = "utf-8") - execd, _ = _run(shim, "pip", ["-c" + str(constraints), "peft"]) + execd, _ = _run(shim, "pip", ["-c" + str(constraints), "snac"]) assert execd is not None and execd[0] == "-c", execd - assert "peft" in execd + assert "snac" in execd filtered = Path(execd[1]).read_text(encoding = "utf-8") assert "torch" not in filtered def test_attached_short_upgrade_package_protected_dropped(shim): - execd, _ = _run(shim, "uv", ["-Ptorch", "peft"]) - assert execd == ["peft"], execd + execd, _ = _run(shim, "uv", ["-Ptorch", "snac"]) + assert execd == ["snac"], execd assert "torch" not in execd and "-P" not in execd @@ -337,13 +337,13 @@ def test_bare_wheel_filename_forms(shim, args, expected): # -------------------------------------------------------------------------- def test_vcs_url_without_egg_protected_dropped(shim): # git+https://github.com/huggingface/transformers.git -> transformers. - execd, _ = _run(shim, "pip", ["git+https://github.com/huggingface/transformers.git", "peft"]) - assert execd == ["peft"], execd + execd, _ = _run(shim, "pip", ["git+https://github.com/huggingface/transformers.git", "snac"]) + assert execd == ["snac"], execd def test_vcs_url_without_egg_with_ref_dropped(shim): - execd, _ = _run(shim, "pip", ["git+https://github.com/unslothai/unsloth-zoo.git@main", "peft"]) - assert execd == ["peft"], execd + execd, _ = _run(shim, "pip", ["git+https://github.com/unslothai/unsloth-zoo.git@main", "snac"]) + assert execd == ["snac"], execd def test_vcs_url_without_egg_unprotected_kept(shim): @@ -364,10 +364,10 @@ R_URL = "https://example.com/reqs.txt" [ # dropped, and no dangling -r left behind pytest.param(["-r", R_URL], None, id = "sep-r-only-noop"), - pytest.param(["-r", R_URL, "peft"], ["peft"], id = "sep-r-target-kept"), - pytest.param(["--requirement=" + R_URL, "peft"], ["peft"], id = "inline-r"), - pytest.param(["-r" + R_URL, "peft"], ["peft"], id = "attached-r"), - pytest.param(["-c", "https://example.com/constraints.txt", "peft"], ["peft"], id = "sep-c"), + pytest.param(["-r", R_URL, "snac"], ["snac"], id = "sep-r-target-kept"), + pytest.param(["--requirement=" + R_URL, "snac"], ["snac"], id = "inline-r"), + pytest.param(["-r" + R_URL, "snac"], ["snac"], id = "attached-r"), + pytest.param(["-c", "https://example.com/constraints.txt", "snac"], ["snac"], id = "sep-c"), ], ) def test_remote_requirement_and_constraint_urls_refused(shim, args, expected): @@ -392,18 +392,18 @@ def test_nested_remote_include_dropped(shim, tmp_path): # stripped so they cannot rebuild already-satisfied baked deps. # -------------------------------------------------------------------------- def test_force_reinstall_flag_stripped(shim): - execd, _ = _run(shim, "pip", ["--force-reinstall", "peft"]) - assert execd == ["peft"], execd + execd, _ = _run(shim, "pip", ["--force-reinstall", "snac"]) + assert execd == ["snac"], execd def test_ignore_installed_short_flag_stripped(shim): - execd, _ = _run(shim, "pip", ["-I", "peft"]) - assert execd == ["peft"], execd + execd, _ = _run(shim, "pip", ["-I", "snac"]) + assert execd == ["snac"], execd def test_uv_reinstall_flag_stripped(shim): - execd, _ = _run(shim, "uv", ["--reinstall", "peft"]) - assert execd == ["peft"], execd + execd, _ = _run(shim, "uv", ["--reinstall", "snac"]) + assert execd == ["snac"], execd # -------------------------------------------------------------------------- @@ -413,11 +413,11 @@ def test_uv_reinstall_flag_stripped(shim): @pytest.mark.parametrize( "args, expected, expected_marker", [ - pytest.param(["--reinstall-package", "torch", "peft"], ["peft"], None, id = "sep-protected"), - pytest.param(["--reinstall-package=torch", "peft"], ["peft"], None, id = "inline-protected"), + pytest.param(["--reinstall-package", "torch", "snac"], ["snac"], None, id = "sep-protected"), + pytest.param(["--reinstall-package=torch", "snac"], ["snac"], None, id = "inline-protected"), pytest.param(["--reinstall-package", "requests", "requests"], KEPT, None, id = "unprotected"), pytest.param( - ["--reinstall-package", "transformers==4.55.0", "peft"], ["peft"], "4.55.0", id = "tf-pin" + ["--reinstall-package", "transformers==4.55.0", "snac"], ["snac"], "4.55.0", id = "tf-pin" ), ], ) @@ -436,9 +436,9 @@ SDIST_URL = "https://files.pythonhosted.org/packages/aa/unsloth-2026.7.1.tar.gz" @pytest.mark.parametrize( "args, expected", [ - pytest.param([SDIST_URL, "peft"], ["peft"], id = "url-protected"), + pytest.param([SDIST_URL, "snac"], ["snac"], id = "url-protected"), pytest.param(["torch-2.11.0.tar.gz"], None, id = "bare-protected"), - pytest.param(["./transformers-4.55.0.zip", "peft"], ["peft"], id = "zip-protected"), + pytest.param(["./transformers-4.55.0.zip", "snac"], ["snac"], id = "zip-protected"), # flashinfer-python is protected; the name must survive the hyphen split. pytest.param(["flashinfer-python-0.5.0.tar.gz"], None, id = "hyphenated-name"), pytest.param(["numpy-2.1.0.tar.gz"], KEPT, id = "unprotected-kept"), @@ -466,9 +466,9 @@ def test_uv_plural_requirements_filtered(shim, tmp_path): def test_uv_plural_constraints_filtered(shim, tmp_path): constraints = tmp_path / "constraints.txt" constraints.write_text("torch==2.11.0\n", encoding = "utf-8") - execd, _ = _run(shim, "uv", ["--constraints", str(constraints), "peft"]) + execd, _ = _run(shim, "uv", ["--constraints", str(constraints), "snac"]) assert execd is not None and execd[0] == "--constraints", execd - assert "peft" in execd + assert "snac" in execd filtered = Path(execd[1]).read_text(encoding = "utf-8") assert "torch" not in filtered @@ -480,12 +480,12 @@ def test_uv_plural_constraints_filtered(shim, tmp_path): @pytest.mark.parametrize( "args, expected", [ - pytest.param(["-U", "--upgrade-strategy", "eager", "peft"], ["-U", "peft"], id = "eager"), - pytest.param(["--upgrade-strategy=eager", "peft"], ["peft"], id = "inline-eager"), + pytest.param(["-U", "--upgrade-strategy", "eager", "snac"], ["-U", "snac"], id = "eager"), + pytest.param(["--upgrade-strategy=eager", "snac"], ["snac"], id = "inline-eager"), # only-if-needed is pip's default, so dropping it is a harmless no-op that # keeps the kept target installing normally. pytest.param( - ["--upgrade-strategy", "only-if-needed", "peft"], ["peft"], id = "only-if-needed" + ["--upgrade-strategy", "only-if-needed", "snac"], ["snac"], id = "only-if-needed" ), ], ) @@ -512,7 +512,7 @@ def _raw_execd(shim, tool, args): def test_forwarded_install_carries_protected_constraints(shim): - execd = _raw_execd(shim, "pip", ["peft"]) + execd = _raw_execd(shim, "pip", ["snac"]) assert execd is not None and execd[-2] == "--constraint", execd pins = Path(execd[-1]).read_text(encoding = "utf-8").strip().splitlines() assert pins, "constraints file must pin the installed protected packages" @@ -600,8 +600,8 @@ def test_filter_write_failure_clean_file_passes_through(shim, tmp_path, monkeypa # resolver-wide destructive switches. # -------------------------------------------------------------------------- def test_uv_exact_flag_stripped(shim): - execd, _ = _run(shim, "uv", ["--exact", "peft"]) - assert execd == ["peft"], execd + execd, _ = _run(shim, "uv", ["--exact", "snac"]) + assert execd == ["snac"], execd # -------------------------------------------------------------------------- @@ -620,14 +620,14 @@ def _make_local_project(tmp_path, dirname, project_name): def test_local_dir_protected_by_metadata_dropped(shim, tmp_path): # Directory name is innocuous; pyproject names a protected package. path = _make_local_project(tmp_path, "my-checkout", "transformers") - execd, _ = _run(shim, "pip", [path, "peft"]) - assert execd == ["peft"], execd + execd, _ = _run(shim, "pip", [path, "snac"]) + assert execd == ["snac"], execd def test_local_dir_protected_editable_dropped(shim, tmp_path): path = _make_local_project(tmp_path, "unsloth", "unsloth") - execd, _ = _run(shim, "pip", ["-e", path, "peft"]) - assert execd == ["peft"], execd + execd, _ = _run(shim, "pip", ["-e", path, "snac"]) + assert execd == ["snac"], execd assert "-e" not in execd @@ -636,8 +636,8 @@ def test_local_dir_basename_fallback_setup_py(shim, tmp_path): proj = tmp_path / "torch" proj.mkdir() (proj / "setup.py").write_text("from setuptools import setup\nsetup()\n") - execd, _ = _run(shim, "pip", [str(proj), "peft"]) - assert execd == ["peft"], execd + execd, _ = _run(shim, "pip", [str(proj), "snac"]) + assert execd == ["snac"], execd def test_local_dir_unprotected_kept(shim, tmp_path): @@ -656,8 +656,8 @@ def test_local_dir_without_metadata_passes_through(shim, tmp_path): # -------------------------------------------------------------------------- # Item 3592835033 -- every uv/pip value-taking flag must be in _VALUE_FLAGS. # `--torch-backend cu128 torch` used to drop torch but keep the separated flag -# pair, exec'ing uv with no target; `--extra torch peft` misread the extra NAME -# "torch" as a target, leaving a dangling `--extra` that swallowed peft. +# pair, exec'ing uv with no target; `--extra torch snac` misread the extra NAME +# "torch" as a target, leaving a dangling `--extra` that swallowed snac. @pytest.mark.parametrize( @@ -689,15 +689,15 @@ def test_value_flag_protected_only_noops(shim, tool, flag, value): ], ) def test_value_flag_pair_forwarded_with_kept_target(shim, tool, flag, value): - execd, _ = _run(shim, tool, [flag, value, "torch", "peft"]) - assert execd == [flag, value, "peft"], execd + execd, _ = _run(shim, tool, [flag, value, "torch", "snac"]) + assert execd == [flag, value, "snac"], execd def test_extra_value_is_not_a_protected_target(shim): # `--extra torch` names an EXTRA, not the torch package: the pair stays and - # peft is not swallowed by a dangling --extra. - execd, _ = _run(shim, "uv", ["--extra", "torch", "peft"]) - assert execd == ["--extra", "torch", "peft"], execd + # snac is not swallowed by a dangling --extra. + execd, _ = _run(shim, "uv", ["--extra", "torch", "snac"]) + assert execd == ["--extra", "torch", "snac"], execd def _value_flags_from_help(cmd): @@ -762,8 +762,8 @@ def test_uv_help_value_flags_all_classified(shim): ], ) def test_vcs_slash_ref_still_protected(shim, url): - execd, _ = _run(shim, "pip", [url, "peft"]) - assert execd == ["peft"], execd + execd, _ = _run(shim, "pip", [url, "snac"]) + assert execd == ["snac"], execd def test_vcs_slash_ref_unprotected_kept(shim):