Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,3 +139,96 @@ def _install_device_type_stub(name: str) -> None:
if not _preload_device_type("unsloth"):
_install_device_type_stub("unsloth.device_type")
_patch_torch_cuda_for_import()


# ---------------------------------------------------------------------------
# Apply unsloth-local upstream-drift fixes that need to run before pytest
# collects tests that import the affected third-party module directly.
#
# Specifically: ``from peft.utils import transformers_weight_conversion``
# blows up on (peft 0.19.x + transformers 4.x) because peft unconditionally
# imports two transformers-v5 submodules at module top. The production
# import path applies the stub-injection workaround via
# ``unsloth/_gpu_init.py``, but the GPU-free test harness above
# deliberately avoids triggering the full ``unsloth`` package init (which
# pulls in the CUDA / torch device chain). Load just the standalone
# import-fixes module by file path so drift detectors that probe peft
# see the same patched state a real unsloth install would.
# ---------------------------------------------------------------------------


def _apply_unsloth_peft_import_fix_for_tests() -> None:
import importlib.util as _ilu

try:
pkg_spec = _ilu.find_spec("unsloth")
except Exception:
return
if pkg_spec is None or not pkg_spec.submodule_search_locations:
return
fix_path = os.path.join(
pkg_spec.submodule_search_locations[0],
"import_fixes.py",
)
if not os.path.exists(fix_path):
return

mod_name = "unsloth.import_fixes"
_installed_skeleton = False
if mod_name in sys.modules:
mod = sys.modules[mod_name]
else:
# Submodule import requires SOME parent ``unsloth`` entry in
# sys.modules. Reuse one if a sibling conftest step already
# installed it (and don't pop in that case); otherwise install a
# bare skeleton and pop on the way out so subsequent
# ``import unsloth`` calls hit the real package init.
if "unsloth" not in sys.modules:
pkg = types.ModuleType("unsloth")
pkg.__path__ = list(pkg_spec.submodule_search_locations)
pkg.__spec__ = pkg_spec
pkg.__package__ = "unsloth"
pkg.__file__ = os.path.join(
pkg_spec.submodule_search_locations[0],
"__init__.py",
)
sys.modules["unsloth"] = pkg
_installed_skeleton = True
spec = _ilu.spec_from_file_location(mod_name, fix_path)
if spec is None or spec.loader is None:
if _installed_skeleton:
sys.modules.pop("unsloth", None)
return
mod = _ilu.module_from_spec(spec)
sys.modules[mod_name] = mod
try:
spec.loader.exec_module(mod)
except Exception:
sys.modules.pop(mod_name, None)
if _installed_skeleton:
sys.modules.pop("unsloth", None)
return

fix = getattr(mod, "fix_peft_transformers_weight_conversion_import", None)
if fix is None:
if _installed_skeleton:
sys.modules.pop("unsloth", None)
return
try:
fix()
except Exception:
# Individual fix is internally guarded; if the entry point itself
# blows up, don't take pytest collection down.
pass
finally:
# Drop our scratch skeleton so subsequent ``import unsloth``
# calls hit the real package init rather than our empty
# placeholder. The import-fixes module itself stays in
# sys.modules under ``unsloth.import_fixes`` -- python's import
# machinery is happy to find a submodule without an active
# parent entry.
if _installed_skeleton:
sys.modules.pop("unsloth", None)


_apply_unsloth_peft_import_fix_for_tests()
11 changes: 11 additions & 0 deletions unsloth/_gpu_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@
disable_torchcodec_if_broken,
disable_broken_wandb,
fix_trl_vllm_ascend,
fix_peft_transformers_weight_conversion_import,
patch_peft_weight_converter_compatibility,
)

Expand All @@ -177,6 +178,15 @@
patch_torchcodec_audio_decoder()
disable_torchcodec_if_broken()
disable_broken_wandb()
# Must run BEFORE patch_peft_weight_converter_compatibility: on peft 0.19.x
# + transformers 4.x, ``from peft.utils import transformers_weight_conversion``
# raises ModuleNotFoundError because peft unconditionally imports
# ``transformers.conversion_mapping`` and ``transformers.core_model_loading``
# at module top, but neither exists on transformers <5. Stubbing those two
# submodules first lets the converter compat patch actually wrap
# ``build_peft_weight_mapping`` instead of silently no-opping in its bare
# ``except (ImportError, AttributeError): return``.
fix_peft_transformers_weight_conversion_import()
patch_peft_weight_converter_compatibility()

del fix_xformers_performance_issue
Expand All @@ -199,6 +209,7 @@
del patch_torchcodec_audio_decoder
del disable_torchcodec_if_broken
del disable_broken_wandb
del fix_peft_transformers_weight_conversion_import
del patch_peft_weight_converter_compatibility

# Torch 2.4 has including_emulation
Expand Down
Loading
Loading