Coverage for src/ezcompiler/adapters/base_installer.py: 100.00%

13 statements  

« prev     ^ index     » next       coverage.py v7.16.2, created at 2026-10-16 16:37 +0000

1# /////////////////////////////////////////////////////////////// 

2# BASE_INSTALLER - Abstract base installer interface 

3# Project: ezcompiler 

4# /////////////////////////////////////////////////////////////// 

5 

6""" 

7Base installer - Abstract base class for first-deployment installer builders. 

8 

9Defines the interface (conforming to ``types.InstallerPort``) and shared 

10validation. The structural contract is the Port; this base factors common 

11behaviour. Boundaries are typed via the Port, not this base. 

12""" 

13 

14from __future__ import annotations 

15 

16# /////////////////////////////////////////////////////////////// 

17# IMPORTS 

18# /////////////////////////////////////////////////////////////// 

19from abc import ABC, abstractmethod 

20from pathlib import Path 

21 

22from ..shared import InstallerConfig 

23from ..shared.exceptions import InstallerConfigError 

24 

25# /////////////////////////////////////////////////////////////// 

26# CLASSES 

27# /////////////////////////////////////////////////////////////// 

28 

29 

30class BaseInstaller(ABC): 

31 """Abstract base class for first-deployment installer builders.""" 

32 

33 # //////////////////////////////////////////////// 

34 # INITIALIZATION 

35 # //////////////////////////////////////////////// 

36 

37 def __init__(self, config: InstallerConfig) -> None: 

38 self._config = config 

39 

40 # //////////////////////////////////////////////// 

41 # ABSTRACT METHODS 

42 # //////////////////////////////////////////////// 

43 

44 @abstractmethod 

45 def build( 

46 self, 

47 bundle_dir: Path, 

48 app_name: str, 

49 version: str, 

50 output_dir: Path, 

51 *, 

52 company_name: str = "", 

53 icon: str = "", 

54 main_file: str = "", 

55 ) -> Path: 

56 """Build the installer executable. Raises InstallerError on failure.""" 

57 

58 @abstractmethod 

59 def get_installer_name(self) -> str: 

60 """Human-readable installer backend name.""" 

61 

62 # //////////////////////////////////////////////// 

63 # VALIDATION METHODS 

64 # //////////////////////////////////////////////// 

65 

66 def _validate_bundle_dir(self, bundle_dir: Path) -> None: 

67 """Ensure the bundle directory exists and is non-empty.""" 

68 if not bundle_dir.is_dir(): 

69 raise InstallerConfigError(f"Bundle directory does not exist: {bundle_dir}") 

70 if not any(bundle_dir.iterdir()): 

71 raise InstallerConfigError(f"Bundle directory is empty: {bundle_dir}")