* Reduce and tighten comments and docstrings in tests Shorten verbose comments and docstrings across the test suite without changing any test logic. Remove narration that restates the next line, collapse long module and test docstrings to a single line, and drop banner separators. Keep regression context (issue and PR references, run ids), skip reasons, mocking and timing rationale, license headers, lint and type directives, and commented-out code. Comments and docstrings only: an AST signature check confirms no code, assertions, or string literals changed, and the suite byte-compiles cleanly. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
339 lines
12 KiB
Python
339 lines
12 KiB
Python
# SPDX-License-Identifier: AGPL-3.0-only
|
|
# Copyright 2026-present the Unsloth AI Inc. team.
|
|
"""Pinned-symbol compat check across PEFT minor versions unsloth + unsloth-zoo
|
|
target. For each tracked tag, fetch source from github.com/huggingface/peft and
|
|
assert every PEFT symbol unsloth touches is present, catching API drift.
|
|
Versioning covers unsloth/pyproject.toml's `peft>=0.18.0,!=0.11.0` window + main.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
|
|
import pytest
|
|
|
|
from tests.version_compat._fetch import fetch_text, first_match, has_def
|
|
|
|
|
|
# pyproject pin: peft>=0.18.0. Test the floor + each minor since.
|
|
# `main` catches breakage before a release lands.
|
|
PEFT_TAGS = [
|
|
"v0.18.0",
|
|
"v0.18.1",
|
|
"v0.19.0",
|
|
"v0.19.1",
|
|
"main",
|
|
]
|
|
|
|
|
|
# Top-level re-exports: sentence_transformer.py:1948 does `from peft import
|
|
# LoraConfig, get_peft_model`; unsloth_zoo saving_utils/lora extractors hit PeftModel.
|
|
|
|
|
|
@pytest.mark.parametrize("tag", PEFT_TAGS)
|
|
def test_peft_top_level_exports(tag: str):
|
|
src = fetch_text("huggingface/peft", tag, "src/peft/__init__.py")
|
|
assert src is not None, f"{tag}: src/peft/__init__.py missing"
|
|
needed = (
|
|
"LoraConfig",
|
|
"get_peft_model",
|
|
"PeftModel",
|
|
)
|
|
missing = [n for n in needed if n not in src]
|
|
assert not missing, (
|
|
f"{tag}: peft top-level missing {missing}; "
|
|
f"unsloth.models.sentence_transformer:1948 + unsloth-zoo saving_utils "
|
|
f"will ImportError"
|
|
)
|
|
|
|
|
|
# LoraConfig at the canonical sub-module path: peft.tuners.lora.LoraConfig
|
|
# (or peft.tuners.lora.config.LoraConfig). unsloth-zoo's normaliser inspects
|
|
# it via getattr() and dataclass field introspection.
|
|
|
|
|
|
@pytest.mark.parametrize("tag", PEFT_TAGS)
|
|
def test_peft_lora_config_class(tag: str):
|
|
candidates = [
|
|
"src/peft/tuners/lora/config.py",
|
|
"src/peft/tuners/lora/__init__.py",
|
|
"src/peft/tuners/lora.py",
|
|
]
|
|
found_in = []
|
|
for p in candidates:
|
|
src = fetch_text("huggingface/peft", tag, p)
|
|
if src is not None and has_def(src, "LoraConfig", "class"):
|
|
found_in.append(p)
|
|
assert found_in, f"{tag}: peft.tuners.lora.LoraConfig not in any of {candidates}"
|
|
|
|
|
|
# get_peft_model: top-level helper used by sentence_transformer.py:2043.
|
|
|
|
|
|
@pytest.mark.parametrize("tag", PEFT_TAGS)
|
|
def test_get_peft_model_function(tag: str):
|
|
"""get_peft_model may live in mapping.py or mapping_func.py (0.18+ split)."""
|
|
candidates = [
|
|
"src/peft/mapping.py",
|
|
"src/peft/mapping_func.py",
|
|
"src/peft/__init__.py",
|
|
"src/peft/peft_model.py",
|
|
]
|
|
for p in candidates:
|
|
src = fetch_text("huggingface/peft", tag, p)
|
|
if src is not None and has_def(src, "get_peft_model", "func"):
|
|
return
|
|
pytest.fail(f"{tag}: def get_peft_model(...) not found in any of {candidates}")
|
|
|
|
|
|
# LoraLayer base class: unsloth-zoo's MoE LoRA extractor walks subclasses of
|
|
# peft.tuners.lora.LoraLayer. A rename/move makes the walk silently return 0.
|
|
|
|
|
|
@pytest.mark.parametrize("tag", PEFT_TAGS)
|
|
def test_peft_lora_layer_class(tag: str):
|
|
candidates = [
|
|
"src/peft/tuners/lora/layer.py",
|
|
"src/peft/tuners/lora/__init__.py",
|
|
"src/peft/tuners/lora.py",
|
|
]
|
|
for p in candidates:
|
|
src = fetch_text("huggingface/peft", tag, p)
|
|
if src is not None and has_def(src, "LoraLayer", "class"):
|
|
return
|
|
pytest.fail(
|
|
f"{tag}: class LoraLayer not in any of {candidates} — "
|
|
f"unsloth-zoo MoE LoRA extractor relies on isinstance checks "
|
|
f"against this class"
|
|
)
|
|
|
|
|
|
# bnb-aware LoRA: peft.tuners.lora.bnb is the bitsandbytes integration point.
|
|
# Missing it -> 4bit LoRA silently falls back to fp16 (bigger memory footprint).
|
|
|
|
|
|
@pytest.mark.parametrize("tag", PEFT_TAGS)
|
|
def test_peft_lora_bnb_integration(tag: str):
|
|
candidates = [
|
|
"src/peft/tuners/lora/bnb.py",
|
|
"src/peft/tuners/lora/_bnb.py",
|
|
]
|
|
for p in candidates:
|
|
src = fetch_text("huggingface/peft", tag, p)
|
|
if src is None:
|
|
continue
|
|
# At least one bnb-flavoured Linear must exist (either name is fine).
|
|
has_4bit = any(
|
|
cls in src
|
|
for cls in (
|
|
"class Linear4bit",
|
|
"class Linear8bitLt",
|
|
"class _Linear4bit",
|
|
"class _Linear8bitLt",
|
|
)
|
|
)
|
|
if has_4bit:
|
|
return
|
|
pytest.fail(
|
|
f"{tag}: peft.tuners.lora.bnb missing or no Linear4bit/Linear8bitLt "
|
|
f"class found; unsloth's 4-bit LoRA path silently degrades to fp16"
|
|
)
|
|
|
|
|
|
# Coverage extension (added 2026-05): symbols from the 8-PR audit
|
|
# unsloth#5015, #5167, #5036, #4807 + unsloth-zoo#618, #596, #482, #430.
|
|
|
|
|
|
# 1. peft.tuners.lora.layer.VARIANT_KWARG_KEYS — added in peft 0.18.
|
|
# unsloth-zoo#430 injects the import into the compiled forward.
|
|
|
|
|
|
@pytest.mark.parametrize("tag", PEFT_TAGS)
|
|
def test_peft_variant_kwarg_keys_const(tag: str):
|
|
src = fetch_text("huggingface/peft", tag, "src/peft/tuners/lora/layer.py")
|
|
if src is None:
|
|
pytest.skip(f"{tag}: src/peft/tuners/lora/layer.py missing")
|
|
if "VARIANT_KWARG_KEYS" not in src:
|
|
pytest.fail(
|
|
f"{tag}: peft.tuners.lora.layer.VARIANT_KWARG_KEYS missing; "
|
|
f"unsloth_zoo/compiler.py:2645 import injection breaks (unsloth-zoo#430)"
|
|
)
|
|
|
|
|
|
# 2. peft.tuners.lora.layer.ParamWrapper — peft 0.18 added the class for MoE
|
|
# 3D-parameter LoRA. unsloth-zoo#618 monkey-patches the MoE LoRA extractor.
|
|
|
|
|
|
@pytest.mark.parametrize("tag", PEFT_TAGS)
|
|
def test_peft_param_wrapper_class(tag: str):
|
|
src = fetch_text("huggingface/peft", tag, "src/peft/tuners/lora/layer.py")
|
|
if src is None:
|
|
pytest.skip(f"{tag}: layer.py missing")
|
|
assert has_def(src, "ParamWrapper", "class"), (
|
|
f"{tag}: peft.tuners.lora.layer.ParamWrapper missing; "
|
|
f"unsloth_zoo/temporary_patches/qwen3_moe.py:43-130 + "
|
|
f"moe_utils.py:757 ImportError (unsloth-zoo#618)"
|
|
)
|
|
# Member names: informational only; the real bug to catch is full removal.
|
|
for name in ("parameter_name", "forward", "lora_A", "get_base_layer"):
|
|
_present = name in src
|
|
|
|
|
|
# 3. peft.tuners.lora.LoraConfig.target_parameters — peft 0.19+. Used by
|
|
# unsloth-zoo's MoE target-parameter extractor.
|
|
|
|
|
|
@pytest.mark.parametrize("tag", PEFT_TAGS)
|
|
def test_peft_lora_config_target_parameters(tag: str):
|
|
src = fetch_text("huggingface/peft", tag, "src/peft/tuners/lora/config.py")
|
|
if src is None:
|
|
pytest.skip(f"{tag}: src/peft/tuners/lora/config.py missing")
|
|
# Optional on 0.18.x, required from 0.19.0+
|
|
has_it = "target_parameters" in src
|
|
if "0.18" in tag and not has_it:
|
|
pytest.skip(f"{tag}: target_parameters not yet introduced (peft 0.18)")
|
|
assert has_it, (
|
|
f"{tag}: LoraConfig.target_parameters missing on peft >=0.19; "
|
|
f"unsloth-zoo MoE target-parameter extraction breaks"
|
|
)
|
|
|
|
|
|
# 4. peft.tuners.lora.model.LoraModel._create_and_replace — unsloth#4807
|
|
# monkey-patches this for Gemma4ClippableLinear.
|
|
|
|
|
|
@pytest.mark.parametrize("tag", PEFT_TAGS)
|
|
def test_peft_lora_model_create_and_replace(tag: str):
|
|
src = fetch_text("huggingface/peft", tag, "src/peft/tuners/lora/model.py")
|
|
if src is None:
|
|
pytest.skip(f"{tag}: src/peft/tuners/lora/model.py missing")
|
|
assert has_def(src, "LoraModel", "class"), f"{tag}: class LoraModel missing"
|
|
assert has_def(src, "_create_and_replace", "func"), (
|
|
f"{tag}: LoraModel._create_and_replace missing; "
|
|
f"unsloth/models/loader.py:1535-1601 monkey-patch breaks (unsloth#4807)"
|
|
)
|
|
|
|
|
|
# 5. peft.utils.transformers_weight_conversion.build_peft_weight_mapping —
|
|
# unsloth#5167 wraps it to handle WeightConversion.__init__ kwargs.
|
|
|
|
|
|
@pytest.mark.parametrize("tag", PEFT_TAGS)
|
|
def test_peft_transformers_weight_conversion_module(tag: str):
|
|
candidates = [
|
|
"src/peft/utils/transformers_weight_conversion.py",
|
|
"src/peft/utils/transformers_weight_conversion/__init__.py",
|
|
]
|
|
hit = first_match("huggingface/peft", tag, candidates)
|
|
if hit is None:
|
|
pytest.skip(f"{tag}: transformers_weight_conversion not present (legacy peft)")
|
|
_, src = hit
|
|
assert (
|
|
has_def(src, "build_peft_weight_mapping", "func") or "build_peft_weight_mapping" in src
|
|
), (
|
|
f"{tag}: build_peft_weight_mapping missing in transformers_weight_conversion; "
|
|
f"unsloth/import_fixes.py:1375-1456 wrap breaks (unsloth#5167)"
|
|
)
|
|
|
|
|
|
# 6. peft.utils.integrations.dequantize_module_weight — used by 3 unsloth/
|
|
# unsloth-zoo callsites.
|
|
|
|
|
|
@pytest.mark.parametrize("tag", PEFT_TAGS)
|
|
def test_peft_integrations_dequantize_module_weight(tag: str):
|
|
candidates = [
|
|
"src/peft/utils/integrations.py",
|
|
"src/peft/utils/integrations/__init__.py",
|
|
]
|
|
hit = first_match("huggingface/peft", tag, candidates)
|
|
assert hit is not None, f"{tag}: src/peft/utils/integrations[.py|/__init__.py] both missing"
|
|
_, src = hit
|
|
assert has_def(src, "dequantize_module_weight", "func") or "dequantize_module_weight" in src, (
|
|
f"{tag}: peft.utils.integrations.dequantize_module_weight missing; "
|
|
f"unsloth-zoo vllm_utils.py:2701, unsloth/_utils.py:1550, "
|
|
f"saving_utils.py:270 ImportError"
|
|
)
|
|
|
|
|
|
# 7. peft.PeftType.LORA — used by unsloth-zoo vllm_utils.py:2520-2559.
|
|
|
|
|
|
@pytest.mark.parametrize("tag", PEFT_TAGS)
|
|
def test_peft_type_lora_enum(tag: str):
|
|
candidates = [
|
|
"src/peft/utils/peft_types.py",
|
|
"src/peft/utils/__init__.py",
|
|
"src/peft/__init__.py",
|
|
]
|
|
for p in candidates:
|
|
src = fetch_text("huggingface/peft", tag, p)
|
|
if src is None:
|
|
continue
|
|
# Either `class PeftType(...)` definition with LORA member, or
|
|
# re-export from a submodule.
|
|
if "PeftType" in src and ("LORA" in src or "lora" in src.lower()):
|
|
return
|
|
pytest.fail(
|
|
f"{tag}: peft.PeftType (with LORA member) not in any of {candidates}; "
|
|
f"unsloth-zoo vllm_utils.py:2520 reference breaks"
|
|
)
|
|
|
|
|
|
# 8. peft.utils.ModulesToSaveWrapper — both peft.utils.* and
|
|
# peft.utils.other.* import paths used.
|
|
|
|
|
|
@pytest.mark.parametrize("tag", PEFT_TAGS)
|
|
def test_peft_modules_to_save_wrapper(tag: str):
|
|
candidates = [
|
|
"src/peft/utils/other.py",
|
|
"src/peft/utils/__init__.py",
|
|
]
|
|
found_in = []
|
|
for p in candidates:
|
|
src = fetch_text("huggingface/peft", tag, p)
|
|
if src is None:
|
|
continue
|
|
if has_def(src, "ModulesToSaveWrapper", "class"):
|
|
found_in.append(p)
|
|
assert found_in, (
|
|
f"{tag}: ModulesToSaveWrapper not defined in {candidates}; "
|
|
f"unsloth/training_utils.py:239 + models/llama.py:153 ImportError"
|
|
)
|
|
|
|
|
|
# 9. peft.PeftModel.from_pretrained signature pin — unsloth#4807.
|
|
|
|
|
|
@pytest.mark.parametrize("tag", PEFT_TAGS)
|
|
def test_peft_peft_model_from_pretrained_signature(tag: str):
|
|
src = fetch_text("huggingface/peft", tag, "src/peft/peft_model.py")
|
|
assert src is not None, f"{tag}: src/peft/peft_model.py missing"
|
|
# Just check the method name; the full kwarg list is too brittle.
|
|
assert has_def(
|
|
src, "from_pretrained", "func"
|
|
), f"{tag}: PeftModel.from_pretrained missing in peft_model.py"
|
|
|
|
|
|
# 10. peft.__version__ exported via known mechanism.
|
|
|
|
|
|
@pytest.mark.parametrize("tag", PEFT_TAGS)
|
|
def test_peft_version_parseable(tag: str):
|
|
src = fetch_text("huggingface/peft", tag, "src/peft/__init__.py")
|
|
assert src is not None
|
|
# Same gates as the TRL test: literal / submodule / metadata
|
|
has_literal = bool(re.search(r'^__version__\s*=\s*["\']', src, re.MULTILINE))
|
|
has_subimport = bool(re.search(r"^from\s+\.version\s+import\s+__version__", src, re.MULTILINE))
|
|
has_metadata = bool(
|
|
re.search(
|
|
r"^from\s+importlib\.metadata\s+import\s+(?:[\w,\s]+,\s*)?version",
|
|
src,
|
|
re.MULTILINE,
|
|
)
|
|
and re.search(r"^\s*__version__\s*=\s*version\s*\(", src, re.MULTILINE)
|
|
)
|
|
assert (
|
|
has_literal or has_subimport or has_metadata
|
|
), f"{tag}: peft.__version__ not exported via any known mechanism"
|