diff --git a/scripts/local_setup.py b/scripts/local_setup.py index 0efa90c53..50de341f7 100644 --- a/scripts/local_setup.py +++ b/scripts/local_setup.py @@ -2990,6 +2990,7 @@ def _startup_platform_name() -> str: def _runtime_python_candidates( runtime_python: Optional[Path], venv_dir: Optional[Path] ) -> list[Path]: + """按优先级列出启动解释器,并保留 venv 的 Python 启动器软链接。""" candidates: list[Path] = [] seen: set[str] = set() @@ -3001,16 +3002,17 @@ def _runtime_python_candidates( for candidate in raw_candidates: if not candidate: continue - resolved = Path(candidate).expanduser().resolve() - key = str(resolved) + absolute = Path(candidate).expanduser().absolute() + key = str(absolute) if key in seen: continue seen.add(key) - candidates.append(resolved) + candidates.append(absolute) return candidates def _can_run_moviepilot_cli(python_bin: Path) -> bool: + """通过 CLI 帮助命令确认解释器可加载项目代码及其运行依赖。""" if not python_bin.exists(): return False @@ -3027,6 +3029,7 @@ def _can_run_moviepilot_cli(python_bin: Path) -> bool: def _resolve_runtime_python_for_startup( runtime_python: Optional[Path], venv_dir: Optional[Path] ) -> Path: + """选择可启动 MoviePilot 的运行环境,否则给出安装依赖提示。""" for candidate in _runtime_python_candidates(runtime_python, venv_dir): if _can_run_moviepilot_cli(candidate): return candidate diff --git a/tests/test_local_setup_installer_wizard.py b/tests/test_local_setup_installer_wizard.py index ed1ef282f..d59dc4ee8 100644 --- a/tests/test_local_setup_installer_wizard.py +++ b/tests/test_local_setup_installer_wizard.py @@ -43,6 +43,30 @@ def test_llm_provider_module_loads_when_repository_root_is_not_on_import_path( monkeypatch.delitem(module.sys.modules, provider_module.__name__) +def test_startup_python_resolution_preserves_virtualenv_symlink( + monkeypatch, tmp_path: Path +): + """Autostart must invoke the venv launcher instead of its base interpreter.""" + module = load_local_setup_module() + venv_dir = tmp_path / "venv" + python_launcher = module.get_venv_python(venv_dir) + python_launcher.parent.mkdir(parents=True) + python_launcher.symlink_to(Path(sys.executable)) + expected_path = python_launcher.absolute() + monkeypatch.setattr( + module, + "_can_run_moviepilot_cli", + lambda candidate: candidate == expected_path, + ) + + runtime_python = module._resolve_runtime_python_for_startup( + python_launcher, venv_dir + ) + + assert runtime_python == expected_path + assert runtime_python.is_symlink() + + def test_directory_config_keeps_download_path_as_downloader_path(monkeypatch): """The wizard must preserve downloader paths without resolving them locally.""" module = load_local_setup_module()