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

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

2# BASE_UPLOADER - Abstract base uploader interface 

3# Project: ezcompiler 

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

5 

6""" 

7Base uploader - Abstract base class for uploader implementations. 

8 

9This module defines the interface and common functionality for all uploaders, 

10providing validation and contract enforcement for upload operations. 

11 

12Note: Protocols layer should not perform logging directly. Logging is handled 

13by the service layer that orchestrates upload operations. 

14""" 

15 

16from __future__ import annotations 

17 

18# /////////////////////////////////////////////////////////////// 

19# IMPORTS 

20# /////////////////////////////////////////////////////////////// 

21# Standard library imports 

22from abc import ABC, abstractmethod 

23from pathlib import Path 

24from typing import Any 

25 

26# Local imports 

27from ..shared.exceptions import UploadError 

28 

29# /////////////////////////////////////////////////////////////// 

30# CLASSES 

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

32 

33 

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. 

39 

40 Defines the interface that all uploaders must implement and provides 

41 common functionality for upload operations and validation. 

42 

43 Attributes: 

44 _config: Configuration dictionary for the uploader 

45 

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 """ 

54 

55 # //////////////////////////////////////////////// 

56 # INITIALIZATION 

57 # //////////////////////////////////////////////// 

58 

59 def __init__(self, config: dict[str, Any] | None = None) -> None: 

60 """ 

61 Initialize the uploader with configuration. 

62 

63 Args: 

64 config: Configuration dictionary (default: None) 

65 

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() 

72 

73 # //////////////////////////////////////////////// 

74 # ABSTRACT METHODS 

75 # //////////////////////////////////////////////// 

76 

77 @abstractmethod 

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

79 """ 

80 Upload a file or directory to the destination. 

81 

82 Args: 

83 source_path: Path to the source file or directory 

84 destination: Destination path or URL 

85 

86 Raises: 

87 UploadError: If upload fails 

88 

89 Note: 

90 Subclasses must implement this method to define upload behavior. 

91 """ 

92 

93 @abstractmethod 

94 def get_uploader_name(self) -> str: 

95 """ 

96 Get the name of this uploader. 

97 

98 Returns: 

99 str: Human-readable name of the uploader 

100 

101 Note: 

102 Used for identification purposes. 

103 """ 

104 

105 # //////////////////////////////////////////////// 

106 # VALIDATION METHODS 

107 # //////////////////////////////////////////////// 

108 

109 def _validate_config(self) -> None: # noqa: B027 

110 """ 

111 Validate uploader configuration. 

112 

113 Base implementation does nothing. Subclasses should override this 

114 method to perform specific validation for their configuration. 

115 

116 Raises: 

117 UploadError: If configuration is invalid (in subclasses) 

118 

119 Note: 

120 This method is intentionally empty in the base class. 

121 """ 

122 # Base implementation intentionally empty - subclasses should override 

123 

124 def _validate_source_path(self, source_path: Path) -> None: 

125 """ 

126 Validate that the source path exists and is accessible. 

127 

128 Args: 

129 source_path: Path to validate 

130 

131 Raises: 

132 UploadError: If source path is invalid 

133 

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}")