Coverage for src/ezcompiler/adapters/base_uploader.py: 47.37%
15 statements
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-03 15:46 +0000
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-03 15:46 +0000
1# ///////////////////////////////////////////////////////////////
2# BASE_UPLOADER - Abstract base uploader interface
3# Project: ezcompiler
4# ///////////////////////////////////////////////////////////////
6"""
7Base uploader - Abstract base class for uploader implementations.
9This module defines the interface and common functionality for all uploaders,
10providing validation and contract enforcement for upload operations.
12Note: Protocols layer should not perform logging directly. Logging is handled
13by the service layer that orchestrates upload operations.
14"""
16from __future__ import annotations
18# ///////////////////////////////////////////////////////////////
19# IMPORTS
20# ///////////////////////////////////////////////////////////////
21# Standard library imports
22from abc import ABC, abstractmethod
23from pathlib import Path
24from typing import Any
26# Local imports
27from ..shared.exceptions import UploadError
29# ///////////////////////////////////////////////////////////////
30# CLASSES
31# ///////////////////////////////////////////////////////////////
34# TODO [AUDIT P3]: migrer ABC → Protocol (typing.Protocol) pour aligner avec l'architecture hexagonale
35# Même approche que BaseCompiler — créer UploaderPort(Protocol) dans adapters/ports.py.
36class BaseUploader(ABC):
37 """
38 Abstract base class for uploaders.
40 Defines the interface that all uploaders must implement and provides
41 common functionality for upload operations and validation.
43 Attributes:
44 _config: Configuration dictionary for the uploader
46 Example:
47 >>> class MyUploader(BaseUploader):
48 ... def upload(self, source_path: Path, destination: str) -> None:
49 ... # Implementation
50 ... pass
51 ... def get_uploader_name(self) -> str:
52 ... return "My Uploader"
53 """
55 # ////////////////////////////////////////////////
56 # INITIALIZATION
57 # ////////////////////////////////////////////////
59 def __init__(self, config: dict[str, Any] | None = None) -> None:
60 """
61 Initialize the uploader with configuration.
63 Args:
64 config: Configuration dictionary (default: None)
66 Note:
67 Subclasses should call super().__init__(config) to initialize
68 configuration and validation.
69 """
70 self._config = config or {}
71 self._validate_config()
73 # ////////////////////////////////////////////////
74 # ABSTRACT METHODS
75 # ////////////////////////////////////////////////
77 @abstractmethod
78 def upload(self, source_path: Path, destination: str) -> None:
79 """
80 Upload a file or directory to the destination.
82 Args:
83 source_path: Path to the source file or directory
84 destination: Destination path or URL
86 Raises:
87 UploadError: If upload fails
89 Note:
90 Subclasses must implement this method to define upload behavior.
91 """
93 @abstractmethod
94 def get_uploader_name(self) -> str:
95 """
96 Get the name of this uploader.
98 Returns:
99 str: Human-readable name of the uploader
101 Note:
102 Used for identification purposes.
103 """
105 # ////////////////////////////////////////////////
106 # VALIDATION METHODS
107 # ////////////////////////////////////////////////
109 def _validate_config(self) -> None: # noqa: B027
110 """
111 Validate uploader configuration.
113 Base implementation does nothing. Subclasses should override this
114 method to perform specific validation for their configuration.
116 Raises:
117 UploadError: If configuration is invalid (in subclasses)
119 Note:
120 This method is intentionally empty in the base class.
121 """
122 # Base implementation intentionally empty - subclasses should override
124 def _validate_source_path(self, source_path: Path) -> None:
125 """
126 Validate that the source path exists and is accessible.
128 Args:
129 source_path: Path to validate
131 Raises:
132 UploadError: If source path is invalid
134 Note:
135 Validation is handled at protocol level to keep this port independent
136 from uploader utility helpers.
137 """
138 if not source_path.exists():
139 raise UploadError(f"Source path does not exist: {source_path}")
140 if not source_path.is_file() and not source_path.is_dir():
141 raise UploadError(f"Source path is not a file or directory: {source_path}")