Skip to content

Services layer

Business logic services and orchestration for EzCompiler.

The services layer implements the core business logic, handling compilation, configuration, template processing, and upload operations.


CompilerService

Service for managing compiler selection and orchestrating the compilation process.

CompilerService

CompilerService(config: CompilerConfig)

Compilation orchestration service.

Orchestrates project compilation using different compiler backends. Handles compiler selection, validation, and execution.

Attributes:

Name Type Description
_config

CompilerConfig instance with project settings

Example

config = CompilerConfig(...) service = CompilerService(config) result = service.compile(console=True, compiler="PyInstaller") print(result.zip_needed) False

Initialize the compiler service.

Parameters:

Name Type Description Default
config CompilerConfig

CompilerConfig instance with project settings

required

Raises:

Type Description
ConfigurationError

If config is None or invalid

compiler_instance property

compiler_instance: CompilerPort | None

Get the current compiler instance.

Returns:

Type Description
CompilerPort | None

CompilerPort | None: Current compiler instance or None if not compiled yet

compile

compile(console: bool = True, compiler: _CompilerName | None = None) -> CompilationResult

Compile the project using specified or auto-selected compiler.

Validates configuration, selects compiler if not specified, and executes compilation. Returns result with zip_needed flag.

Parameters:

Name Type Description Default
console bool

Whether to show console window (default: True)

True
compiler _CompilerName | None

Compiler to use or None for auto-selection - "Cx_Freeze": Creates directory with dependencies - "PyInstaller": Creates single executable - "Nuitka": Creates standalone folder or single executable - None: Use config compiler, or prompt user if none set

None

Returns:

Name Type Description
CompilationResult CompilationResult

Result with zip_needed flag and compiler instance

Raises:

Type Description
ConfigurationError

If project not initialized

CompilationError

If compilation fails

Example

service = CompilerService(config) result = service.compile(console=False, compiler="PyInstaller") if result.zip_needed: ... # Create ZIP archive


ConfigService

Service for loading, validating, and managing configuration.

ConfigService

Configuration orchestration service.

Orchestrates configuration loading from multiple sources with a cascade merge system. Sources are merged in priority order (later wins):

  1. pyproject.toml [project] + [tool.ezcompiler] (base)
  2. ezcompiler.yaml or ezcompiler.json (override)
  3. CLI arguments (final override)
Example

config = ConfigService.build_compiler_config() config = ConfigService.build_compiler_config( ... config_path=Path("ezcompiler.yaml"), ... cli_overrides={"compiler": "PyInstaller"}, ... )

load_config staticmethod

load_config(config_path: Path | None = None, pyproject_path: Path | None = None, cli_overrides: dict[str, Any] | None = None, search_dir: Path | None = None) -> dict[str, Any]

Load configuration with full cascade.

Merges configuration from multiple sources in priority order: 1. pyproject.toml [project] + [tool.ezcompiler] (base layer) 2. ezcompiler.yaml or .json - explicit or auto-discovered (override) 3. CLI overrides (final override)

Parameters:

Name Type Description Default
config_path Path | None

Explicit config file path for YAML/JSON (skips auto-discovery)

None
pyproject_path Path | None

Explicit pyproject.toml path (default: search_dir/pyproject.toml)

None
cli_overrides dict[str, Any] | None

Dict of CLI-provided overrides (only non-default values)

None
search_dir Path | None

Directory to search for config files (default: cwd)

None

Returns:

Type Description
dict[str, Any]

dict[str, Any]: Merged configuration dictionary

Raises:

Type Description
ConfigurationError

If no configuration source found or loading fails

build_compiler_config staticmethod

build_compiler_config(config_path: Path | None = None, pyproject_path: Path | None = None, cli_overrides: dict[str, Any] | None = None, search_dir: Path | None = None) -> CompilerConfig

Load configuration and create a CompilerConfig instance.

Convenience method that combines load_config() with CompilerConfig.from_dict().

Parameters:

Name Type Description Default
config_path Path | None

Explicit config file path for YAML/JSON

None
pyproject_path Path | None

Explicit pyproject.toml path

None
cli_overrides dict[str, Any] | None

Dict of CLI-provided overrides

None
search_dir Path | None

Directory to search for config files (default: cwd)

