mirror of
https://github.com/unslothai/unsloth.git
synced 2026-10-21 14:00:32 +00:00
Comment and docstring changes only, in test files, split out of #12968 so the test-only part can merge first.
140 lines
5.4 KiB
Python
140 lines
5.4 KiB
Python
# Copyright 2023-present Daniel Han-Chen & the Unsloth team. All rights reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
"""An empty PYTHONPATH component means the cwd, so keep it; a set-but-empty one must stay empty."""
|
|
|
|
import importlib.util
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
sys.path.insert(0, str(ROOT))
|
|
|
|
from unsloth import import_fixes as IF # noqa: E402
|
|
|
|
|
|
def _stage(monkeypatch, tmp_path, pythonpath):
|
|
"""Force the gate open, since it returns early on a healthy torch/torchao pair and nothing would run."""
|
|
if importlib.util.find_spec("torchao") is None:
|
|
pytest.skip("no torchao here; the function returns before PYTHONPATH")
|
|
monkeypatch.setattr(
|
|
IF, "importlib_version", lambda name: "0.18.0" if name == "torchao" else "0"
|
|
)
|
|
monkeypatch.setattr(IF, "_torch_really_has", lambda F, name: False)
|
|
monkeypatch.setattr("tempfile.gettempdir", lambda: str(tmp_path))
|
|
# monkeypatch restores PYTHONPATH at teardown even though the function writes os.environ.
|
|
if pythonpath is None:
|
|
monkeypatch.delenv("PYTHONPATH", raising = False)
|
|
else:
|
|
monkeypatch.setenv("PYTHONPATH", pythonpath)
|
|
|
|
directory = IF.propagate_torchao_fix_to_subprocesses()
|
|
assert directory is not None, "gate did not fire; the test proves nothing"
|
|
assert os.pathsep not in directory, directory
|
|
return directory, os.environ.get("PYTHONPATH", "")
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"before",
|
|
[
|
|
os.pathsep + "/opt/lib",
|
|
"/opt/lib" + os.pathsep,
|
|
"/opt/a" + os.pathsep + os.pathsep + "/opt/b",
|
|
os.pathsep,
|
|
],
|
|
)
|
|
def test_empty_components_survive(monkeypatch, tmp_path, before):
|
|
directory, after = _stage(monkeypatch, tmp_path, before)
|
|
assert after == directory + os.pathsep + before, after
|
|
|
|
|
|
@pytest.mark.parametrize("before", [None, ""])
|
|
def test_an_absent_or_empty_pythonpath_does_not_gain_the_cwd(monkeypatch, tmp_path, before):
|
|
"""CPython ignores a set-but-empty PYTHONPATH, so "" must not be split
|
|
into a lone "" component."""
|
|
directory, after = _stage(monkeypatch, tmp_path, before)
|
|
assert after == directory, after
|
|
|
|
|
|
def test_it_is_still_idempotent(monkeypatch, tmp_path):
|
|
"""Two calls must not stack the directory, nor eat the empty components
|
|
the first preserved."""
|
|
before = os.pathsep + "/opt/lib"
|
|
directory, after = _stage(monkeypatch, tmp_path, before)
|
|
assert IF.propagate_torchao_fix_to_subprocesses() == directory
|
|
assert os.environ["PYTHONPATH"] == after
|
|
|
|
|
|
def _probe_tree(tmp_path):
|
|
"""Child's sys.path[0] is scriptdir, so only_in_cwd imports only if PYTHONPATH carries the cwd."""
|
|
cwddir = tmp_path / "cwddir"
|
|
libdir = tmp_path / "libdir"
|
|
scriptdir = tmp_path / "scriptdir"
|
|
for d in (cwddir, libdir, scriptdir):
|
|
d.mkdir(exist_ok = True)
|
|
(cwddir / "only_in_cwd.py").write_text("MARKER = 1\n", encoding = "utf-8")
|
|
(scriptdir / "probe.py").write_text(
|
|
"import importlib.util as u\nprint(u.find_spec('only_in_cwd') is not None)\n",
|
|
encoding = "utf-8",
|
|
)
|
|
return cwddir, libdir, scriptdir
|
|
|
|
|
|
def _cwd_is_importable(cwddir, scriptdir, pythonpath):
|
|
env = {k: v for k, v in os.environ.items() if k != "PYTHONPATH"}
|
|
if pythonpath is not None:
|
|
env["PYTHONPATH"] = pythonpath
|
|
out = subprocess.run(
|
|
[sys.executable, str(scriptdir / "probe.py")],
|
|
cwd = str(cwddir),
|
|
env = env,
|
|
capture_output = True,
|
|
text = True,
|
|
timeout = 300,
|
|
)
|
|
assert out.returncode == 0, out.stderr
|
|
return out.stdout.strip() == "True"
|
|
|
|
|
|
def test_the_cwd_really_is_importable_from_an_empty_component(tmp_path):
|
|
"""The premise, against this interpreter, with both controls."""
|
|
cwddir, libdir, scriptdir = _probe_tree(tmp_path)
|
|
assert _cwd_is_importable(cwddir, scriptdir, None) is False
|
|
assert _cwd_is_importable(cwddir, scriptdir, str(libdir)) is False
|
|
assert _cwd_is_importable(cwddir, scriptdir, os.pathsep + str(libdir)) is True
|
|
|
|
|
|
def test_the_rewritten_pythonpath_still_reaches_the_cwd(monkeypatch, tmp_path):
|
|
"""The consequence: what a descendant process actually sees afterwards."""
|
|
cwddir, libdir, scriptdir = _probe_tree(tmp_path)
|
|
before = os.pathsep + str(libdir)
|
|
assert _cwd_is_importable(cwddir, scriptdir, before) is True
|
|
_directory, after = _stage(monkeypatch, tmp_path, before)
|
|
assert _cwd_is_importable(cwddir, scriptdir, after) is True, after
|
|
|
|
|
|
def test_a_set_but_empty_pythonpath_still_reaches_nothing_new(monkeypatch, tmp_path):
|
|
"""And the mirror image: no cwd before, no cwd after."""
|
|
cwddir, _libdir, scriptdir = _probe_tree(tmp_path)
|
|
assert _cwd_is_importable(cwddir, scriptdir, "") is False
|
|
_directory, after = _stage(monkeypatch, tmp_path, "")
|
|
assert _cwd_is_importable(cwddir, scriptdir, after) is False, after
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(pytest.main([__file__, "-q"]))
|