Coverage for src/ezcompiler/_types.py: 90.91%

20 statements  

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

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

2# TYPES - Type Aliases Module 

3# Project: ezcompiler 

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

5 

6""" 

7Type aliases module for ezcompiler. 

8 

9This module centralizes common type aliases used throughout the library 

10to improve code readability, maintainability, and type safety. 

11 

12Example: 

13 >>> from ezcompiler._types import IncludeFiles, CompilerName 

14 >>> 

15 >>> def build(compiler: CompilerName, files: IncludeFiles) -> None: 

16 ... pass 

17""" 

18 

19from __future__ import annotations 

20 

21# /////////////////////////////////////////////////////////////// 

22# IMPORTS 

23# /////////////////////////////////////////////////////////////// 

24# Standard library imports 

25from pathlib import Path 

26from typing import TYPE_CHECKING, Literal, Protocol, runtime_checkable 

27 

28if TYPE_CHECKING: 28 ↛ 29line 28 didn't jump to line 29 because the condition on line 28 was never true

29 from .shared import CompilerConfig 

30 

31# /////////////////////////////////////////////////////////////// 

32# TYPE ALIASES 

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

34 

35# ------------------------------------------------ 

36# Path Types 

37# ------------------------------------------------ 

38 

39type FilePath = str | Path 

40"""Type alias for file path inputs. 

41 

42Accepts: 

43 - str: String file path 

44 - Path: pathlib.Path object 

45 

46Used by: CompilerConfig, template loaders, file utilities. 

47""" 

48 

49# ------------------------------------------------ 

50# Compiler Types 

51# ------------------------------------------------ 

52 

53type CompilerName = str 

54"""Type alias for compiler name selection. 

55 

56Valid values: "" (unset -> prompt), "Cx_Freeze", "PyInstaller", "Nuitka" 

57 

58Used by: CompilerConfig.compiler, EzCompiler.compile_project(). 

59""" 

60 

61type RepoDestination = Literal["disk", "server", "r2"] 

62"""Type alias for the TUF repository upload backend. 

63 

64Valid values: "disk", "server", "r2" 

65 

66Used by: CompilerConfig.repo_destination, `ezcompiler publish update`. 

67""" 

68 

69type ReleaseDestination = Literal["disk", "server", "r2", "github", "gitlab"] 

70"""Type alias for the release asset destination or publication platform. 

71 

72Valid values: "disk", "server", "r2", "github", "gitlab" 

73 

74Used by: CompilerConfig.release_destination, `ezcompiler publish release`. 

75""" 

76 

77type ReleaseTarget = Literal["tufup"] 

78"""Type alias for the secure-release backend selection. 

79 

80Valid values: "tufup" 

81 

82Used by: CompilerConfig.release_type, ReleaserFactory. 

83""" 

84 

85# ------------------------------------------------ 

86# Configuration Types 

87# ------------------------------------------------ 

88 

89type IncludeFiles = dict[str, list[str]] 

90"""Type alias for the include_files configuration structure. 

91 

92Expected shape:: 

93 

94 { 

95 "files": ["path/to/file1.txt", "path/to/file2.dll"], 

96 "folders": ["path/to/assets/", "path/to/data/"], 

97 } 

98 

99Used by: CompilerConfig.include_files. 

100""" 

101 

102type JsonMap = dict[str, object] 

103"""Type alias for a generic JSON-serializable mapping. 

104 

105Used by: configuration parsers, template renderers, YAML/JSON loaders. 

106""" 

107 

108# /////////////////////////////////////////////////////////////// 

109# PORTS (structural contracts — Hexagonal architecture) 

110# /////////////////////////////////////////////////////////////// 

111 

112 

113@runtime_checkable 

114class CompilerPort(Protocol): 

115 """Structural contract for a project compiler (Port). 

116 

117 Any object exposing this surface is a valid compiler — no inheritance 

118 required. ``adapters.BaseCompiler`` and its subclasses conform to it. 

119 

120 Used by: CompilerFactory return type, CompilationResult.compiler_instance. 

121 """ 

122 

123 @property 

124 def config(self) -> CompilerConfig: 

125 """Configuration the compiler was built with.""" 

126 ... 

127 

128 @property 

129 def zip_needed(self) -> bool: 

130 """Whether the compiled output must be zipped.""" 

131 ... 

132 

133 def compile(self, console: bool = True) -> None: 

134 """Compile the project. Raises CompilationError on failure.""" 

135 ... 

136 

137 def get_compiler_name(self) -> str: 

138 """Human-readable compiler name.""" 

139 ... 

140 

141 

142@runtime_checkable 

143class UploaderPort(Protocol): 

144 """Structural contract for an artifact uploader (Port). 

145 

146 Any object exposing this surface is a valid uploader — no inheritance 

147 required. ``adapters.BaseUploader`` and its subclasses conform to it. 

148 

149 Used by: UploaderFactory return type, UploaderService boundaries. 

150 """ 

151 

152 def upload(self, source_path: Path, destination: str) -> None: 

