Files
unslothai--unsloth/tests/test_enforce_kwargs_spacing.py
T
wehub-resource-sync 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
chore: import upstream snapshot with attribution
2026-07-13 12:59:56 +08:00

414 lines
14 KiB
Python

"""Tests for scripts/enforce_kwargs_spacing.py rewrite rules (AST-preserving, idempotent)."""
from __future__ import annotations
import ast
import sys
from pathlib import Path
import pytest
_SCRIPTS = str(Path(__file__).resolve().parent.parent / "scripts")
if _SCRIPTS not in sys.path:
sys.path.insert(0, _SCRIPTS)
from enforce_kwargs_spacing import ( # noqa: E402
collapse_short_asserts,
enforce_spacing,
merge_adjacent_string_literals,
normalize_def_trailing_comma,
remove_blank_after_short_import,
)
# (name, source) pairs where the blank after the import block MUST be removed.
_MUST_CHANGE = {
"try_except_import": (
"def f():\n"
" try:\n"
" import torch\n"
"\n"
" return torch.inference_mode\n"
" except Exception:\n"
" from contextlib import nullcontext\n"
"\n"
" return nullcontext\n"
),
"if_from_import": (
"def g():\n"
" if cond:\n"
" from . import locators\n"
"\n"
" regions = locators.regions()\n"
),
"multiple_consecutive_imports": ("def f():\n import a\n import b\n\n return a, b\n"),
"type_checking_block": (
"def f():\n"
" if TYPE_CHECKING:\n"
" import x\n"
"\n"
" y = x\n"
" return y\n"
),
"with_block": ("def f():\n with ctx():\n import a\n\n return a.run()\n"),
}
# Sources that MUST be left byte-for-byte unchanged.
_MUST_NOT_CHANGE = {
"module_level": 'import os\n\nVALUE = os.environ.get("V")\n',
"large_suite": (
"def f():\n"
" import a\n"
"\n"
" x = a.load()\n"
" y = transform(x)\n"
" return y\n"
),
"comment_between": ("def f():\n import a\n\n # keep separated\n return a.value\n"),
"import_is_last_stmt": "def f():\n if cond:\n import a\n\n",
"no_blank_already": "def f():\n import a\n return a\n",
}
@pytest.mark.parametrize("name", sorted(_MUST_CHANGE))
def test_blank_removed_for_small_import_block(name):
src = _MUST_CHANGE[name]
out, changed = remove_blank_after_short_import(src)
assert changed is True
assert out != src
# Import and following statement now adjacent.
assert "\n\n" not in out or out.count("\n\n") < src.count("\n\n")
assert ast.dump(ast.parse(out)) == ast.dump(ast.parse(src))
out2, changed2 = remove_blank_after_short_import(out)
assert out2 == out and changed2 is False
@pytest.mark.parametrize("name", sorted(_MUST_NOT_CHANGE))
def test_blank_preserved_when_not_applicable(name):
src = _MUST_NOT_CHANGE[name]
out, changed = remove_blank_after_short_import(src)
assert changed is False
assert out == src
def test_exact_output_try_block():
src = (
"def f():\n"
" try:\n"
" import torch\n"
"\n"
" return torch.inference_mode\n"
" except Exception:\n"
" from contextlib import nullcontext\n"
"\n"
" return nullcontext\n"
)
expected = (
"def f():\n"
" try:\n"
" import torch\n"
" return torch.inference_mode\n"
" except Exception:\n"
" from contextlib import nullcontext\n"
" return nullcontext\n"
)
out, changed = remove_blank_after_short_import(src)
assert changed is True
assert out == expected
def test_exact_output_multiple_consecutive_imports():
# Only the blank after the LAST import in a run is dropped; both imports kept.
src = "def f():\n import a\n import b\n\n return a, b\n"
expected = "def f():\n import a\n import b\n return a, b\n"
out, changed = remove_blank_after_short_import(src)
assert changed is True
assert out == expected
def test_multiple_blank_lines_in_gap_all_removed():
src = "def f():\n import a\n\n\n return a\n"
expected = "def f():\n import a\n return a\n"
out, changed = remove_blank_after_short_import(src)
assert changed is True
assert out == expected
out2, changed2 = remove_blank_after_short_import(out)
assert out2 == out and changed2 is False
def test_multiline_import_internal_blank_preserved():
# A blank inside a parenthesized import is part of the import, not the gap.
src = (
"def g():\n"
" from mod import (\n"
" a,\n"
"\n"
" b,\n"
" )\n"
"\n"
" return a, b\n"
)
expected = (
"def g():\n"
" from mod import (\n"
" a,\n"
"\n"
" b,\n"
" )\n"
" return a, b\n"
)
out, changed = remove_blank_after_short_import(src)
assert changed is True
assert out == expected
assert ast.dump(ast.parse(out)) == ast.dump(ast.parse(src))
out2, changed2 = remove_blank_after_short_import(out)
assert out2 == out and changed2 is False
def test_syntax_error_is_left_alone():
src = "def f(:\n import a\n\n return a\n"
out, changed = remove_blank_after_short_import(src)
assert changed is False
assert out == src
def test_enforce_spacing_pads_kwargs():
src = "f(a=1, b = 2)\n"
out, changed = enforce_spacing(src)
assert changed is True
assert "a = 1" in out and "b = 2" in out
def test_enforce_spacing_noop_when_already_spaced():
src = "f(a = 1, b = 2)\n"
out, changed = enforce_spacing(src)
assert changed is False
assert out == src
# ── Rule D: def one-per-line iff >= 3 params AND a default ──────────────────
# add comma -> force one-per-line; strip comma -> stay collapsible.
# Comma must be ADDED: >= 3 params, has a default, no trailing comma yet.
_DEF_ADD = {
"three_with_default": "def f(a, b, c=1):\n return a\n",
"four_with_default": "def f(a, b, c, d=1):\n return a\n",
"kwonly_default": "def f(a, b, *, c=1):\n return a\n", # 3 real params, kw default
"continuation_default": "def f(\n a, b, c=1\n):\n return a\n",
"starred_with_default": "def f(a, b, *args, c=1):\n return a\n", # 4 params
}
# Comma must be STRIPPED: NOT (>=3 params and default), but a trailing comma exists.
_DEF_STRIP = {
"three_no_default_multiline": "def f(\n a,\n b,\n c,\n):\n return a\n",
"four_no_default_multiline": "def f(\n a,\n b,\n c,\n d,\n):\n return a\n",
"two_with_default": "def f(\n a,\n b=1,\n):\n return a\n", # < 3 params -> one line
"single_arg": "def f(\n a,\n):\n return a\n",
}
# Left byte-for-byte unchanged.
_DEF_NOCHANGE = {
"three_no_default_oneline": "def f(a, b, c):\n return a\n",
"two_with_default_oneline": "def f(a, b=1):\n return a\n", # < 3 -> one line, no comma
"noparams": "def f():\n return 1\n",
"call_site": "x = foo(\n a,\n b,\n c,\n d,\n)\n",
"nested_default_call": "def f(a=g(1, 2,)):\n return a\n", # 1 param, no def comma
"three_default_already_comma": "def f(\n a,\n b,\n c=1,\n):\n return a\n",
}
@pytest.mark.parametrize("name", sorted(_DEF_ADD))
def test_def_comma_added(name):
src = _DEF_ADD[name]
out, changed = normalize_def_trailing_comma(src)
assert changed is True
assert ast.dump(ast.parse(out)) == ast.dump(ast.parse(src))
assert out.count(",") == src.count(",") + 1
out2, changed2 = normalize_def_trailing_comma(out)
assert out2 == out and changed2 is False
@pytest.mark.parametrize("name", sorted(_DEF_STRIP))
def test_def_comma_stripped(name):
src = _DEF_STRIP[name]
out, changed = normalize_def_trailing_comma(src)
assert changed is True
assert ast.dump(ast.parse(out)) == ast.dump(ast.parse(src))
assert out.count(",") == src.count(",") - 1
out2, changed2 = normalize_def_trailing_comma(out)
assert out2 == out and changed2 is False
@pytest.mark.parametrize("name", sorted(_DEF_NOCHANGE))
def test_def_comma_unchanged(name):
src = _DEF_NOCHANGE[name]
out, changed = normalize_def_trailing_comma(src)
assert changed is False
assert out == src
def test_def_comma_exact_output_strip_and_add():
# >= 3 params + default -> add comma (force one-per-line)
assert normalize_def_trailing_comma("def f(a, b, c=1):\n return a\n")[0] == (
"def f(a, b, c=1,):\n return a\n"
)
# 3 params, no default -> strip comma (collapsible)
assert (
normalize_def_trailing_comma("def f(\n a,\n b,\n c,\n):\n return a\n")[0]
== "def f(\n a,\n b,\n c\n):\n return a\n"
)
# ── Rule C: merge adjacent same-line string literals ───────────────────────
@pytest.mark.parametrize(
"src,expected",
[
('x = "ab" "cd"\n', 'x = "abcd"\n'),
('d = "newly-" "added dep."\n', 'd = "newly-added dep."\n'),
('m = ("a. " "b.")\n', 'm = ("a. b.")\n'),
('x = r"a\\n" r"b"\n', 'x = r"a\\nb"\n'),
('x = "a\\"q" "b"\n', 'x = "a\\"qb"\n'),
# f + plain folds into one f-string (plain braces escaped).
('x = f"a" "b"\n', 'x = f"ab"\n'),
(
'd = (f"{pkg}@{ver} is on the " "BLOCKED list")\n',
'd = (f"{pkg}@{ver} is on the BLOCKED list")\n',
),
('x = f"a{z}" "{lit}"\n', 'x = f"a{z}{{lit}}"\n'),
('m = "plain " f"then {y}"\n', 'm = f"plain then {y}"\n'), # plain + f
],
)
def test_merge_adjacent_strings(src, expected):
out, changed = merge_adjacent_string_literals(src)
assert changed is True
assert out == expected
assert ast.dump(ast.parse(out)) == ast.dump(ast.parse(src))
out2, changed2 = merge_adjacent_string_literals(out)
assert out2 == out and changed2 is False
@pytest.mark.parametrize(
"src",
[
'x = "ab"\n', # single literal
"x = \"ab\" 'cd'\n", # mixed quote style
'x = b"a" b"b"\n', # bytes: left side-by-side by request
'x = rb"a" rb"b"\n', # raw-bytes: also left alone
'm = f"a {x} " f"after {y}"\n', # pure f + f: left side-by-side
'x = rf"a{z}" "b"\n', # raw f-string: brace/backslash too subtle -> skip
'x = f"a{z}" "\\N{BULLET}"\n', # named escape: AST guard rejects the fold
'x = (\n "a"\n "b"\n)\n', # different lines, not merged
],
)
def test_merge_adjacent_strings_skips(src):
out, changed = merge_adjacent_string_literals(src)
assert changed is False
assert out == src
def test_fstring_fold_skipped_when_statement_would_not_collapse():
# Folding a long f + plain assert message can't fit on one line, so leave it.
src = (
"def f():\n"
" assert some_condition_holds_here, (\n"
' f"a fairly detailed message about {value} explaining " "why this failed badly"\n'
" )\n"
)
out, changed = merge_adjacent_string_literals(src)
assert changed is False
assert out == src
def test_fstring_fold_applied_when_statement_collapses():
# A multi-line f + plain that fits on one line after folding is folded.
src = "def f():\n raise ValueError(\n" ' f"bad {x}: " "try again"\n' " )\n"
out, changed = merge_adjacent_string_literals(src)
assert changed is True
assert 'f"bad {x}: try again"' in out
assert ast.dump(ast.parse(out)) == ast.dump(ast.parse(src))
def test_fstring_fold_applied_inside_large_multiline_call():
# The fit guard only restricts asserts; an f + plain arg in a big call folds.
src = (
"findings.append(\n"
" Finding(\n"
" path=str(path),\n"
" package=key,\n"
' detail=(f"{name}@{ver} is on the " "BLOCKED list"),\n'
" )\n"
")\n"
)
out, changed = merge_adjacent_string_literals(src)
assert changed is True
assert 'detail=(f"{name}@{ver} is on the BLOCKED list")' in out
assert ast.dump(ast.parse(out)) == ast.dump(ast.parse(src))
# ── collapse_short_asserts: strip the magic comma holding a short assert open ──
# Strips the trailing comma so ruff joins the assert onto one line; AST unchanged.
@pytest.mark.parametrize(
"name,src",
[
(
"dict_eq",
'def t():\n assert got == {\n "a": 1,\n "b": 2,\n }\n',
),
(
"list_eq",
'def t():\n assert xs == [\n "a",\n "b",\n "c",\n ]\n',
),
(
"membership",
'def t():\n assert {\n "type": "x",\n "name": "y",\n } in tools\n',
),
(
"tuple_message",
"def t():\n assert cond, (\n base,\n headers,\n )\n",
),
(
"call_args",
"def t():\n assert eq(\n a,\n b,\n )\n",
),
],
)
def test_collapse_short_assert_strips_trailing_comma(name, src):
out, changed = collapse_short_asserts(src)
assert changed is True
# Magic trailing comma is gone, so ruff joins it on the next pass.
assert out.count(",") == src.count(",") - 1
assert ast.dump(ast.parse(out)) == ast.dump(ast.parse(src))
out2, changed2 = collapse_short_asserts(out)
assert out2 == out and changed2 is False
@pytest.mark.parametrize(
"name,src",
[
# one-element tuple message: stripping (only,) -> (only) changes meaning.
("one_tuple_message", "def t():\n assert cond, (\n only,\n )\n"),
# a comment inside keeps ruff multi-line, so collapsing would oscillate.
(
"comment_inside",
'def t():\n assert x == {\n "a": 1, # keep\n "b": 2,\n }\n',
),
# genuinely long: would not fit on one line, leave expanded.
(
"too_long",
"def t():\n assert some_really_long_left_operand_name_here == {\n"
' "alpha": 11111111,\n "beta": 22222222,\n'
' "gamma": 33333333,\n "delta": 44444444,\n }\n',
),
# already one line: nothing to do.
("one_line", 'def t():\n assert got == {"a": 1, "b": 2}\n'),
],
)
def test_collapse_short_assert_left_alone(name, src):
out, changed = collapse_short_asserts(src)
assert changed is False
assert out == src