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
155 lines
5.3 KiB
Python
155 lines
5.3 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 `--verbose/-v` Studio flag: option registration on both the
|
|
plain callback and the `run` subcommand, re-exec forwarding, the access-log
|
|
env override, and rejection before a subcommand. Modeled on
|
|
test_studio_secure_flag.py."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
from typer.testing import CliRunner
|
|
|
|
_REPO_ROOT = Path(__file__).resolve().parents[2]
|
|
if str(_REPO_ROOT) not in sys.path:
|
|
sys.path.insert(0, str(_REPO_ROOT))
|
|
|
|
_DEDUP = "UNSLOTH_STUDIO_ACCESS_LOG_DEDUP_MS"
|
|
_POLL = "UNSLOTH_STUDIO_ACCESS_LOG_POLL_DEDUP_MS"
|
|
_BASE = ["--model", "unsloth/Qwen3-1.7B-GGUF"]
|
|
|
|
|
|
def _studio():
|
|
from unsloth_cli.commands import studio as _studio_mod
|
|
return _studio_mod
|
|
|
|
|
|
# ── option registration ──────────────────────────────────────────────
|
|
|
|
|
|
def test_run_exposes_verbose_option_default_off():
|
|
import inspect
|
|
|
|
opt = inspect.signature(_studio().run).parameters["verbose"].default
|
|
decls = set(getattr(opt, "param_decls", []) or [])
|
|
assert "--verbose" in decls and "-v" in decls
|
|
assert getattr(opt, "default", None) is False
|
|
|
|
|
|
def test_studio_default_exposes_verbose_option_default_off():
|
|
import inspect
|
|
|
|
opt = inspect.signature(_studio().studio_default).parameters["verbose"].default
|
|
decls = set(getattr(opt, "param_decls", []) or [])
|
|
assert "--verbose" in decls and "-v" in decls
|
|
assert getattr(opt, "default", None) is False
|
|
|
|
|
|
# ── re-exec capture plumbing (mirrors test_studio_secure_flag.py) ─────
|
|
|
|
|
|
class _ExecCaptured(SystemExit):
|
|
def __init__(self, argv):
|
|
super().__init__(0)
|
|
self.argv = list(argv)
|
|
|
|
|
|
def _invoke_run(monkeypatch, args):
|
|
import typer as _typer
|
|
|
|
studio_mod = _studio()
|
|
captured = []
|
|
monkeypatch.setattr(sys, "prefix", "/nonexistent/outer/venv")
|
|
fake_venv = Path("/fake/studio/venv/unsloth_studio")
|
|
monkeypatch.setattr(studio_mod, "_studio_venv_python", lambda: fake_venv / "bin" / "python")
|
|
fake_bin = fake_venv / "bin" / "unsloth"
|
|
real_is_file = Path.is_file
|
|
monkeypatch.setattr(
|
|
Path,
|
|
"is_file",
|
|
lambda self: True if str(self) == str(fake_bin) else real_is_file(self),
|
|
)
|
|
from unsloth_cli import _tool_policy as _tp_mod
|
|
|
|
monkeypatch.setattr(
|
|
_tp_mod,
|
|
"resolve_tool_policy",
|
|
lambda host, flag, yes, silent: False if flag is None else bool(flag),
|
|
)
|
|
monkeypatch.setattr(sys, "platform", "linux")
|
|
|
|
def fake_execvp(file, argv):
|
|
captured.append(list(argv))
|
|
raise _ExecCaptured(argv)
|
|
|
|
monkeypatch.setattr(studio_mod.os, "execvp", fake_execvp)
|
|
app = _typer.Typer()
|
|
app.command(
|
|
context_settings = {"allow_extra_args": True, "ignore_unknown_options": True},
|
|
)(studio_mod.run)
|
|
CliRunner().invoke(app, args, catch_exceptions = True)
|
|
return captured
|
|
|
|
|
|
# ── re-exec forwarding + env override ─────────────────────────────────
|
|
|
|
|
|
def test_run_verbose_sets_env_and_forwards_on_reexec(monkeypatch):
|
|
monkeypatch.delenv(_DEDUP, raising = False)
|
|
monkeypatch.delenv(_POLL, raising = False)
|
|
captured = _invoke_run(monkeypatch, _BASE + ["--verbose"])
|
|
assert len(captured) == 1, captured
|
|
assert "--verbose" in captured[0], captured[0]
|
|
import os as _os
|
|
|
|
assert _os.environ.get(_DEDUP) == "0"
|
|
assert _os.environ.get(_POLL) == "0"
|
|
|
|
|
|
def test_run_without_verbose_leaves_env_unset(monkeypatch):
|
|
monkeypatch.delenv(_DEDUP, raising = False)
|
|
monkeypatch.delenv(_POLL, raising = False)
|
|
captured = _invoke_run(monkeypatch, _BASE)
|
|
assert len(captured) == 1, captured
|
|
assert "--verbose" not in captured[0], captured[0]
|
|
assert "--log-verbose" not in captured[0], captured[0]
|
|
import os as _os
|
|
|
|
assert _os.environ.get(_DEDUP) is None
|
|
assert _os.environ.get(_POLL) is None
|
|
|
|
|
|
def test_run_verbose_preserves_llama_server_verbosity(monkeypatch):
|
|
# Studio consumes --verbose but still forwards llama-server's own verbosity.
|
|
monkeypatch.delenv(_DEDUP, raising = False)
|
|
monkeypatch.delenv(_POLL, raising = False)
|
|
captured = _invoke_run(monkeypatch, _BASE + ["--verbose"])
|
|
assert len(captured) == 1, captured
|
|
assert "--log-verbose" in captured[0], captured[0]
|
|
|
|
|
|
def test_run_verbose_does_not_duplicate_existing_llama_verbose(monkeypatch):
|
|
monkeypatch.delenv(_DEDUP, raising = False)
|
|
monkeypatch.delenv(_POLL, raising = False)
|
|
captured = _invoke_run(monkeypatch, _BASE + ["--verbose", "--log-verbose"])
|
|
assert len(captured) == 1, captured
|
|
assert captured[0].count("--log-verbose") == 1, captured[0]
|
|
|
|
|
|
# ── --verbose before a subcommand is rejected ─────────────────────────
|
|
|
|
|
|
def test_studio_default_rejects_verbose_with_subcommand():
|
|
import typer as _typer
|
|
|
|
studio_mod = _studio()
|
|
app = _typer.Typer()
|
|
app.add_typer(studio_mod.studio_app, name = "studio")
|
|
result = CliRunner().invoke(app, ["studio", "--verbose", "run", "--model", "X"])
|
|
assert result.exit_code == 2, result.output
|
|
combined = (result.output or "") + (getattr(result, "stderr", "") or "")
|
|
assert "--verbose" in combined, combined
|