153 """Upload a file or directory. Raises UploadError on failure.""" 

154 ... 

155 

156 def download(self, remote_source: str, local_dir: Path) -> None: 

157 """Download a remote tree into ``local_dir``. Raises UploadError.""" 

158 ... 

159 

160 def get_uploader_name(self) -> str: 

161 """Human-readable uploader name.""" 

162 ... 

163 

164 

165@runtime_checkable 

166class ReleaserPort(Protocol): 

167 """Structural contract for a secure-release packager (Port). 

168 

169 Any object exposing this surface is a valid releaser — no inheritance 

170 required. ``adapters.BaseReleaser`` and its subclasses conform to it. 

171 

172 Used by: ReleaserFactory return type, ReleaseService boundaries. 

173 """ 

174 

175 def release( 

176 self, 

177 bundle_dir: Path, 

178 app_name: str, 

179 version: str, 

180 repo_dir: Path, 

181 *, 

182 patch: bool = True, 

183 required: bool = False, 

184 ) -> Path: 

185 """Build and sign the local TUF repository for ``bundle_dir``. 

186 

187 ``required`` marks the version as mandatory for clients (tufup ``required``). 

188 

189 Returns the path to the produced ``repository/`` tree. 

190 Raises ReleaseError on failure. 

191 """ 

192 ... 

193 

194 def init_keys(self, app_name: str, repo_dir: Path, keys_dir: Path) -> bool: 

195 """Initialize TUF keys and the repo skeleton. Idempotent. 

196 

197 Returns True if the init ran, False if the keys were already present (skip). 

198 Raises ReleaseError / SigningKeyError on failure. 

199 """ 

200 ... 

201 

202 def refresh_expiration( 

203 self, 

204 app_name: str, 

205 repo_dir: Path, 

206 keys_dir: Path, 

207 *, 

208 roles: tuple[str, ...] = ..., 

209 days: int | None = None, 

210 ) -> Path: 

211 """Re-sign metadata to push out expiration without a new release. 

212 

213 Returns the local repository directory. 

214 Raises ReleaseError / SigningKeyError on failure. 

215 """ 

216 ... 

217 

218 def remove_latest(self, app_name: str, repo_dir: Path, keys_dir: Path) -> str: 

219 """Remove the latest archive (and its patch), then re-sign. 

220 

221 Returns the removed version as spelled in the archive name. 

222 Raises ReleaseError / SigningKeyError on failure. 

223 """ 

224 ... 

225 

226 def get_releaser_name(self) -> str: 

227 """Human-readable releaser name.""" 

228 ... 

229 

230 

231@runtime_checkable 

232class PublisherPort(Protocol): 

233 """Structural contract for an addressable release publisher (Port).""" 

234 

235 def preflight(self) -> None: 

236 """Raise unless the platform CLI is installed and authenticated.""" 

237 ... 

238 

239 def exists(self, tag: str) -> bool: 

240 """Whether a release already exists for ``tag``.""" 

241 ... 

242 

243 def publish( 

244 self, 

245 assets: list[Path], 

246 *, 

247 tag: str, 

248 title: str, 

249 notes: str | None = None, 

250 prerelease: bool = False, 

251 draft: bool = False, 

252 ) -> str: 

253 """Create a release with ``assets`` and return its URL.""" 

254 ... 

255 

256 def get_publisher_name(self) -> str: 

257 """Human-readable publisher name.""" 

258 ... 

259 

260 

261@runtime_checkable 

262class InstallerPort(Protocol): 

263 """Structural contract for a first-deployment installer builder (Port). 

264 

265 Any object exposing this surface is a valid installer builder — no 

266 inheritance required. ``adapters.BaseInstaller`` and its subclasses 

267 conform to it. 

268 

269 An implementation is constructed with an ``InstallerConfig`` (see 

270 ``adapters.BaseInstaller.__init__``); the config is not part of this 

271 structural contract since only ``build``/``get_installer_name`` are 

272 called through the Port. 

273 

274 Used by: InstallerFactory return type, InstallerService boundaries. 

275 """ 

276 

277 def build( 

278 self, 

279 bundle_dir: Path, 

280 app_name: str, 

281 version: str, 

282 output_dir: Path, 

283 *, 

284 company_name: str = "", 

285 icon: str = "", 

286 main_file: str = "", 

287 ) -> Path: 

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

289 ... 

290 

291 def get_installer_name(self) -> str: 

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

293 ... 

294 

295 

296# /////////////////////////////////////////////////////////////// 

297# PUBLIC API 

298# /////////////////////////////////////////////////////////////// 

299 

300__all__ = [ 

301 "FilePath", 

302 "CompilerName", 

303 "RepoDestination", 

304 "ReleaseDestination", 

305 "ReleaseTarget", 

306 "IncludeFiles", 

307 "JsonMap", 

308 "CompilerPort", 

309 "UploaderPort", 

310 "ReleaserPort", 

311 "PublisherPort", 

312 "InstallerPort", 

313]