from __future__ import annotations import os import sys from pathlib import Path from typing import Optional def is_frozen() -> bool: return getattr(sys, "frozen", False) def _find_app_bundle_root(exe_path: Path) -> Optional[Path]: for parent in exe_path.parents: if parent.suffix.lower() == ".app": return parent return None def _pick_runtime_root(exe_path: Path) -> Path: candidates: list[Path] = [] app_root = _find_app_bundle_root(exe_path) if app_root is not None: candidates.append(app_root.parent) candidates.append(exe_path.parent) for candidate in candidates: if (candidate / "conf" / "factory_private.json.enc").exists(): return candidate if (candidate / "conf").exists() or (candidate / "bin").exists() or (candidate / "keys").exists(): return candidate return exe_path.parent def venv_python(base_dir: Path) -> Path: scripts = "Scripts" if os.name == "nt" else "bin" exe = "python.exe" if os.name == "nt" else "python3" return base_dir / ".venv" / scripts / exe def resolve_python(base_dir: Path) -> Path: venv_py = venv_python(base_dir) if venv_py.exists(): return venv_py raise FileNotFoundError( "未找到当前目录的虚拟环境:\n" f"{venv_py}\n" "请先创建并安装依赖:\n" f" python3 -m venv {base_dir / '.venv'}\n" f" {venv_py} -m pip install -r {base_dir / 'requirements.txt'}" ) def get_base_dir() -> Path: if is_frozen(): return _pick_runtime_root(Path(sys.executable).resolve()) return Path(__file__).resolve().parent.parent def resolve_bin_dir(base_dir: Path, configured: Path) -> Path: if configured.is_absolute(): return configured return base_dir / configured def select_bin_dir(base_dir: Path, configured: Path) -> Path: return resolve_bin_dir(base_dir, configured) def _display_path(base_dir: Path, path: Path) -> str: try: rel = path.relative_to(base_dir) return str(rel) except ValueError: return path.name