mirror of
https://github.com/unslothai/unsloth.git
synced 2026-08-25 16:52:26 +00:00
Studio: apply base.txt on the install.sh and install.ps1 paths (#8195)
* Studio: apply base.txt on the install.sh and install.ps1 paths
install.sh and install.ps1 install unsloth and unsloth-zoo inline, then
export SKIP_STUDIO_BASE=1 so setup.sh / setup.ps1 do not install the same
two packages a second time. install_python_stack.py read that flag as
"skip base.txt" and short-circuited the whole step:
if skip_base:
pass
That was the same thing only for as long as base.txt held nothing but
those two names. Add a third, pinned entry to base.txt and it reaches no
fresh install on any platform: neither installer reads the file, and the
one branch that does was skipped. It would only land later, if the user
happened to run `unsloth studio update`.
Every install.sh and install.ps1 path was affected, on every platform:
CUDA, ROCm, XPU, CPU, macOS, local and non-local, fresh and migrated.
Keep skipping the two core packages, which is all the flag was ever
meant to avoid repeating, and apply whatever else base.txt asks for.
When base.txt holds only the core packages, as it does today, there is
nothing left to install and no extra subprocess runs. No-torch mode is
untouched: it has its own list in no-torch-runtime.txt, which the
installers do apply inline.
The core-package filter parses the project name rather than matching on
a prefix, so a future unsloth-<something> pin is not swallowed too.
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* Reconcile base requirements with current main
* Preserve relative requirements includes across filters
* Fix filtered requirements test cleanup
* Separate core and shared base requirements
* Preserve shared base requirement resolution
* Keep the filtered-requirements and uv alias paths from aborting an install
The adjacent temp copy raised PermissionError on a read-only requirements dir, and a symlink failure handed uv back the spaced path it cannot read. Fall back to the temp dir and to a copy respectively, and stop the real-extras tests leaving filtered files in the tree.
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* Count the MLX slot and survive an unusable base.txt
Simulating every install path showed two gaps. base_total never counted the Apple Silicon MLX step, so `studio update` there ran 13 steps out of a declared 12 and recorded the wrong steps_total. And the new base.txt read happens before the manifest is dropped, so a missing or unreadable file aborted with a traceback where the old code reached pip; a BOM also read as content and scheduled an empty step. Progress coverage now spans both core paths on all four platforms.
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* Make the unreadable base.txt case independent of the mode bits
chmod(0o000) denies nothing as root, which containerized test jobs run as, and Windows does not implement POSIX modes at all, so the case asserted None against a file it could still read. Raise from a patched read instead.
* Tighten the comments this PR adds
---------
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: oobabooga <112222186+oobabooga@users.noreply.github.com>
This commit is contained in:
parent
0e488c211b
commit
3a58fa5c41
11 changed files with 410 additions and 88 deletions
|
|
@ -4,13 +4,11 @@ from __future__ import annotations
|
|||
|
||||
import ast
|
||||
import contextlib
|
||||
import glob
|
||||
import importlib
|
||||
import io
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
from unittest import mock
|
||||
|
||||
|
|
@ -86,16 +84,18 @@ class TestUvSafePath:
|
|||
assert ips._uv_safe_path(p) == p
|
||||
|
||||
@pytest.mark.skipif(ips.IS_WINDOWS, reason = "POSIX temp-copy fallback")
|
||||
def test_posix_space_path_returns_spacefree_copy(self, tmp_path):
|
||||
def test_posix_space_path_preserves_relative_requirements(self, tmp_path):
|
||||
src = tmp_path / "Open Source" / "constraints.txt"
|
||||
src.parent.mkdir(parents = True)
|
||||
src.write_text("torch>=2.6\n")
|
||||
src.write_text("-r child.txt\n")
|
||||
(src.parent / "child.txt").write_text("torch>=2.6\n")
|
||||
|
||||
out = ips._uv_safe_path(str(src))
|
||||
|
||||
assert " " not in out, f"uv-safe path still has a space: {out!r}"
|
||||
assert out != str(src)
|
||||
assert Path(out).read_text() == "torch>=2.6\n"
|
||||
assert Path(out).read_text() == "-r child.txt\n"
|
||||
assert (Path(out).parent / "child.txt").read_text() == "torch>=2.6\n"
|
||||
|
||||
@pytest.mark.skipif(ips.IS_WINDOWS, reason = "POSIX temp-copy fallback")
|
||||
def test_posix_missing_file_falls_back_to_original(self):
|
||||
|
|
@ -121,24 +121,23 @@ class TestUvSafePathHardening:
|
|||
assert uvps.uv_safe_path(str(src)) == str(src)
|
||||
|
||||
@pytest.mark.skipif(ips.IS_WINDOWS, reason = "POSIX temp-copy fallback")
|
||||
def test_no_temp_dir_leak_on_copy_failure(self, tmp_path, monkeypatch):
|
||||
"""A copyfile failure after mkdtemp must not orphan the temp dir."""
|
||||
def test_alias_failure_falls_back_to_a_copy(self, tmp_path, monkeypatch):
|
||||
"""A symlink failure must still hand uv a space-free path, and not orphan the dir."""
|
||||
from backend.utils import uv_path_safety as uvps
|
||||
|
||||
src = tmp_path / "Open Source" / "constraints.txt"
|
||||
src.parent.mkdir(parents = True)
|
||||
src.write_text("idna\n")
|
||||
pattern = os.path.join(tempfile.gettempdir(), "unsloth_uv_*")
|
||||
before = set(glob.glob(pattern))
|
||||
|
||||
def boom(*a, **k):
|
||||
raise OSError("boom")
|
||||
|
||||
monkeypatch.setattr(uvps.shutil, "copyfile", boom)
|
||||
monkeypatch.setattr(uvps.os, "symlink", boom)
|
||||
out = uvps.uv_safe_path(str(src))
|
||||
|
||||
assert out == str(src)
|
||||
assert set(glob.glob(pattern)) == before
|
||||
assert " " not in out
|
||||
assert Path(out).read_text() == "idna\n"
|
||||
assert str(Path(out).parent) in uvps._UV_SAFE_PATH_TMPDIRS
|
||||
|
||||
@pytest.mark.skipif(ips.IS_WINDOWS, reason = "POSIX temp-copy fallback")
|
||||
def test_cleanup_removes_and_clears_registry(self, tmp_path):
|
||||
|
|
@ -149,7 +148,7 @@ class TestUvSafePathHardening:
|
|||
src.parent.mkdir(parents = True)
|
||||
src.write_text("idna\n")
|
||||
out = uvps.uv_safe_path(str(src))
|
||||
tmp_dir = Path(out).parent
|
||||
tmp_dir = Path(out).parents[1]
|
||||
assert tmp_dir.is_dir() and str(tmp_dir) in uvps._UV_SAFE_PATH_TMPDIRS
|
||||
|
||||
uvps._cleanup_uv_safe_path_tmpdirs()
|
||||
|
|
@ -516,9 +515,9 @@ class TestProgressLineNotes:
|
|||
class TestBuildPipCmdUpgradeIntent:
|
||||
"""pip has no --upgrade-package, so uv's flag must be translated, not dropped.
|
||||
|
||||
Dropping it made the fallback a no-op on the update path: `studio update`
|
||||
passes --upgrade-package unsloth with a base.txt listing a bare unsloth, so
|
||||
pip found it satisfied, installed nothing, and the update reported success.
|
||||
Dropping it made the fallback a no-op on the update path: pip saw the named
|
||||
distributions as already satisfied, installed nothing, and the update
|
||||
reported success.
|
||||
"""
|
||||
|
||||
def test_update_path_keeps_the_upgrade_intent(self):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue