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
« 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# ///////////////////////////////////////////////////////////////
6"""
7Type aliases module for ezcompiler.
9This module centralizes common type aliases used throughout the library
10to improve code readability, maintainability, and type safety.
12Example:
13 >>> from ezcompiler._types import IncludeFiles, CompilerName
14 >>>
15 >>> def build(compiler: CompilerName, files: IncludeFiles) -> None:
16 ... pass
17"""
19from __future__ import annotations
21# ///////////////////////////////////////////////////////////////
22# IMPORTS
23# ///////////////////////////////////////////////////////////////
24# Standard library imports
25from pathlib import Path
26from typing import TYPE_CHECKING, Literal, Protocol, runtime_checkable
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
31# ///////////////////////////////////////////////////////////////
32# TYPE ALIASES
33# ///////////////////////////////////////////////////////////////
35# ------------------------------------------------
36# Path Types
37# ------------------------------------------------
39type FilePath = str | Path
40"""Type alias for file path inputs.
42Accepts:
43 - str: String file path
44 - Path: pathlib.Path object
46Used by: CompilerConfig, template loaders, file utilities.
47"""
49# ------------------------------------------------
50# Compiler Types
51# ------------------------------------------------
53type CompilerName = str
54"""Type alias for compiler name selection.
56Valid values: "" (unset -> prompt), "Cx_Freeze", "PyInstaller", "Nuitka"
58Used by: CompilerConfig.compiler, EzCompiler.compile_project().
59"""
61type RepoDestination = Literal["disk", "server", "r2"]
62"""Type alias for the TUF repository upload backend.
64Valid values: "disk", "server", "r2"
66Used by: CompilerConfig.repo_destination, `ezcompiler publish update`.
67"""
69type ReleaseDestination = Literal["disk", "server", "r2", "github", "gitlab"]
70"""Type alias for the release asset destination or publication platform.
72Valid values: "disk", "server", "r2", "github", "gitlab"
74Used by: CompilerConfig.release_destination, `ezcompiler publish release`.
75"""
77type ReleaseTarget = Literal["tufup"]
78"""Type alias for the secure-release backend selection.
80Valid values: "tufup"
82Used by: CompilerConfig.release_type, ReleaserFactory.
83"""
85# ------------------------------------------------
86# Configuration Types
87# ------------------------------------------------
89type IncludeFiles = dict[str, list[str]]
90"""Type alias for the include_files configuration structure.
92Expected shape::
94 {
95 "files": ["path/to/file1.txt", "path/to/file2.dll"],
96 "folders": ["path/to/assets/", "path/to/data/"],
97 }
99Used by: CompilerConfig.include_files.
100"""
102type JsonMap = dict[str, object]
103"""Type alias for a generic JSON-serializable mapping.
105Used by: configuration parsers, template renderers, YAML/JSON loaders.
106"""
108# ///////////////////////////////////////////////////////////////
109# PORTS (structural contracts — Hexagonal architecture)
110# ///////////////////////////////////////////////////////////////
113@runtime_checkable
114class CompilerPort(Protocol):
115 """Structural contract for a project compiler (Port).
117 Any object exposing this surface is a valid compiler — no inheritance
118 required. ``adapters.BaseCompiler`` and its subclasses conform to it.
120 Used by: CompilerFactory return type, CompilationResult.compiler_instance.
121 """
123 @property
124 def config(self) -> CompilerConfig:
125 """Configuration the compiler was built with."""
126 ...
128 @property
129 def zip_needed(self) -> bool:
130 """Whether the compiled output must be zipped."""
131 ...
133 def compile(self, console: bool = True) -> None:
134 """Compile the project. Raises CompilationError on failure."""
135 ...
137 def get_compiler_name(self) -> str:
138 """Human-readable compiler name."""
139 ...
142@runtime_checkable
143class UploaderPort(Protocol):
144 """Structural contract for an artifact uploader (Port).
146 Any object exposing this surface is a valid uploader — no inheritance
147 required. ``adapters.BaseUploader`` and its subclasses conform to it.
149 Used by: UploaderFactory return type, UploaderService boundaries.
150 """
152 def upload(self, source_path: Path, destination: str) -> None:
153 """Upload a file or directory. Raises UploadError on failure."""
154 ...
156 def download(self, remote_source: str, local_dir: Path) -> None:
157 """Download a remote tree into ``local_dir``. Raises UploadError."""
158 ...
160 def get_uploader_name(self) -> str:
161 """Human-readable uploader name."""
162 ...
165@runtime_checkable
166class ReleaserPort(Protocol):
167 """Structural contract for a secure-release packager (Port).
169 Any object exposing this surface is a valid releaser — no inheritance
170 required. ``adapters.BaseReleaser`` and its subclasses conform to it.
172 Used by: ReleaserFactory return type, ReleaseService boundaries.
173 """
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``.
187 ``required`` marks the version as mandatory for clients (tufup ``required``).
189 Returns the path to the produced ``repository/`` tree.
190 Raises ReleaseError on failure.
191 """
192 ...
194 def init_keys(self, app_name: str, repo_dir: Path, keys_dir: Path) -> bool:
195 """Initialize TUF keys and the repo skeleton. Idempotent.
197 Returns True if the init ran, False if the keys were already present (skip).
198 Raises ReleaseError / SigningKeyError on failure.
199 """
200 ...
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.
213 Returns the local repository directory.
214 Raises ReleaseError / SigningKeyError on failure.
215 """
216 ...
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.
221 Returns the removed version as spelled in the archive name.
222 Raises ReleaseError / SigningKeyError on failure.
223 """
224 ...
226 def get_releaser_name(self) -> str:
227 """Human-readable releaser name."""
228 ...
231@runtime_checkable
232class PublisherPort(Protocol):
233 """Structural contract for an addressable release publisher (Port)."""
235 def preflight(self) -> None:
236 """Raise unless the platform CLI is installed and authenticated."""
237 ...
239 def exists(self, tag: str) -> bool:
240 """Whether a release already exists for ``tag``."""
241 ...
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 ...
256 def get_publisher_name(self) -> str:
257 """Human-readable publisher name."""
258 ...
261@runtime_checkable
262class InstallerPort(Protocol):
263 """Structural contract for a first-deployment installer builder (Port).
265 Any object exposing this surface is a valid installer builder — no
266 inheritance required. ``adapters.BaseInstaller`` and its subclasses
267 conform to it.
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.
274 Used by: InstallerFactory return type, InstallerService boundaries.
275 """
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 ...
291 def get_installer_name(self) -> str:
292 """Human-readable installer backend name."""
293 ...
296# ///////////////////////////////////////////////////////////////
297# PUBLIC API
298# ///////////////////////////////////////////////////////////////
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]