Coverage for src / ezqt_app / services / bootstrap / init_service.py: 35.14%

31 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-03-26 07:07 +0000

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

2# SERVICES.BOOTSTRAP.INIT_SERVICE - Unified init orchestrator 

3# Project: ezqt_app 

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

5 

6"""Unified initialization service used by both Python API and CLI.""" 

7 

8from __future__ import annotations 

9 

10# /////////////////////////////////////////////////////////////// 

11# IMPORTS 

12# /////////////////////////////////////////////////////////////// 

13# Standard library imports 

14from pathlib import Path 

15 

16# Local imports 

17from ...domain.errors import InitAlreadyInitializedError 

18from ...domain.results import InitResult 

19from ...domain.results.result_error import ResultError 

20from ..application.file_service import FileService 

21from .contracts.options import InitOptions 

22from .sequence import InitializationSequence 

23 

24 

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

26# CLASSES 

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

28class InitService: 

29 """Coordinates initialization with a single options object.""" 

30 

31 def __init__(self) -> None: 

32 self._initialized = False 

33 

34 def run(self, options: InitOptions | None = None) -> InitResult: 

35 """Run initialization sequence with normalized options.""" 

36 resolved = (options or InitOptions()).resolve() 

37 

38 if self._initialized: 

39 err = InitAlreadyInitializedError( 

40 code="bootstrap.already_initialized", 

41 message="Initialization already completed", 

42 context={ 

43 "project_root": ( 

44 str(resolved.project_root) if resolved.project_root else None 

45 ), 

46 "bin_path": str(resolved.bin_path) if resolved.bin_path else None, 

47 }, 

48 ) 

49 return InitResult( 

50 success=True, 

51 message="Already initialized", 

52 error=ResultError( 

53 code=err.code, message=err.message, context=err.context 

54 ), 

55 ) 

56 

57 try: 

58 sequence = InitializationSequence(resolved) 

59 summary = sequence.execute(verbose=resolved.verbose) 

60 except Exception as e: 

61 return InitResult( 

62 success=False, 

63 message="Initialization failed before sequence completion", 

64 error=ResultError( 

65 code="bootstrap.unexpected_error", 

66 message=str(e), 

67 context={ 

68 "project_root": ( 

69 str(resolved.project_root) 

70 if resolved.project_root 

71 else None 

72 ), 

73 "bin_path": ( 

74 str(resolved.bin_path) if resolved.bin_path else None 

75 ), 

76 }, 

77 ), 

78 ) 

79 

80 # CLI-only optional step: generate main.py from package template. 

81 if summary.success and resolved.generate_main and resolved.project_root: 

82 maker = FileService( 

83 base_path=Path(resolved.project_root), 

84 bin_path=resolved.bin_path, 

85 verbose=resolved.verbose, 

86 overwrite_policy=resolved.overwrite_policy.value, 

87 ) 

88 maker.make_main_from_template() 

89 

90 if summary.success: 

91 self._initialized = True 

92 

93 return summary 

94 

95 def is_initialized(self) -> bool: 

96 """Return ``True`` if initialization has completed successfully.""" 

97 return self._initialized 

98 

99 def reset(self) -> None: 

100 """Reset initialization state.""" 

101 self._initialized = False