Prevent installer validation from importing engine runtimes Technical details: - Walk dotted module specs without importing parent packages - Keep presence-only validation isolated from third-party startup checks - Add regression coverage for import side effects - Address issue #337
109 lines
3.6 KiB
Python
109 lines
3.6 KiB
Python
import importlib.util
|
|
import sys
|
|
import types
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parents[2]
|
|
if str(REPO_ROOT) not in sys.path:
|
|
sys.path.insert(0, str(REPO_ROOT))
|
|
|
|
INSTALL_SPEC = importlib.util.spec_from_file_location("tts_suite_install_test_module", REPO_ROOT / "install.py")
|
|
INSTALL_MODULE = importlib.util.module_from_spec(INSTALL_SPEC)
|
|
INSTALL_SPEC.loader.exec_module(INSTALL_MODULE)
|
|
|
|
from engines.dots_tts.dots_tts_engine import DotsTTSEngine
|
|
from utils.runtimes.profiles import get_runtime_profile
|
|
|
|
|
|
@pytest.mark.unit
|
|
def test_nested_module_check_does_not_import_parent(tmp_path, monkeypatch):
|
|
package = tmp_path / "explosive_parent"
|
|
package.mkdir()
|
|
(package / "__init__.py").write_text(
|
|
"raise RuntimeError('parent package must not be imported')\n",
|
|
encoding="utf-8",
|
|
)
|
|
(package / "runtime.py").write_text("", encoding="utf-8")
|
|
monkeypatch.syspath_prepend(str(tmp_path))
|
|
monkeypatch.delitem(sys.modules, "explosive_parent", raising=False)
|
|
|
|
installer = INSTALL_MODULE.TTSAudioInstaller()
|
|
|
|
assert installer.module_available("explosive_parent.runtime") is True
|
|
assert installer.module_available("explosive_parent.missing") is False
|
|
assert "explosive_parent" not in sys.modules
|
|
|
|
|
|
@pytest.mark.unit
|
|
def test_fish_source_restore_accepts_namespace_package(tmp_path, monkeypatch):
|
|
source_root = tmp_path / "source"
|
|
source_package = source_root / "fish_speech"
|
|
(source_package / "inference_engine").mkdir(parents=True)
|
|
(source_package / "inference_engine" / "__init__.py").write_text("", encoding="utf-8")
|
|
|
|
site_root = tmp_path / "site-packages"
|
|
target_package = site_root / "fish_speech"
|
|
target_package.mkdir(parents=True)
|
|
(target_package / "content_sequence.py").write_text("", encoding="utf-8")
|
|
monkeypatch.setattr(sys, "path", [str(site_root)])
|
|
|
|
installer = INSTALL_MODULE.TTSAudioInstaller()
|
|
|
|
assert installer._restore_fish_source_package(source_root) is True
|
|
assert (target_package / "inference_engine" / "__init__.py").is_file()
|
|
|
|
|
|
@pytest.mark.unit
|
|
def test_dots_fallback_overrides_incomplete_tn_module_temporarily(monkeypatch):
|
|
unrelated_tn = types.ModuleType("tn")
|
|
monkeypatch.setitem(sys.modules, "tn", unrelated_tn)
|
|
for name in (
|
|
"tn.chinese",
|
|
"tn.chinese.normalizer",
|
|
"tn.english",
|
|
"tn.english.normalizer",
|
|
):
|
|
monkeypatch.delitem(sys.modules, name, raising=False)
|
|
|
|
with DotsTTSEngine._text_normalizer_compat():
|
|
from tn.chinese.normalizer import Normalizer as ZhNormalizer
|
|
from tn.english.normalizer import Normalizer as EnNormalizer
|
|
|
|
assert ZhNormalizer().normalize("123") == "123"
|
|
assert EnNormalizer().normalize("test") == "test"
|
|
|
|
assert sys.modules["tn"] is unrelated_tn
|
|
assert "tn.chinese" not in sys.modules
|
|
assert "tn.english" not in sys.modules
|
|
|
|
|
|
@pytest.mark.unit
|
|
def test_vibevoice_runtime_avoids_webrtc_dependency_conflict():
|
|
profile = get_runtime_profile("vibevoice_transformers4_shared")
|
|
|
|
assert profile is not None
|
|
assert "av" in profile.pip_packages
|
|
assert not {
|
|
"aiortc",
|
|
"pyee",
|
|
"dnspython",
|
|
"ifaddr",
|
|
"pylibsrtp",
|
|
"pyopenssl",
|
|
}.intersection(profile.pip_packages)
|
|
|
|
|
|
@pytest.mark.unit
|
|
@pytest.mark.parametrize(
|
|
"profile_name",
|
|
("vibevoice_transformers4_shared", "qwen3_tts_transformers4_dedicated"),
|
|
)
|
|
def test_legacy_transformers_runtime_pins_compatible_kernels(profile_name):
|
|
profile = get_runtime_profile(profile_name)
|
|
|
|
assert profile is not None
|
|
assert "kernels>=0.6.1,<=0.9" in profile.pip_packages
|