mirror of
https://github.com/unslothai/unsloth.git
synced 2026-11-01 03:30:08 +00:00
The earlier comment-trim PRs skipped every module (top-of-file)
docstring outside tests. This condenses 556 of them across
`studio/backend`, `unsloth`, `unsloth_cli`, `scripts` and the top-level
`studio/*.py` helpers, from 5,673 lines to 959. Nothing else changes.
| | before | after |
|---|---|---|
| module docstring lines in the 556 files | 5,673 | 959 |
Each docstring keeps what the module is for plus the rules other code
relies on: fail-open or best-effort behaviour, import-time safety
("stdlib only, safe to import early"), compatibility promises, route
lists, security reasons, ordering rules. For example
`studio/backend/routes/llama.py` now reads `llama.cpp prebuilt update
routes: GET /api/llama/update-status, GET /api/llama/update-changelog,
POST /api/llama/update; fail open; old top-level fields keep shape so
older clients work; whisper and audio.cpp chain as phases.`
## Help text and anything that reads a docstring is left alone
A module docstring was only touched when an AST pass over every Python
file in the repo (nothing imported) found no way for it to be read:
- the module itself never names `__doc__` (`description=__doc__`,
`print(__doc__)`, `globals()`, `sys.modules[__name__]`);
- no file reads `<module>.__doc__` through its imports, and no file that
imports the module reads `.__doc__` on a base that cannot be resolved;
- no file passes the module (or an unresolved name, in a file importing
it) to `inspect.getdoc`, `inspect.getcomments`, `pydoc` or `help()`, or
the whole module to `inspect.getsource`;
- no file reads `__doc__` through `getattr`/`vars` while naming the
module;
- no 20+ character fragment of the docstring appears verbatim in another
file that names or imports the module (a source-reading test or tool
anchoring on it).
80 modules failed one of these and are unchanged, including every
`scripts/` file whose argparse help is `description=__doc__`,
`studio/install_python_stack.py`, and modules that tests read whole with
`inspect.getsource` (`core/inference/diffusion.py`, `llama_cpp.py`,
`video.py`, ...).
## How it was checked
- **Only module docstrings changed:** for every changed file the module
AST, with the docstring text blanked, is identical to `main` (every
other statement, docstring and string literal is byte-identical), and
the file is on the safe list. `comment_tools.py check --strict-ext
--allow-docstring-edits` passes on all 556 files.
- **Help output, byte for byte against `main`:** `unsloth --help` and
all 40 subcommand help pages, plus `python <file> --help` for all 65
scripts with an argparse/click/typer parser and a `__main__` block. Two
scripts cannot print help in this environment on either side
(`kv_cache_multiturn_benchmark.py` needs a GPU to import,
`kernel_install.py` must run as a module); `python -m
utils.kernel_install --help` is identical on both, and the benchmark's
parser has no description.
- **Tests, `main` vs this branch on the same commit:** every backend
test file that reads source text, docstrings or line numbers (905 files:
`read_text`, `open(`, `getsource`, `linecache`, `lineno`, `__doc__`,
`get_docstring`, `ast.parse`, `tokenize`), the full root and
`unsloth_cli` suites, and every frontend test that reads a `.py` file.
Failing IDs match `main`; the few one-off differences pass 3/3 alone on
both sides.
- **Wording:** each rewrite was reviewed against its original for a lost
rule, a reversed condition or an invented claim, and each proposed
correction was independently checked against the original before it was
applied.
107 lines
4.1 KiB
Python
107 lines
4.1 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
|
|
|
|
"""Syncs byte-identical helpers from install.ps1 into studio/setup.ps1; --check exits 1 on drift."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import re
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
INSTALL_PS1 = ROOT / "install.ps1"
|
|
SETUP_PS1 = ROOT / "studio" / "setup.ps1"
|
|
|
|
SHARED_BEGIN = "# ── BEGIN SHARED WITH studio/setup.ps1 ──"
|
|
SHARED_END = "# ── END SHARED WITH studio/setup.ps1 ──"
|
|
|
|
|
|
def function_span(text: str, name: str) -> tuple[int, int]:
|
|
"""Half-open character span of a PowerShell function, by balanced braces."""
|
|
match = re.search(rf"(?im)^[ \t]*function[ \t]+{re.escape(name)}\b", text)
|
|
if not match:
|
|
raise SystemExit(f"{name} is not defined")
|
|
start = text.index("{", match.start())
|
|
depth = 0
|
|
for index in range(start, len(text)):
|
|
if text[index] == "{":
|
|
depth += 1
|
|
elif text[index] == "}":
|
|
depth -= 1
|
|
if depth == 0:
|
|
return match.start(), index + 1
|
|
raise SystemExit(f"unbalanced braces in {name}")
|
|
|
|
|
|
def dedented(source: str) -> str:
|
|
"""Drop the common indentation, which is the only difference allowed."""
|
|
lines = source.splitlines()
|
|
body = [line for line in lines if line.strip()]
|
|
indent = min((len(line) - len(line.lstrip(" ")) for line in body), default = 0)
|
|
return "\n".join(line[indent:] if line.strip() else "" for line in lines)
|
|
|
|
|
|
def shared_names(install_text: str) -> list[str]:
|
|
"""The functions declared inside the shared block, in file order."""
|
|
block = install_text.split(SHARED_BEGIN, 1)[1].split(SHARED_END, 1)[0]
|
|
return re.findall(r"(?m)^[ \t]*function[ \t]+([A-Za-z-]+)", block)
|
|
|
|
|
|
def leading_comment_start(text: str, start: int) -> int:
|
|
"""Index of the contiguous comment block directly above a function."""
|
|
lines = text[:start].splitlines(keepends = True)
|
|
cut = len(lines)
|
|
while cut > 0 and lines[cut - 1].lstrip().startswith("#"):
|
|
cut -= 1
|
|
return sum(len(line) for line in lines[:cut])
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("--check", action = "store_true", help = "report drift, write nothing")
|
|
args = parser.parse_args()
|
|
|
|
install_text = INSTALL_PS1.read_text(encoding = "utf-8")
|
|
setup_text = SETUP_PS1.read_text(encoding = "utf-8")
|
|
|
|
stale: list[str] = []
|
|
previous: str | None = None
|
|
for name in shared_names(install_text):
|
|
start, end = function_span(install_text, name)
|
|
wanted = dedented(install_text[start:end])
|
|
if re.search(rf"(?im)^[ \t]*function[ \t]+{re.escape(name)}\b", setup_text):
|
|
setup_start, setup_end = function_span(setup_text, name)
|
|
if setup_text[setup_start:setup_end] == wanted:
|
|
previous = name
|
|
continue
|
|
stale.append(name)
|
|
setup_text = setup_text[:setup_start] + wanted + setup_text[setup_end:]
|
|
previous = name
|
|
continue
|
|
# New helper: insert it after the one before it in install.ps1, with its comment block.
|
|
if previous is None:
|
|
raise SystemExit(f"{name} is new and has nothing to follow in setup.ps1")
|
|
# dedented() drops the trailing newline, which would glue the comment to the function keyword.
|
|
comment = dedented(install_text[leading_comment_start(install_text, start) : start])
|
|
comment = comment.rstrip("\n") + "\n" if comment else ""
|
|
_, after = function_span(setup_text, previous)
|
|
setup_text = setup_text[:after] + "\n\n" + comment + wanted + setup_text[after:]
|
|
stale.append(name)
|
|
previous = name
|
|
|
|
if not stale:
|
|
print("shared helpers already match")
|
|
return 0
|
|
if args.check:
|
|
print("setup.ps1 is behind install.ps1: " + ", ".join(stale))
|
|
print("run: python3 scripts/sync_shared_ps1_helpers.py")
|
|
return 1
|
|
SETUP_PS1.write_text(setup_text, encoding = "utf-8")
|
|
print("synced into studio/setup.ps1: " + ", ".join(stale))
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|