Adapters layer¶
Compiler and uploader adapter implementations for EzCompiler.
The adapters layer defines abstract base classes and concrete implementations for compilation and upload operations.
Internal modules
Concrete compiler/uploader implementations live in private modules
(prefixed with _) and are instantiated through the factories rather than
imported directly. Public consumers should use
CompilerFactory and UploaderFactory,
or the high-level EzCompiler facade.
Compiler adapters¶
BaseCompiler¶
Abstract base class defining the compiler interface.
BaseCompiler
¶
BaseCompiler(config: CompilerConfig)
Bases: ABC
Abstract base class for project compilers.
Defines the interface that all compiler implementations must follow. Provides common functionality for compilation operations and enforces the contract for concrete compiler classes.
Attributes:
| Name | Type | Description |
|---|---|---|
_config |
CompilerConfig instance with project settings |
|
_zip_needed |
Whether compilation output needs to be zipped |
Example
Cannot instantiate directly, must subclass¶
class MyCompiler(BaseCompiler): ... def compile(self, console=True) -> None: ... pass ... def get_compiler_name(self) -> str: ... return "MyCompiler"
Initialize the compiler with configuration.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
CompilerConfig
|
CompilerConfig instance with project settings |
required |
config
property
¶
config: CompilerConfig
Get the compiler configuration.
Returns:
| Name | Type | Description |
|---|---|---|
CompilerConfig |
CompilerConfig
|
Configuration instance with project settings |
zip_needed
property
¶
Whether the compiled project needs to be zipped.
Returns:
| Name | Type | Description |
|---|---|---|
bool |
bool
|
True if output should be zipped, False otherwise |
Note
Subclasses set this based on their output format. Cx_Freeze sets to True, PyInstaller sets to False.
compile
abstractmethod
¶
Compile the project.
This method must be implemented by all subclasses to handle the actual compilation using their respective compiler.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
console
|
bool
|
Whether to show console window (default: True) |
True
|
Raises:
| Type | Description |
|---|---|
CompilationError
|
If compilation fails |
Example
compiler = PyInstallerCompiler(config) compiler.compile(console=False)
get_compiler_name
abstractmethod
¶
Get the name of this compiler.
Returns:
| Name | Type | Description |
|---|---|---|
str |
str
|
Display name of the compiler |
Example
compiler = PyInstallerCompiler(config) name = compiler.get_compiler_name() print(name) 'PyInstaller (Empaquetée)'
CxFreezeCompiler¶
Cx_Freeze compiler implementation for directory-based builds.
CxFreezeCompiler
¶
CxFreezeCompiler(config: CompilerConfig)
Bases: BaseCompiler
Cx_Freeze compiler implementation.
Handles project compilation using Cx_Freeze, which creates a directory structure containing the executable and all dependencies. The output is typically zipped for distribution.
Compilation runs in a separate subprocess to prevent cx_Freeze output from interfering with the main process display (DLP).
Attributes:
| Name | Type | Description |
|---|---|---|
_config |
CompilerConfig with project settings |
Example
config = CompilerConfig(...) compiler = CxFreezeCompiler(config) compiler.compile(console=True)
Initialize Cx_Freeze compiler.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
CompilerConfig
|
CompilerConfig instance with project settings |
required |
Note
Cx_Freeze output requires zipping, so _zip_needed is set to True.
get_compiler_name
¶
Get the name of this compiler.
Returns:
| Name | Type | Description |
|---|---|---|
str |
str
|
Display name "Cx_Freeze" |
Example
compiler = CxFreezeCompiler(config) print(compiler.get_compiler_name()) 'Cx_Freeze'
compile
¶
Compile the project using Cx_Freeze in a subprocess.
Generates a temporary setup script and executes it in a separate process to isolate cx_Freeze stdout/stderr from the main process.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
console
|
bool
|
Whether to show console window (default: True) |
True
|
Raises:
| Type | Description |
|---|---|
CompilationError
|
If compilation fails |
Note
On Windows with console=False, uses Win32GUI base. Runs in subprocess to preserve DLP rendering in main process.
Example
config = CompilerConfig(...) compiler = CxFreezeCompiler(config) compiler.compile(console=False)
PyInstallerCompiler¶
PyInstaller compiler implementation for single-file executables.
PyInstallerCompiler
¶
PyInstallerCompiler(config: CompilerConfig)
Bases: BaseCompiler
PyInstaller compiler implementation.
Handles project compilation using PyInstaller, which creates a single executable file with all dependencies bundled. Can generate either single-file or directory-based executables.
Compilation runs in a separate subprocess to prevent PyInstaller output from interfering with the main process display (DLP).
Attributes:
| Name | Type | Description |
|---|---|---|
_config |
CompilerConfig with project settings |
Example
config = CompilerConfig(...) compiler = PyInstallerCompiler(config) compiler.compile(console=True)
Initialize PyInstaller compiler.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
CompilerConfig
|
CompilerConfig instance with project settings |
required |
Note
PyInstaller creates single files, so _zip_needed is set to False.
get_compiler_name
¶
Get the name of this compiler.
Returns:
| Name | Type | Description |
|---|---|---|
str |
str
|
Display name "PyInstaller (Empaquetée)" |
Example
compiler = PyInstallerCompiler(config) print(compiler.get_compiler_name()) 'PyInstaller (Empaquetée)'
compile
¶
Compile the project using PyInstaller in a subprocess.
Validates configuration, prepares output directory, builds PyInstaller command-line arguments, and runs compilation in a separate process to isolate stdout/stderr from the DLP.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
console
|
bool
|
Whether to show console window (default: True) |
True
|
Raises:
| Type | Description |
|---|---|
CompilationError
|
If compilation fails |
Note
Adds version file if it exists. Includes all configured packages, includes, and applies excludes. Adds files and folders with appropriate data paths.
Example
config = CompilerConfig(...) compiler = PyInstallerCompiler(config) compiler.compile(console=False)
NuitkaCompiler¶
Nuitka compiler implementation for optimized native compilation.
NuitkaCompiler
¶
NuitkaCompiler(config: CompilerConfig)
Bases: BaseCompiler
Nuitka compiler implementation.
Handles project compilation using Nuitka, which can produce a standalone folder or a single-file executable depending on options.
Attributes:
| Name | Type | Description |
|---|---|---|
_config |
CompilerConfig with project settings |
Initialize Nuitka compiler.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
CompilerConfig
|
CompilerConfig instance with project settings |
required |
get_compiler_name
¶
Get the name of this compiler.
Returns:
| Name | Type | Description |
|---|---|---|
str |
str
|
Display name "Nuitka" |
compile
¶
Compile the project using Nuitka.
Validates configuration, prepares output directory, builds Nuitka options from project settings, and runs compilation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
console
|
bool
|
Whether to show console window (default: True) |
True
|
Raises:
| Type | Description |
|---|---|
CompilationError
|
If compilation fails |
CompilerFactory¶
Factory class for creating compiler instances based on name or configuration.
CompilerFactory
¶
Factory class for creating compiler instances.
create_compiler
staticmethod
¶
create_compiler(config: CompilerConfig, compiler_name: str) -> CompilerPort
Create a compiler instance from its name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
CompilerConfig
|
Compiler configuration to inject |
required |
compiler_name
|
str
|
Compiler name (Cx_Freeze, PyInstaller, Nuitka) |
required |
Returns:
| Name | Type | Description |
|---|---|---|
CompilerPort |
CompilerPort
|
Concrete compiler instance (satisfies the Port) |
Raises:
| Type | Description |
|---|---|
CompilationError
|
If compiler name is unsupported or package not installed |
get_supported_compilers
staticmethod
¶
Return the list of supported compiler names.
Installer adapters¶
BaseInstaller¶
Abstract base class defining the first-deployment installer interface.
BaseInstaller
¶
Bases: ABC
Abstract base class for first-deployment installer builders.
InnoSetupInstaller¶
Inno Setup installer implementation, building a setup.exe via the external ISCC.exe binary.
InnoSetupInstaller
¶
Bases: BaseInstaller
Installer backed by the Inno Setup compiler (ISCC.exe).
build
¶
Render the .iss script and compile it into a setup.exe.
InstallerFactory¶
Factory class for creating installer instances based on backend name.
InstallerFactory
¶
Factory class for creating installer instances.
create_installer
staticmethod
¶
Create an installer instance for the given type.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
installer_type
|
str
|
Type of installer builder ("innosetup") |
required |
config
|
dict[str, Any] | None
|
Configuration dictionary for the installer adapter |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
InstallerPort |
InstallerPort
|
Configured installer instance (satisfies the Port) |
Raises:
| Type | Description |
|---|---|
InstallerTypeError
|
If installer type is not supported |
get_supported_types
staticmethod
¶
Return the list of supported installer backend names.
Releaser adapters¶
BaseReleaser¶
Abstract base class defining the secure-release packager interface.
BaseReleaser
¶
Bases: ABC
Abstract base class for secure-release packagers.
release
abstractmethod
¶
release(bundle_dir: Path, app_name: str, version: str, repo_dir: Path, *, patch: bool = True) -> Path
Build and sign the local TUF repository. Raises ReleaseError.
init_keys
abstractmethod
¶
Initialise clés + squelette repo TUF. Idempotent.
Returns True si init effectuée, False si déjà présente (skip). Raises ReleaseError / SigningKeyError on failure.
refresh_expiration
abstractmethod
¶
refresh_expiration(app_name: str, repo_dir: Path, keys_dir: Path, *, roles: tuple[str, ...] = ('targets', 'snapshot', 'timestamp'), days: int | None = None) -> Path
Re-sign metadata to push out expiration without a new release.
Returns the local repository directory. Raises ReleaseError / SigningKeyError on failure.
TufupReleaser¶
tufup (TUF) releaser implementation, packaging a compiled bundle into a signed TUF repository.
TufupReleaser
¶
Bases: BaseReleaser
Releaser backed by the tufup TUF framework.
release
¶
release(bundle_dir: Path, app_name: str, version: str, repo_dir: Path, *, patch: bool = True) -> Path
Build and sign the local TUF repository for the bundle.
init_keys
¶
Initialise clés + squelette repo TUF. Idempotent.
Returns True si l'init a été effectuée, False si déjà présente (skip).
refresh_expiration
¶
refresh_expiration(app_name: str, repo_dir: Path, keys_dir: Path, *, roles: tuple[str, ...] = ('targets', 'snapshot', 'timestamp'), days: int | None = None) -> Path
Re-sign metadata to push out expiration without a new release.
Native tufup keep-alive for projects updated irregularly: bumps the
expiration date of the short-lived roles (targets/snapshot/
timestamp by default) and re-publishes. root is long-lived
(365 days) and left untouched unless explicitly requested.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
app_name
|
str
|
Application name (must match the initialized repo). |
required |
repo_dir
|
Path
|
Root of the local TUF repository tree. |
required |
keys_dir
|
Path
|
Directory holding the private signing keys. |
required |
roles
|
tuple[str, ...]
|
Role names whose expiration should be refreshed. |
('targets', 'snapshot', 'timestamp')
|
days
|
int | None
|
Number of days from now. None → the repo's configured
per-role |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
Path |
Path
|
The local repository directory. |
Raises:
| Type | Description |
|---|---|
SigningKeyError
|
If the keys directory is missing. |
ReleaseError
|
If the repo is not initialized or refresh fails. |
ReleaserFactory¶
Factory class for creating releaser instances based on backend name.
ReleaserFactory
¶
Factory class for creating releaser instances.
create_releaser
staticmethod
¶
create_releaser(release_type: str, config: dict[str, Any] | None = None) -> ReleaserPort
Create a releaser instance for the given type.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
release_type
|
str
|
Type of releaser ("tufup") |
required |
config
|
dict[str, Any] | None
|
Configuration dictionary for the releaser |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
ReleaserPort |
ReleaserPort
|
Configured releaser instance (satisfies the Port) |
Raises:
| Type | Description |
|---|---|
ReleaserTypeError
|
If release type is not supported |
get_supported_types
staticmethod
¶
Return the list of supported release backend names.
File writer adapters¶
BaseFileWriter¶
Abstract base class defining the file writer interface.
BaseFileWriter
¶
Bases: ABC
Port for writing text content to a target file path.
write_text
abstractmethod
¶
Write text content to output_path.
DiskFileWriter¶
Concrete file writer implementation for local disk operations.
DiskFileWriter
¶
Bases: BaseFileWriter
Write text files to local disk using pathlib.
write_text
¶
Write text content to disk.
Uploader adapters¶
BaseUploader¶
Abstract base class defining the uploader interface.
BaseUploader
¶
Bases: ABC
Abstract base class for uploaders.
Defines the interface that all uploaders must implement and provides common functionality for upload operations and validation.
Attributes:
| Name | Type | Description |
|---|---|---|
_config |
Configuration dictionary for the uploader |
Example
class MyUploader(BaseUploader): ... def upload(self, source_path: Path, destination: str) -> None: ... # Implementation ... pass ... def get_uploader_name(self) -> str: ... return "My Uploader"
Initialize the uploader with configuration.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
dict[str, Any] | None
|
Configuration dictionary (default: None) |
None
|
Note
Subclasses should call super().init(config) to initialize configuration and validation.
upload
abstractmethod
¶
Upload a file or directory to the destination.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source_path
|
Path
|
Path to the source file or directory |
required |
destination
|
str
|
Destination path or URL |
required |
Raises:
| Type | Description |
|---|---|
UploadError
|
If upload fails |
Note
Subclasses must implement this method to define upload behavior.
get_uploader_name
abstractmethod
¶
Get the name of this uploader.
Returns:
| Name | Type | Description |
|---|---|---|
str |
str
|
Human-readable name of the uploader |
Note
Used for identification purposes.
download
¶
Download a remote tree into local_dir.
Default implementation: backends that host an update channel override this. Others reject it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
remote_source
|
str
|
Remote source (path, URL or prefix) to fetch. |
required |
local_dir
|
Path
|
Local directory to populate with the downloaded tree. |
required |
Raises:
| Type | Description |
|---|---|
UploadError
|
Always, unless overridden by a subclass. |
DiskUploader¶
Local disk uploader for saving compiled projects to the file system.
DiskUploader
¶
Bases: BaseUploader
Uploader for local disk operations.
Handles copying files and directories to local disk locations with configurable behavior for permissions, overwrites, and backups.
Configuration keys
preserve_permissions (bool): Preserve file permissions (default: True) overwrite (bool): Allow overwriting existing files (default: True) create_backup (bool): Create backup before overwrite (default: False)
Example
config = {"preserve_permissions": True, "overwrite": True} uploader = DiskUploader(config) uploader.upload(Path("source.zip"), "/path/to/destination.zip")
Initialize the disk uploader.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
dict[str, Any] | None
|
Optional configuration dictionary with keys: - preserve_permissions (bool): Preserve file permissions - overwrite (bool): Allow overwriting existing files - create_backup (bool): Create backup before overwrite |
None
|
get_uploader_name
¶
Get the name of this uploader.
Returns:
| Name | Type | Description |
|---|---|---|
str |
str
|
Name of the uploader |
upload
¶
Upload a file or directory to a local disk location.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source_path
|
Path
|
Path to the source file or directory |
required |
destination
|
str
|
Destination path on local disk |
required |
Raises:
| Type | Description |
|---|---|
UploadError
|
If upload fails |
Note
Creates parent directories automatically if they don't exist.
download
¶
Copy a remote disk tree into local_dir (recursive).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
remote_source
|
str
|
Filesystem path of the remote tree. |
required |
local_dir
|
Path
|
Local directory to populate. |
required |
Raises:
| Type | Description |
|---|---|
UploadError
|
If the copy fails. |
Note
Missing remote source is a no-op (first run: empty channel).
ServerUploader¶
HTTP/HTTPS server uploader for remote distribution.
ServerUploader
¶
Bases: BaseUploader
Uploader for server operations via HTTP/HTTPS.
Handles uploading files to remote servers using HTTP POST requests with support for authentication, SSL verification, and automatic retry logic.
Configuration keys
server_url (str): Base URL of the upload server
username (str): Username for basic authentication (default: "")
password (str): Password for basic authentication (default: "")
api_key (str): API key for bearer token authentication (default: "")
timeout (int|float): Request timeout in seconds (default: 30)
verify_ssl (bool): Verify SSL certificates (default: True)
chunk_size (int): Chunk size for uploads (default: 8192)
retry_attempts (int): Number of retry attempts (default: 3)
proxies (dict): Proxy URLs keyed by scheme, e.g.
{"http": "http://proxy:3128", "https": "http://proxy:3128"} (default: {})
extra_headers (dict): Additional HTTP headers merged into every request (default: {})
cert (str | tuple | None): Client certificate for mTLS — path to a .pem file,
or a (certfile, keyfile) tuple (default: None)
Example
config = {"server_url": "https://example.com", "api_key": "abc123"} uploader = ServerUploader(config) uploader.upload(Path("file.zip"), "/uploads/file.zip")
Initialize the server uploader.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
dict[str, Any] | None
|
Optional configuration dictionary with server settings |
None
|
get_uploader_name
¶
Get the name of this uploader.
Returns:
| Name | Type | Description |
|---|---|---|
str |
str
|
Name of the uploader |
upload
¶
Upload a file or a directory tree to a remote server.
For directories, walks recursively and POSTs each file using its
POSIX path relative to source_path as the server destination.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source_path
|
Path
|
Path to the source file or directory |
required |
destination
|
str
|
Destination path on the server (single-file uploads) |
required |
Raises:
| Type | Description |
|---|---|
UploadError
|
If any file fails after all retry attempts. |
Note
Automatically retries on failure based on retry_attempts config.
download
¶
Fetch the current TUF tree from remote_source (metadata-driven).
Reads the role metadata by known names, then the targets listed in
targets.json. A 404 on timestamp.json means the channel is
empty (first run) -> no-op.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
remote_source
|
str
|
Base read URL of the update channel. |
required |
local_dir
|
Path
|
Local directory to populate with the TUF tree. |
required |
Raises:
| Type | Description |
|---|---|
UploadError
|
On any non-404 transport failure. |
Factory¶
UploaderFactory¶
Factory class for creating uploader instances based on configuration.
UploaderFactory
¶
Factory class for creating uploader instances.
Provides a centralized way to create and configure uploader instances based on type specification, with support for validation and type discovery.
Example
uploader = UploaderFactory.create_uploader("disk", {"overwrite": True}) types = UploaderFactory.get_supported_types() print(types) ['disk', 'server']
create_uploader
staticmethod
¶
create_uploader(upload_type: str, config: dict[str, Any] | None = None) -> UploaderPort
Create an uploader instance based on the specified type.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
upload_type
|
str
|
Type of uploader ("disk" or "server") |
required |
config
|
dict[str, Any] | None
|
Configuration dictionary for the uploader (default: None) |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
UploaderPort |
UploaderPort
|
Configured uploader instance (satisfies the Port) |
Raises:
| Type | Description |
|---|---|
UploadError
|
If upload type is not supported |
Example
disk_uploader = UploaderFactory.create_uploader("disk") server_uploader = UploaderFactory.create_uploader( ... "server", {"server_url": "https://example.com"} ... )
create_from_config
staticmethod
¶
create_from_config(config: dict[str, Any]) -> UploaderPort
Create an uploader instance from a configuration dictionary.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
dict[str, Any]
|
Configuration dictionary containing: - type: Upload type ("disk" or "server") - config: Uploader-specific configuration (optional) |
required |
Returns:
| Name | Type | Description |
|---|---|---|
UploaderPort |
UploaderPort
|
Configured uploader instance (satisfies the Port) |
Raises:
| Type | Description |
|---|---|
UploadError
|
If configuration is invalid or type is missing |
Example
config = { ... "type": "disk", ... "config": {"overwrite": True} ... } uploader = UploaderFactory.create_from_config(config)
get_supported_types
staticmethod
¶
Get list of supported upload types.
Returns:
| Type | Description |
|---|---|
list[str]
|
list[str]: List of supported upload type names |
Example
types = UploaderFactory.get_supported_types() print(types) ['disk', 'server', 'r2']
validate_config
staticmethod
¶
Validate configuration for a specific upload type.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
upload_type
|
str
|
Type of uploader to validate |
required |
config
|
dict[str, Any] | None
|
Configuration to validate (default: None) |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
bool |
bool
|
True if configuration is valid, False otherwise |
Note
Creates a temporary uploader instance to test configuration validity.
Example
is_valid = UploaderFactory.validate_config("disk", {"overwrite": True}) print(is_valid) True