None

Returns:

Name Type Description
CompilerConfig CompilerConfig

Validated configuration instance

Raises:

Type Description
ConfigurationError

If configuration is invalid or not found

load_pyproject_as_dict staticmethod

load_pyproject_as_dict(pyproject_path: Path) -> dict[str, Any]

Load and extract configuration from a pyproject.toml file.

Parameters:

Name Type Description Default
pyproject_path Path

Path to the pyproject.toml file

required

Returns:

Type Description
dict[str, Any]

dict[str, Any]: Extracted configuration dictionary

Raises:

Type Description
ConfigurationError

If the file cannot be loaded or parsed

merge_configs staticmethod

merge_configs(base: dict[str, Any], override: dict[str, Any]) -> dict[str, Any]

Deep-merge two configuration dictionaries (override wins on conflicts).

Parameters:

Name Type Description Default
base dict[str, Any]

Base configuration dictionary

required
override dict[str, Any]

Override dictionary (values win over base)

required

Returns:

Type Description
dict[str, Any]

dict[str, Any]: Merged configuration dictionary


TemplateService

Service for processing templates and generating dynamic files.

TemplateService

TemplateService(file_writer: BaseFileWriter | None = None)

Template processing and file generation service.

Orchestrates template loading, processing, and file generation for configuration files, setup.py, and version information files.

Attributes:

Name Type Description
_template_loader

TemplateLoader instance for template operations

_processor

TemplateProcessor instance for variable substitution

Example

service = TemplateService() config = {"version": "1.0.0", "project_name": "MyApp"} service.generate_setup_file(config, Path("setup.py")) service.generate_version_file(config, Path("version_info.txt"))

Initialize the template service.

Creates TemplateLoader and TemplateProcessor instances for template operations.

generate_config_file

generate_config_file(config: dict[str, Any], output_path: Path, format_type: str = 'yaml') -> None

Generate a configuration file (YAML or JSON) from template.

Parameters:

Name Type Description Default
config dict[str, Any]

Project configuration dictionary

required
output_path Path

Path where to save the config file

required
format_type str

Format type ("yaml" or "json")

'yaml'

Raises:

Type Description
TemplateError

If generation fails

Example

config = {"version": "1.0.0", "project_name": "MyApp"} service.generate_config_file(config, Path("ezcompiler.yaml"), "yaml")

generate_setup_file

generate_setup_file(config: dict[str, Any], output_path: Path | None = None, output_dir: Path | None = None) -> Path

Generate a setup.py file from template.

Parameters:

Name Type Description Default
config dict[str, Any]

Project configuration dictionary

required
output_path Path | None

Direct path to setup.py file (optional)

None
output_dir Path | None

Directory where to save setup.py (optional, defaults to current dir)

None

Returns:

Name Type Description
Path Path

Path to the generated setup.py file

Raises:

Type Description
TemplateError

If generation fails

Example

config = {"version": "1.0.0", "project_name": "MyApp"} setup_path = service.generate_setup_file(config, output_dir=Path("build"))

generate_version_file

generate_version_file(config: dict[str, Any], output_path: Path | None = None, format_type: str = 'txt') -> Path

Generate a version information file from template.

Parameters:

Name Type Description Default
config dict[str, Any]

Project configuration dictionary

required
output_path Path | None

Path where to save the version file (optional)

None
format_type str

Template format type (default: "txt")

'txt'

Returns:

Name Type Description
Path Path

Path to the generated version file

Raises:

Type Description
VersionError

If generation fails

Note

If output_path is None, uses version_filename and output_folder from config.

Example

config = { ... "version": "1.0.0", ... "project_name": "MyApp", ... "version_filename": "version_info.txt", ... "output_folder": "dist" ... } version_path = service.generate_version_file(config)

process_config_template

process_config_template(format_type: str, config: dict[str, Any]) -> str

Process a configuration template with values.

Parameters:

Name Type Description Default
format_type str

Format type ("yaml" or "json")

required
config dict[str, Any]

Configuration dictionary

required

Returns:

Name Type Description
str str

Processed template content

Example

config = {"version": "1.0.0", "project_name": "MyApp"} content = service.process_config_template("yaml", config)

process_setup_template

process_setup_template(config: dict[str, Any]) -> str

