Coverage for src/dataknobs_common/exceptions.py: 100%

1"""Common exception hierarchy for all dataknobs packages.

2

3 This module provides a unified exception framework that all dataknobs packages

4 can extend. It supports both simple exceptions and context-rich exceptions with

5 detailed error information.

6

7 The exception hierarchy supports:

8- Simple error messages for straightforward cases

9- Context dictionaries for rich error information

10- Details dictionaries (FSM-style) for structured error data

11- Package-specific extensions

12

13 Example:

14 ```python

15 from dataknobs_common.exceptions import ValidationError, NotFoundError

16

17 # Simple exception

18 raise ValidationError("Invalid email format")

19

20 # Context-rich exception

21 raise NotFoundError(

22 "User not found",

23 context={"user_id": "123", "attempted_at": "2024-11-08"}

24 )

25

26 # Catch any dataknobs error

27 try:

28 operation()

29 except DataknobsError as e:

30 logger.error(f"Error: {e}")

31 if e.context:

32 logger.error(f"Context: {e.context}")

33 ```

34

35 Package-Specific Extensions:

36 ```python

37 from dataknobs_common.exceptions import DataknobsError

38

39 class MyPackageError(DataknobsError):

40 '''Base exception for mypackage.'''

41 pass

42

43 class SpecificError(MyPackageError):

44 '''Specific error with custom context.'''

45 def __init__(self, item_id: str, message: str):

46 super().__init__(

47 f"Item '{item_id}': {message}",

48 context={"item_id": item_id}

49 )

50 ```

51"""

52

53 from typing import Any, Dict

54

55

56 class DataknobsError(Exception):

57 """Base exception for all dataknobs packages.

58

59 This is the root exception that all dataknobs packages should extend.

60 It supports optional context data for rich error information, making

61 debugging and error handling more effective.

62

63 Attributes:

64 context: Dictionary containing contextual information about the error

65 details: Alias for context (FSM-style compatibility)

66

67 Args:

68 message: Human-readable error message

69 context: Optional dictionary with error context (field names, IDs, etc.)

70 details: Alternative to context (both are supported for compatibility)

71

72 Example:

73 ```python

74 error = DataknobsError(

75 "Operation failed",

76 context={"operation": "save", "item_id": "123"}

77 )

78 str(error)

79 # 'Operation failed'

80 error.context

81 # {'operation': 'save', 'item_id': '123'}

82 ```

83 """

84

85 def __init__(

86 self,

87 message: str,

88 context: Dict[str, Any] | None = None,

89 details: Dict[str, Any] | None = None,

90 ):

91 """Initialize the exception with optional context.

92

93 Args:

94 message: Error message

95 context: Optional context dictionary

96 details: Optional details dictionary (merged with context)

97 """

98 super().__init__(message)

99 # Support both context and details parameters

100 # Details takes precedence if both are provided

101 self.context = details or context or {}

102 # Alias for FSM-style compatibility

103 self.details = self.context

104

105

106 class ValidationError(DataknobsError):

107 """Raised when validation fails.

108

109 Use this exception when data or configuration fails validation checks.

110 Common scenarios include:

111 - Invalid input data

112 - Schema validation failures

113 - Constraint violations

114 - Type mismatches

115

116 Example:

117 ```python

118 raise ValidationError(

119 "Email format invalid",

120 context={"field": "email", "value": "not-an-email"}

121 )

122 ```

123 """

124

125 pass

126

127

128 class ConfigurationError(DataknobsError):

129 """Raised when configuration is invalid or missing.

130

131 Use this exception for configuration-related errors including:

132 - Missing required configuration

133 - Invalid configuration values

134 - Configuration file not found

135 - Circular references in configuration

136

137 Example:

138 ```python

139 raise ConfigurationError(

140 "Database configuration missing",

141 context={"config_key": "database.primary", "available_keys": ["cache", "auth"]}

142 )

143 ```

144 """

145

146 pass

147

148

149 class ResourceError(DataknobsError):

150 """Raised when resource operations fail.

151

152 Use this exception for resource management failures including:

153 - Resource acquisition failures

154 - Connection errors

155 - Resource pool exhaustion

156 - Timeout errors

157

158 Example:

159 ```python

160 raise ResourceError(

161 "Failed to acquire database connection",

162 context={"pool_size": 10, "active_connections": 10, "timeout": 30}

163 )

164 ```

165 """

166

167 pass

168

169

170 class NotFoundError(DataknobsError):

171 """Raised when a requested item is not found.

172

173 Use this exception when looking up items by ID, name, or key and they

174 don't exist. Common scenarios include:

175 - Record not found in database

176 - Configuration key not found

177 - File not found

178 - Resource not registered

179

180 Example:

181 ```python

182 raise NotFoundError(

183 "Record not found",

184 context={"record_id": "user-123", "table": "users"}

185 )

186 ```

187 """

188

189 pass

190

191

192 class OperationError(DataknobsError):

193 """Raised when an operation fails.

194

195 Use this exception for general operation failures that don't fit

196 other categories. Common scenarios include:

197 - Database operation failures

198 - File I/O errors

199 - Network operation failures

200 - State transition errors

201

202 Example:

203 ```python

204 raise OperationError(

205 "Failed to save record",

206 context={"operation": "update", "backend": "postgres", "error": "connection lost"}

207 )

208 ```

209 """

210

211 pass

212

213

214 class ConcurrencyError(DataknobsError):

215 """Raised when concurrent operation conflicts occur.

216

217 Use this exception for concurrency-related failures including:

218 - Lock acquisition failures

219 - Transaction conflicts

220 - Race conditions

221 - Optimistic locking failures

222

223 Example:

224 ```python

225 raise ConcurrencyError(

226 "Record modified by another process",

227 context={"record_id": "123", "expected_version": 5, "actual_version": 6}

228 )

229 ```

230 """

231

232 pass

233

234

235 class SerializationError(DataknobsError):

236 """Raised when serialization or deserialization fails.

237

238 Use this exception for data format conversion errors including:

239 - JSON encoding/decoding failures

240 - Invalid data format

241 - Schema mismatch

242 - Type conversion errors

243

244 Example:

245 ```python

246 raise SerializationError(

247 "Cannot deserialize data",

248 context={"format": "json", "field": "created_at", "value": "invalid-date"}

249 )

250 ```

251 """

252

253 pass

254

255

256 class TimeoutError(DataknobsError):

257 """Raised when an operation times out.

258

259 Use this exception when operations exceed their time limit including:

260 - Connection timeouts

261 - Query timeouts

262 - Resource acquisition timeouts

263 - Operation execution timeouts

264

265 Example:

266 ```python

267 raise TimeoutError(

268 "Database query timed out",

269 context={"query": "SELECT * FROM large_table", "timeout_seconds": 30}

270 )

271 ```

272 """

273

274 pass

275

276

277 __all__ = [

278 "DataknobsError",

279 "ValidationError",

280 "ConfigurationError",

281 "ResourceError",

282 "NotFoundError",

283 "OperationError",

284 "ConcurrencyError",

285 "SerializationError",

286 "TimeoutError",

287]