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

1"""Fail-friendly check for the required runtime dependencies. 

2 

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. 

6 

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 

12 

13import importlib 

14 

15from .i18n import tr 

16from .constants import APP_DISPLAY_NAME 

17 

18#: Modules the application cannot start without. 

19REQUIRED_MODULES = ("PySide6.QtWidgets", "numpy", "paintmaskanimator.native") 

20 

21 

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)) 

33 

34 

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 

43 

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)