Process a setup template with values.

Parameters:

Name Type Description Default
config dict[str, Any]

Configuration dictionary

required

Returns:

Name Type Description
str str

Processed template content

Example

config = {"version": "1.0.0", "project_name": "MyApp"} content = service.process_setup_template(config)

process_version_template

process_version_template(version: str, company_name: str, project_description: str, project_name: str, format_type: str = 'txt') -> str

Process a version template with values.

Parameters:

Name Type Description Default
version str

Project version

required
company_name str

Company name

required
project_description str

Project description

required
project_name str

Project name

required
format_type str

Format type (default: "txt")

'txt'

Returns:

Name Type Description
str str

Processed template content

Example

content = service.process_version_template( ... "1.0.0", "MyCompany", "Description", "MyApp" ... )

list_available_templates

list_available_templates() -> dict[str, list[str]]

List all available templates.

Returns:

Type Description
dict[str, list[str]]

dict[str, list[str]]: Dictionary mapping template types to available formats

Example

templates = service.list_available_templates() print(templates)

validate_template

validate_template(template_type: str, format_type: str) -> bool

Validate a template file.

Parameters:

Name Type Description Default
template_type str

Type of template (config, version, setup)

required
format_type str

Format of the template

required

Returns:

Name Type Description
bool bool

True if template is valid, False otherwise

Example

is_valid = service.validate_template("config", "yaml")

generate_mockup_template

generate_mockup_template(template_type: str, format_type: str, output_path: Path) -> None

Generate a template file with mockup values instead of placeholders.

Parameters:

Name Type Description Default
template_type str

Type of template (config, version, setup)

required
format_type str

Format of the template (yaml, json, py, txt)

required
output_path Path

Path where to save the generated file

required

Raises:

Type Description
TemplateError

If generation fails

Example

service.generate_mockup_template("config", "yaml", Path("ezcompiler.yaml"))

generate_raw_template

generate_raw_template(template_type: str, format_type: str, output_path: Path) -> None

Generate a raw template file with placeholders.

Parameters:

Name Type Description Default
template_type str

Type of template (config, version, setup)

required
format_type str

Format of the template (yaml, json, py, txt)

required
output_path Path

Path where to save the template file

required

Raises:

Type Description
TemplateError

If generation fails

Example

service.generate_raw_template("config", "yaml", Path("template.yaml"))


PipelineService

Service for orchestrating the full compile → zip → upload pipeline with injectable factory.

PipelineService

PipelineService(compiler_service_factory: Callable[[CompilerConfig], CompilerService] | None = None)

Service that coordinates compile, zip and upload stages.

Initialise the pipeline service.

Parameters:

Name Type Description Default
compiler_service_factory Callable[[CompilerConfig], CompilerService] | None

Optional factory to create a CompilerService from a CompilerConfig. Defaults to CompilerService constructor. Inject a custom factory in tests to avoid triggering real compilation.

None

compile_project

compile_project(config: CompilerConfig, console: bool = True, compiler: str | None = None) -> tuple[CompilerService, CompilationResult]

Compile a project and return service + result.

zip_artifact

zip_artifact(config: CompilerConfig, compiler_service: CompilerService, compilation_result: CompilationResult | None, progress_callback: Callable[[str, int], None] | None = None) -> bool

Create ZIP artifact when required and return True when created.

build_stages staticmethod

build_stages(config: CompilerConfig, should_zip: bool = False, should_upload: bool = False, should_release: bool = False, should_installer: bool = False) -> list[dict[str, Any]]

Build the stage list for dynamic_layered_progress.

Parameters:

Name Type Description Default
config CompilerConfig

Compiler configuration (used for display labels)

required
should_zip bool

Whether a ZIP stage should be included

False
should_upload bool

Whether an upload stage should be included

False

Returns:

Type Description
list[dict[str, Any]]

list[dict]: Stage configuration list ready for dynamic_layered_progress

upload_artifact

upload_artifact(config: CompilerConfig, structure: str, destination: str, compilation_result: CompilationResult | None, upload_config: dict[str, Any] | None = None) -> None

Upload project artifact to a destination.

assemble_release_dir staticmethod

assemble_release_dir(config: CompilerConfig) -> Path

Assemble le dossier release contenant le zip et l'installeur.

Layout (nettoyé à chaque run)::

release/
├── <App>.zip                     (si le fichier existe)
└── <App>-<version>-setup.exe      (si installer_enabled=True)

L'arbre TUF (metadata/ + targets/) reste dans tufup_repo_dir et est poussé directement vers le backend d'update par upload().

Parameters:

Name Type Description Default
config CompilerConfig

Configuration (fournit output_folder et zip_file_path).

required

Returns:

Name Type Description
Path Path

Le dossier release/ assemblé.

release_artifact staticmethod

release_artifact(config: CompilerConfig, compilation_result: CompilationResult | None) -> Path

Build le repo TUF local depuis output_folder. Ne publie jamais.

build_installer staticmethod

build_installer(config: CompilerConfig, compilation_result: CompilationResult | None) -> Path | None

Build the Inno Setup installer when installer_enabled=True.


UploaderService

Service for orchestrating upload operations to various backends.

UploaderService

Upload orchestration service.

Orchestrates file and directory uploads using different upload backends. Handles upload type selection, validation, and execution.

Example

service = UploaderService() service.upload( ... source_path=Path("dist.zip"), ... upload_type="disk", ... destination="releases/", ... upload_config={"overwrite": True} ... )

upload staticmethod

upload(source_path: Path, upload_type: UploadType, destination: str, upload_config: dict[str, Any] | None = None) -> None

Upload a file or directory to the specified destination.

Parameters:

Name Type Description Default
source_path Path

Path to the source file or directory

required
upload_type UploadType

Type of upload ("disk" or "server")

required
destination str

Destination path or URL

required
upload_config dict[str, Any] | None

Additional uploader configuration options

None

Raises:

Type Description
UploadError

If upload fails or upload type is invalid

Example

UploaderService.upload( ... Path("dist.zip"), ... "disk", ... "releases/", ... {"overwrite": True} ... )

download staticmethod

download(remote_source: str, upload_type: UploadType, destination_local: Path, upload_config: dict[str, Any] | None = None) -> None

Download a remote tree into destination_local via the backend.

Parameters:

Name Type Description Default
remote_source str

Remote source (path, URL or prefix) to fetch.

required
upload_type UploadType

Backend type ("disk", "server" or "r2").

required
destination_local Path

Local directory to populate.

required
upload_config dict[str, Any] | None

Additional uploader configuration options.

None

Raises:

Type Description
UploadError

If the download fails or the type is invalid.

upload_release staticmethod

upload_release(config: CompilerConfig, repo_dir: Path, release_root: Path | None, destination: str | None = None, repo_destination: str | None = None, release_destination: str | None = None, upload_config: dict[str, Any] | None = None) -> None

Double upload séquentiel : arbre TUF puis zip installeur.

Étape 1 — upload arbre TUF vers <dest>/update/ (ou préfixe R2). Étape 2 — upload zip installeur vers <dest>/release/ (ignoré si R2).

Note: Le double-upload n'est pas atomique. Si l'upload du zip échoue, le repo TUF est déjà en ligne. En cas d'échec, ré-exécuter upload() pour reprendre.

Parameters:

Name Type Description Default
config CompilerConfig

CompilerConfig contenant les destinations et options R2.

required
repo_dir Path

Répertoire local du repo TUF.

required
release_root Path | None

Répertoire local du zip installeur (None si R2).

required
destination str | None

Override commun pour les deux destinations.

None
repo_destination str | None

Override de config.repo_destination.

None
release_destination str | None

Override de config.release_destination.

None
upload_config dict[str, Any] | None

Options supplémentaires passées aux uploaders.

None

Raises:

Type Description
UploadError

Si un upload échoue.

get_supported_types staticmethod

get_supported_types() -> list[str]

Get list of supported upload types.

Returns:

Type Description
list[str]

list[str]: List of supported upload type names

Example

types = UploaderService.get_supported_types() print(types) ['disk', 'server']

validate_upload_config staticmethod

validate_upload_config(upload_type: UploadType, config: dict[str, Any] | None = None) -> bool

Validate configuration for a specific upload type.

Parameters:

Name Type Description Default
upload_type UploadType

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

Example

is_valid = UploaderService.validate_upload_config( ... "disk", {"overwrite": True} ... )


UpdaterService

Service for generating client updater files (update.py, settings.py, root.json) for tufup-based auto-update workflows.

UpdaterService

Service that generates the tufup client update files.

generate staticmethod

generate(config: CompilerConfig, output_dir: Path) -> list[Path]

Generate update.py, settings.py, and copy root.json.

Parameters:

Name Type Description Default
config CompilerConfig

Compiler configuration (must have tuf_enabled=True).

required
output_dir Path

Directory where files are written (created if absent).

required

Returns:

Type Description
list[Path]

List of generated file paths [settings.py, update.py, root.json].

Raises:

Type Description
UpdaterConfigError

If config is invalid (tuf_enabled=False, root.json absent, repo_public_url missing for non-disk).

UpdaterGenerationError

If writing files fails.


InstallerService

Service orchestrating first-deployment installer packaging via an installer adapter.

InstallerService

Service orchestrating first-deployment installer packaging.

build_installer staticmethod

build_installer(bundle_dir: Path, app_name: str, version: str, output_dir: Path, *, installer_type: str = 'innosetup', installer_config: dict[str, Any] | None = None) -> Path

Build the installer executable for a compiled bundle.

Parameters:

Name Type Description Default
bundle_dir Path

Directory containing the compiled application.

required
app_name str

Application name.

required
version str

Application version string.

required
output_dir Path

Directory where the setup.exe is produced.

required
installer_type str

Installer backend to use (default: "innosetup").

'innosetup'
installer_config dict[str, Any] | None

Extra config forwarded to the installer adapter (e.g. "icon", "company_name", "iss_path", "iscc_path").

None

Returns:

Name Type Description
Path Path

The produced setup.exe path.

Raises:

Type Description
InstallerError

When installer packaging fails.


ReleaseService

Service orchestrating secure-release packaging (TUF) and, optionally, publication.

ReleaseService

Service orchestrating secure-release packaging and publication.

release_and_publish staticmethod

release_and_publish(bundle_dir: Path, app_name: str, version: str, repo_dir: Path, *, release_type: str = 'tufup', publish: bool = False, pull_before: bool = False, upload_type: str | None = None, destination: str | None = None, releaser_config: dict[str, Any] | None = None, upload_config: dict[str, Any] | None = None) -> Path

Build the local TUF repo, then optionally publish it.

Parameters:

Name Type Description Default
bundle_dir Path

Directory containing the compiled application.

required
app_name str

Application name (used by tufup to name bundles).

required
version str

Application version string.

required
repo_dir Path

Root directory for the local TUF repository tree.

required
release_type str

Release backend to use (default: "tufup").

'tufup'
publish bool

When True, transfer the repository/ tree via an uploader.

False
pull_before bool

When True, download the current remote tree into repo_dir before releasing (R2 source-of-truth cycle). Requires upload_type and destination.

False
upload_type str | None

Upload backend ("disk" or "server"). Required when publish=True.

None
destination str | None

Upload destination path or URL. Required when publish=True.

None
releaser_config dict[str, Any] | None

Extra config forwarded to the releaser adapter.

None
upload_config dict[str, Any] | None

Extra config forwarded to the uploader adapter.

None

Returns:

Name Type Description
Path Path

The local repository/ tree path.

Raises:

Type Description
ValueError

When publish=True but upload_type or destination is missing.

ReleaseError

When release packaging or publishing fails.

init_release staticmethod

init_release(app_name: str, repo_dir: Path, keys_dir: Path, *, release_type: str = 'tufup', releaser_config: dict[str, Any] | None = None) -> bool

Crée le releaser via la factory et délègue à init_keys.

Returns True si init effectuée, False si déjà présente (skip).

refresh_expiration staticmethod

refresh_expiration(app_name: str, repo_dir: Path, keys_dir: Path, *, roles: tuple[str, ...] = ('targets', 'snapshot', 'timestamp'), days: int | None = None, release_type: str = 'tufup', releaser_config: dict[str, Any] | None = None) -> Path

Re-signe les metadata pour repousser l'expiration sans nouvelle release.

Keep-alive natif tufup pour les projets mis à jour irrégulièrement.

Returns le dossier du repo TUF local.