Coverage for paintmaskanimator/dependency_check.py: 78%
28 statements
« prev ^ index » next coverage.py v7.17.0, created at 2026-11-02 04:50 +0000
« prev ^ index » next coverage.py v7.17.0, created at 2026-11-02 04:50 +0000
1"""Fail-friendly check for the required runtime dependencies.
3Double-clicking a ``.py`` normally closes the console immediately, so a missing
4PySide6/numpy would flash an unreadable traceback. This module turns that into a
5visible dialog with a copy-pasteable install command.
7It must run **before** anything imports Qt, so the application entry points call
8:func:`require_runtime_dependencies` as their first statement. It deliberately
9imports no Qt itself -- the whole point is that Qt may be absent.
10"""
11from __future__ import annotations
13import importlib
15from .i18n import tr
16from .constants import APP_DISPLAY_NAME
18#: Modules the application cannot start without.
19REQUIRED_MODULES = ("PySide6.QtWidgets", "numpy", "paintmaskanimator.native")
22def require_runtime_dependencies(modules: tuple[str, ...] = REQUIRED_MODULES) -> None:
23 """Exit with a readable message if a required third-party module is missing."""
24 for module in modules:
25 try:
26 importlib.import_module(module)
27 except ModuleNotFoundError as exc:
28 _report_and_exit(exc.name or module, str(exc) if module == "paintmaskanimator.native" else None)
29 except ImportError as exc:
30 if module != "paintmaskanimator.native": 30 ↛ 31line 30 didn't jump to line 31 because the condition on line 30 was never true
31 raise
32 _report_and_exit("pma_native", str(exc))
35def _report_and_exit(missing: str, detail: str | None = None) -> None:
36 message = detail or (
37 tr("{missing} がインストールされていないため起動できません。\n\nWindowsのコマンドプロンプトで次を実行してください。\n\npy -m pip install PySide6 numpy\n\nインストール後、このファイルをもう一度起動してください。").format(missing=missing)
38 )
39 print(message)
40 try:
41 import tkinter as tk
42 from tkinter import messagebox
44 root = tk.Tk()
45 root.withdraw()
46 messagebox.showerror(tr("{NAME} 起動エラー").format(NAME=APP_DISPLAY_NAME), message)
47 root.destroy()
48 except Exception:
49 # tkinter itself may be missing or unusable (no display); the console
50 # message above is the fallback, so ignore any GUI-dialog failure.
51 pass
52 raise SystemExit(1)