Merge remote-tracking branch 'origin/main' into windows-torch-211
Three files conflicted, all on the gfx906 (MI50 / Radeon VII) work that landed on main next to this branch's comment pass and torch 2.11 default rollout. install.sh: kept main's _is_gfx906_bnb_skip / _gfx906_bnb_snapshot / _prune helpers and the _gfx906_bnb_snapshot call in the migrated path, with this branch's condensed comments. The gfx906 reroute keeps its literal TORCH_CONSTRAINT="torch>=2.4,<2.11.0": the rocm6.3 index it routes to tops out at torch 2.9.x, so it is now a deliberate narrower cap rather than a restatement of the default, and the comment says so. studio/install_python_stack.py: kept main's _runtime_is_gfx906 detection, the rocm6.3 legacy override and the bnb skip/prune branch, again with the condensed comments. _ROCM_TORCH_PKG_SPECS["_default"] stays at <2.11.0, which is what the gfx906 path installs from. tests: the two sides asserted opposite things about hard-coded torch ranges in install.sh. This branch composes the default from _TORCH_CEILING and asserted zero literals; main asserted the literal exists and only on a TORCH_CONSTRAINT= assignment. Merged into the rule both wanted: a literal range is allowed only on a curated per-index TORCH_CONSTRAINT= override, never on a pip/uv install line, plus a separate check that the gfx906 reroute keeps its sub-2.11 cap. The same resolution is applied to tests/sh/test_torch_constraint.sh, which asserted the zero-literal count and would otherwise have gone red on main's new assignment.
This commit is contained in:
commit
ac8bc5f9a3
266 changed files with 33916 additions and 1889 deletions
|
|
@ -40,7 +40,7 @@ def _registrations(source):
|
|||
|
||||
|
||||
def test_cpo_registration_matches_orpo():
|
||||
regs = _registrations(open(RL_PATH).read())
|
||||
regs = _registrations(open(RL_PATH, encoding = "utf-8").read())
|
||||
shared = {"orpo_trainer_text_tokenizer", "orpo_trainer_processor_pad_token"}
|
||||
assert shared <= set(regs.get("orpo_trainer", []))
|
||||
assert shared <= set(regs.get("cpo_trainer", []))
|
||||
|
|
@ -48,7 +48,7 @@ def test_cpo_registration_matches_orpo():
|
|||
|
||||
def _load_pad_rewriter():
|
||||
"""Exec orpo_trainer_processor_pad_token (+ _PAD_FALLBACK) without importing unsloth."""
|
||||
tree = ast.parse(open(RL_PATH).read())
|
||||
tree = ast.parse(open(RL_PATH, encoding = "utf-8").read())
|
||||
nodes = []
|
||||
for n in tree.body:
|
||||
if isinstance(n, ast.Assign) and any(
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ RL_PATH = os.path.join(REPO_ROOT, "unsloth", "models", "rl_replacements.py")
|
|||
|
||||
|
||||
def _load_helpers():
|
||||
src = open(RL_PATH).read()
|
||||
src = open(RL_PATH, encoding = "utf-8").read()
|
||||
tree = ast.parse(src)
|
||||
import torch as _torch
|
||||
|
||||
|
|
|
|||
|
|
@ -193,7 +193,7 @@ class TestBeforeAfterImportChain:
|
|||
mm = types.ModuleType('model_mappings')
|
||||
mm.MODEL_TO_TEMPLATE_MAPPER = {{}}
|
||||
sys.modules['model_mappings'] = mm
|
||||
source = open({str(before_file)!r}).read()
|
||||
source = open({str(before_file)!r}, encoding = "utf-8").read()
|
||||
source = source.replace('from .format_detection import', 'from format_detection import')
|
||||
source = source.replace('from .model_mappings import', 'from model_mappings import')
|
||||
exec(source)
|
||||
|
|
@ -215,7 +215,7 @@ class TestBeforeAfterImportChain:
|
|||
loggers = types.ModuleType('loggers')
|
||||
loggers.get_logger = lambda n: None
|
||||
sys.modules['loggers'] = loggers
|
||||
exec(open({str(before_file)!r}).read())
|
||||
exec(open({str(before_file)!r}, encoding = "utf-8").read())
|
||||
""")
|
||||
result = _run_in_sandbox(no_torch_venv, code)
|
||||
assert result.returncode != 0, "BEFORE data_collators.py should crash without torch"
|
||||
|
|
@ -284,7 +284,7 @@ class TestBeforeAfterImportChain:
|
|||
it = types.ModuleType('iterable')
|
||||
it.is_streaming_dataset = lambda *a, **k: False
|
||||
sys.modules['iterable'] = it
|
||||
source = open({str(CHAT_TEMPLATES)!r}).read()
|
||||
source = open({str(CHAT_TEMPLATES)!r}, encoding = "utf-8").read()
|
||||
source = source.replace('from .format_detection import', 'from format_detection import')
|
||||
source = source.replace('from .model_mappings import', 'from model_mappings import')
|
||||
source = source.replace('from .iterable import', 'from iterable import')
|
||||
|
|
@ -304,7 +304,7 @@ class TestBeforeAfterImportChain:
|
|||
loggers = types.ModuleType('loggers')
|
||||
loggers.get_logger = lambda n: None
|
||||
sys.modules['loggers'] = loggers
|
||||
exec(open({str(DATA_COLLATORS)!r}).read())
|
||||
exec(open({str(DATA_COLLATORS)!r}, encoding = "utf-8").read())
|
||||
print("OK")
|
||||
""")
|
||||
result = _run_in_sandbox(no_torch_venv, code)
|
||||
|
|
@ -382,7 +382,7 @@ class TestDataclassInstantiation:
|
|||
loggers = types.ModuleType('loggers')
|
||||
loggers.get_logger = lambda n: None
|
||||
sys.modules['loggers'] = loggers
|
||||
exec(open({str(DATA_COLLATORS)!r}).read())
|
||||
exec(open({str(DATA_COLLATORS)!r}, encoding = "utf-8").read())
|
||||
obj = DataCollatorSpeechSeq2SeqWithPadding(processor=None)
|
||||
assert obj.processor is None
|
||||
print("OK")
|
||||
|
|
@ -397,7 +397,7 @@ class TestDataclassInstantiation:
|
|||
loggers = types.ModuleType('loggers')
|
||||
loggers.get_logger = lambda n: None
|
||||
sys.modules['loggers'] = loggers
|
||||
exec(open({str(DATA_COLLATORS)!r}).read())
|
||||
exec(open({str(DATA_COLLATORS)!r}, encoding = "utf-8").read())
|
||||
obj = DeepSeekOCRDataCollator(processor=None)
|
||||
assert obj.processor is None
|
||||
assert obj.max_length == 2048
|
||||
|
|
@ -414,7 +414,7 @@ class TestDataclassInstantiation:
|
|||
loggers = types.ModuleType('loggers')
|
||||
loggers.get_logger = lambda n: None
|
||||
sys.modules['loggers'] = loggers
|
||||
exec(open({str(DATA_COLLATORS)!r}).read())
|
||||
exec(open({str(DATA_COLLATORS)!r}, encoding = "utf-8").read())
|
||||
obj = VLMDataCollator(processor=None)
|
||||
assert obj.processor is None
|
||||
assert obj.max_length == 2048
|
||||
|
|
@ -441,7 +441,7 @@ class TestDataclassInstantiation:
|
|||
it.is_streaming_dataset = lambda *a, **k: False
|
||||
sys.modules['iterable'] = it
|
||||
ns = {{}}
|
||||
source = open({str(CHAT_TEMPLATES)!r}).read()
|
||||
source = open({str(CHAT_TEMPLATES)!r}, encoding = "utf-8").read()
|
||||
source = source.replace('from .format_detection import', 'from format_detection import')
|
||||
source = source.replace('from .model_mappings import', 'from model_mappings import')
|
||||
source = source.replace('from .iterable import', 'from iterable import')
|
||||
|
|
@ -473,7 +473,7 @@ class TestEdgeCasesBrokenTorch:
|
|||
code = textwrap.dedent(f"""\
|
||||
import sys
|
||||
sys.path.insert(0, {str(sandbox_dir)!r})
|
||||
exec(open({str(sandbox_dir / 'data_collators.py')!r}).read())
|
||||
exec(open({str(sandbox_dir / 'data_collators.py')!r}, encoding = "utf-8").read())
|
||||
obj = DataCollatorSpeechSeq2SeqWithPadding(processor=None)
|
||||
print("OK: data_collators works despite broken torch on sys.path")
|
||||
""")
|
||||
|
|
@ -495,7 +495,7 @@ class TestEdgeCasesBrokenTorch:
|
|||
code = textwrap.dedent(f"""\
|
||||
import sys
|
||||
sys.path.insert(0, {str(sandbox_dir)!r})
|
||||
source = open({str(HARDWARE_PY)!r}).read()
|
||||
source = open({str(HARDWARE_PY)!r}, encoding = "utf-8").read()
|
||||
ns = {{'__name__': '__test__'}}
|
||||
exec(source, ns)
|
||||
result = ns['detect_hardware']()
|
||||
|
|
@ -530,7 +530,7 @@ class TestEdgeCasesBrokenTorch:
|
|||
code = textwrap.dedent(f"""\
|
||||
import sys
|
||||
sys.path.insert(0, {str(sandbox_dir)!r})
|
||||
source = open({str(HARDWARE_PY)!r}).read()
|
||||
source = open({str(HARDWARE_PY)!r}, encoding = "utf-8").read()
|
||||
ns = {{'__name__': '__test__'}}
|
||||
exec(source, ns)
|
||||
result = ns['detect_hardware']()
|
||||
|
|
@ -559,7 +559,7 @@ class TestEdgeCasesBrokenTorch:
|
|||
sys.modules['iterable'] = it
|
||||
|
||||
ns = {{}}
|
||||
source = open({str(CHAT_TEMPLATES)!r}).read()
|
||||
source = open({str(CHAT_TEMPLATES)!r}, encoding = "utf-8").read()
|
||||
source = source.replace('from .format_detection import', 'from format_detection import')
|
||||
source = source.replace('from .model_mappings import', 'from model_mappings import')
|
||||
source = source.replace('from .iterable import', 'from iterable import')
|
||||
|
|
@ -604,7 +604,7 @@ class TestHardwareDetectionNoTorch:
|
|||
code = textwrap.dedent(f"""\
|
||||
import sys
|
||||
sys.path.insert(0, {str(sandbox_dir)!r})
|
||||
source = open({str(HARDWARE_PY)!r}).read()
|
||||
source = open({str(HARDWARE_PY)!r}, encoding = "utf-8").read()
|
||||
ns = {{'__name__': '__test__'}}
|
||||
exec(source, ns)
|
||||
device = ns['detect_hardware']()
|
||||
|
|
@ -624,7 +624,7 @@ class TestHardwareDetectionNoTorch:
|
|||
code = textwrap.dedent(f"""\
|
||||
import sys
|
||||
sys.path.insert(0, {str(sandbox_dir)!r})
|
||||
source = open({str(HARDWARE_PY)!r}).read()
|
||||
source = open({str(HARDWARE_PY)!r}, encoding = "utf-8").read()
|
||||
ns = {{'__name__': '__test__'}}
|
||||
exec(source, ns)
|
||||
versions = ns['get_package_versions']()
|
||||
|
|
@ -651,7 +651,7 @@ class TestHardwareDetectionNoTorch:
|
|||
code = textwrap.dedent(f"""\
|
||||
import sys
|
||||
sys.path.insert(0, {str(sandbox_dir)!r})
|
||||
source = open({str(hw_sandbox / 'hardware.py')!r}).read()
|
||||
source = open({str(hw_sandbox / 'hardware.py')!r}, encoding = "utf-8").read()
|
||||
ns = {{'__name__': '__test__'}}
|
||||
exec(source, ns)
|
||||
assert callable(ns['detect_hardware'])
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ UTILS_PATH = REPO_ROOT / "unsloth" / "models" / "_utils.py"
|
|||
|
||||
|
||||
def _source(path):
|
||||
return path.read_text()
|
||||
return path.read_text(encoding = "utf-8")
|
||||
|
||||
|
||||
def _class_method(tree, class_name, method_name):
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ LLAMA_PATH = REPO_ROOT / "unsloth" / "models" / "llama.py"
|
|||
|
||||
|
||||
def _source(path):
|
||||
return path.read_text()
|
||||
return path.read_text(encoding = "utf-8")
|
||||
|
||||
|
||||
def _class_method(tree, class_name, method_name):
|
||||
|
|
|
|||
|
|
@ -17,13 +17,13 @@ def _find_geteuid_guard(tree: ast.AST):
|
|||
|
||||
|
||||
def test_gpu_init_has_geteuid_guard():
|
||||
tree = ast.parse(GPU_INIT.read_text())
|
||||
tree = ast.parse(GPU_INIT.read_text(encoding = "utf-8"))
|
||||
guard = _find_geteuid_guard(tree)
|
||||
assert guard is not None, "_gpu_init.py must guard ldconfig recovery on os.geteuid()"
|
||||
|
||||
|
||||
def test_ldconfig_calls_only_inside_geteuid_guard():
|
||||
src = GPU_INIT.read_text()
|
||||
src = GPU_INIT.read_text(encoding = "utf-8")
|
||||
tree = ast.parse(src)
|
||||
guard = _find_geteuid_guard(tree)
|
||||
assert guard is not None
|
||||
|
|
@ -39,6 +39,6 @@ def test_ldconfig_calls_only_inside_geteuid_guard():
|
|||
|
||||
|
||||
def test_non_root_branch_warns_when_bnb_present():
|
||||
src = GPU_INIT.read_text()
|
||||
src = GPU_INIT.read_text(encoding = "utf-8")
|
||||
assert "elif bnb is not None" in src
|
||||
assert "sudo ldconfig" in src
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ SOURCE_PATH = os.path.join(REPO_ROOT, "unsloth", "models", "rl_replacements.py")
|
|||
|
||||
|
||||
def _read_source() -> str:
|
||||
with open(SOURCE_PATH, "r") as fh:
|
||||
with open(SOURCE_PATH, "r", encoding = "utf-8") as fh:
|
||||
return fh.read()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ RL_PATH = os.path.join(REPO_ROOT, "unsloth", "models", "rl_replacements.py")
|
|||
|
||||
|
||||
def _load_orpo_rewriter(name = "orpo_trainer_text_tokenizer"):
|
||||
src = open(RL_PATH).read()
|
||||
src = open(RL_PATH, encoding = "utf-8").read()
|
||||
tree = ast.parse(src)
|
||||
ns = {"re": re}
|
||||
# Materialise sibling module-level _-prefixed assignments the rewriter may reference.
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ WANTED = {
|
|||
|
||||
def _load_pad_helpers():
|
||||
"""Exec only the pad-token helpers with a stub logger (no heavy imports)."""
|
||||
tree = ast.parse(open(TOK_PATH).read())
|
||||
tree = ast.parse(open(TOK_PATH, encoding = "utf-8").read())
|
||||
nodes = []
|
||||
for node in tree.body:
|
||||
if isinstance(node, ast.Assign):
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ class TestDataCollatorsNoTorchVenv:
|
|||
loggers = types.ModuleType('loggers')
|
||||
loggers.get_logger = lambda n: None
|
||||
sys.modules['loggers'] = loggers
|
||||
exec(open({str(DATA_COLLATORS)!r}).read())
|
||||
exec(open({str(DATA_COLLATORS)!r}, encoding = "utf-8").read())
|
||||
print("OK: exec succeeded")
|
||||
""")
|
||||
result = subprocess.run(
|
||||
|
|
@ -168,7 +168,7 @@ class TestDataCollatorsNoTorchVenv:
|
|||
loggers = types.ModuleType('loggers')
|
||||
loggers.get_logger = lambda n: None
|
||||
sys.modules['loggers'] = loggers
|
||||
exec(open({str(DATA_COLLATORS)!r}).read())
|
||||
exec(open({str(DATA_COLLATORS)!r}, encoding = "utf-8").read())
|
||||
obj = DataCollatorSpeechSeq2SeqWithPadding(processor=None)
|
||||
assert obj.processor is None, "processor should be None"
|
||||
print("OK: DataCollatorSpeechSeq2SeqWithPadding instantiated")
|
||||
|
|
@ -190,7 +190,7 @@ class TestDataCollatorsNoTorchVenv:
|
|||
loggers = types.ModuleType('loggers')
|
||||
loggers.get_logger = lambda n: None
|
||||
sys.modules['loggers'] = loggers
|
||||
exec(open({str(DATA_COLLATORS)!r}).read())
|
||||
exec(open({str(DATA_COLLATORS)!r}, encoding = "utf-8").read())
|
||||
obj = DeepSeekOCRDataCollator(processor=None)
|
||||
assert obj.processor is None, "processor should be None"
|
||||
assert obj.max_length == 2048, "default max_length should be 2048"
|
||||
|
|
@ -212,7 +212,7 @@ class TestDataCollatorsNoTorchVenv:
|
|||
loggers = types.ModuleType('loggers')
|
||||
loggers.get_logger = lambda n: None
|
||||
sys.modules['loggers'] = loggers
|
||||
exec(open({str(DATA_COLLATORS)!r}).read())
|
||||
exec(open({str(DATA_COLLATORS)!r}, encoding = "utf-8").read())
|
||||
obj = VLMDataCollator(processor=None)
|
||||
assert obj.processor is None
|
||||
assert obj.mask_input_tokens is True, "default mask_input_tokens should be True"
|
||||
|
|
@ -259,7 +259,7 @@ class TestChatTemplatesNoTorchVenv:
|
|||
sys.modules['iterable'] = iterable
|
||||
|
||||
# Read and transform the source: replace relative imports with absolute
|
||||
source = open({str(CHAT_TEMPLATES)!r}).read()
|
||||
source = open({str(CHAT_TEMPLATES)!r}, encoding = "utf-8").read()
|
||||
source = source.replace('from .format_detection import', 'from format_detection import')
|
||||
source = source.replace('from .model_mappings import', 'from model_mappings import')
|
||||
source = source.replace('from .iterable import', 'from iterable import')
|
||||
|
|
@ -305,7 +305,7 @@ class TestChatTemplatesNoTorchVenv:
|
|||
sys.modules['iterable'] = iterable
|
||||
|
||||
ns = {{}}
|
||||
source = open({str(CHAT_TEMPLATES)!r}).read()
|
||||
source = open({str(CHAT_TEMPLATES)!r}, encoding = "utf-8").read()
|
||||
source = source.replace('from .format_detection import', 'from format_detection import')
|
||||
source = source.replace('from .model_mappings import', 'from model_mappings import')
|
||||
source = source.replace('from .iterable import', 'from iterable import')
|
||||
|
|
@ -402,7 +402,7 @@ class TestFormatConversionNoTorchVenv:
|
|||
sys.modules['utils.hardware'] = hardware_mod
|
||||
|
||||
# Read and exec format_conversion.py
|
||||
source = open({str(FORMAT_CONVERSION)!r}).read()
|
||||
source = open({str(FORMAT_CONVERSION)!r}, encoding = "utf-8").read()
|
||||
source = source.replace('from .format_detection import', 'from format_detection import')
|
||||
source = source.replace('from .iterable import', 'from iterable import')
|
||||
ns = {{'__name__': '__test__'}}
|
||||
|
|
@ -463,7 +463,7 @@ class TestFormatConversionNoTorchVenv:
|
|||
sys.modules['utils'] = utils_mod
|
||||
sys.modules['utils.hardware'] = hardware_mod
|
||||
|
||||
source = open({str(FORMAT_CONVERSION)!r}).read()
|
||||
source = open({str(FORMAT_CONVERSION)!r}, encoding = "utf-8").read()
|
||||
source = source.replace('from .format_detection import', 'from format_detection import')
|
||||
source = source.replace('from .iterable import', 'from iterable import')
|
||||
ns = {{'__name__': '__test__'}}
|
||||
|
|
@ -517,7 +517,7 @@ class TestNegativeControls:
|
|||
loggers = types.ModuleType('loggers')
|
||||
loggers.get_logger = lambda n: None
|
||||
sys.modules['loggers'] = loggers
|
||||
exec(open({temp_file!r}).read())
|
||||
exec(open({temp_file!r}, encoding = "utf-8").read())
|
||||
""")
|
||||
result = subprocess.run(
|
||||
[no_torch_venv, "-c", code],
|
||||
|
|
|
|||
|
|
@ -83,11 +83,27 @@ class TestStructuralTorchConstraint:
|
|||
"""$TORCH_CONSTRAINT must appear in a uv pip install line."""
|
||||
assert '"$TORCH_CONSTRAINT"' in self._sh
|
||||
|
||||
def test_hardcoded_torch_constraint_gone(self):
|
||||
"""No hard-coded default ranges remain outside the ceiling-composed
|
||||
assignments (curated ROCm >=2.11 floors stay literal)."""
|
||||
assert self._sh.count('"torch>=2.4,<2.11.0"') == 0
|
||||
assert self._sh.count('"torch>=2.4,<2.12.0"') == 0
|
||||
def test_hardcoded_torch_constraint_only_on_assignments(self):
|
||||
"""The default range is composed from the ceiling vars, so the supported
|
||||
line is bumped in one place. A hard-coded range may still appear on a
|
||||
curated per-index TORCH_CONSTRAINT= override -- the gfx906 (MI50) reroute
|
||||
caps below 2.11 because the rocm6.3 index tops out at torch 2.9.x -- but
|
||||
never on a pip/uv install line (those must reference $TORCH_CONSTRAINT)."""
|
||||
for literal in ('"torch>=2.4,<2.11.0"', '"torch>=2.4,<2.12.0"'):
|
||||
for ln in self._sh.splitlines():
|
||||
if literal not in ln:
|
||||
continue
|
||||
assert (
|
||||
"TORCH_CONSTRAINT=" in ln
|
||||
), f"{literal} hardcoded off a TORCH_CONSTRAINT= assignment: {ln.strip()!r}"
|
||||
assert (
|
||||
"pip install" not in ln
|
||||
), f"{literal} hardcoded on a pip install line: {ln.strip()!r}"
|
||||
|
||||
def test_gfx906_reroute_caps_below_211(self):
|
||||
"""The gfx906 / MI50 reroute must keep its literal sub-2.11 cap: the
|
||||
rocm6.3 index it routes to serves no torch 2.11 wheel."""
|
||||
assert self._sh.count('TORCH_CONSTRAINT="torch>=2.4,<2.11.0"') == 1
|
||||
|
||||
def test_tightening_guarded_by_skip_torch(self):
|
||||
"""The block must check SKIP_TORCH=false."""
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ RL_PY = Path(__file__).resolve().parents[2] / "unsloth" / "models" / "rl.py"
|
|||
|
||||
|
||||
def _extract_mixed_precision_code() -> str:
|
||||
lines = RL_PY.read_text().split("\n")
|
||||
lines = RL_PY.read_text(encoding = "utf-8").split("\n")
|
||||
try:
|
||||
start = next(i for i, l in enumerate(lines) if "mixed_precision = (" in l)
|
||||
except StopIteration:
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ def test_vlm_lora_regex_respects_language_only_with_explicit_targets():
|
|||
|
||||
|
||||
def test_fast_vision_model_wraps_explicit_targets_when_layer_filters_are_used():
|
||||
source = Path("unsloth/models/vision.py").read_text()
|
||||
source = Path("unsloth/models/vision.py").read_text(encoding = "utf-8")
|
||||
|
||||
assert "target_modules = get_peft_regex(" in source
|
||||
assert "target_modules = list(target_modules)" in source
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue