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
« 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# ///////////////////////////////////////////////////////////////
6"""Unified initialization service used by both Python API and CLI."""
8from __future__ import annotations
10# ///////////////////////////////////////////////////////////////
11# IMPORTS
12# ///////////////////////////////////////////////////////////////
13# Standard library imports
14from pathlib import Path
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
25# ///////////////////////////////////////////////////////////////
26# CLASSES
27# ///////////////////////////////////////////////////////////////
28class InitService:
29 """Coordinates initialization with a single options object."""
31 def __init__(self) -> None:
32 self._initialized = False
34 def run(self, options: InitOptions | None = None) -> InitResult:
35 """Run initialization sequence with normalized options."""
36 resolved = (options or InitOptions()).resolve()
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 )
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 )
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()
90 if summary.success:
91 self._initialized = True
93 return summary
95 def is_initialized(self) -> bool:
96 """Return ``True`` if initialization has completed successfully."""
97 return self._initialized
99 def reset(self) -> None:
100 """Reset initialization state."""
101 self._initialized = False