Coverage for src / dataknobs_data / exceptions.py: 48%

66 statements  

« prev     ^ index     » next       coverage.py v7.13.0, created at 2025-12-26 15:45 -0700

1"""Custom exceptions for the dataknobs_data package. 

2 

3This module defines exception types for the data package, 

4built on the common exception framework from dataknobs_common. 

5""" 

6 

7from __future__ import annotations 

8 

9from typing import TYPE_CHECKING 

10 

11from dataknobs_common import ( 

12 ConfigurationError as BaseConfigurationError, 

13 ConcurrencyError as BaseConcurrencyError, 

14 DataknobsError, 

15 NotFoundError, 

16 OperationError, 

17 ResourceError, 

18 SerializationError as BaseSerializationError, 

19 ValidationError, 

20) 

21 

22if TYPE_CHECKING: 

23 from dataknobs_data.query import Query 

24 

25# Create DataknobsDataError as alias to DataknobsError for backward compatibility 

26DataknobsDataError = DataknobsError 

27 

28 

29class RecordNotFoundError(NotFoundError): 

30 """Raised when a requested record is not found.""" 

31 

32 def __init__(self, id: str): 

33 self.id = id 

34 super().__init__(f"Record with ID '{id}' not found", context={"id": id}) 

35 

36 

37class RecordValidationError(ValidationError): 

38 """Raised when record validation fails.""" 

39 

40 def __init__(self, message: str, field_name: str | None = None): 

41 self.field_name = field_name 

42 if field_name: 

43 message = f"Field '{field_name}': {message}" 

44 super().__init__(message, context={"field_name": field_name} if field_name else None) 

45 

46 

47class FieldTypeError(ValidationError): 

48 """Raised when a field type operation fails.""" 

49 

50 def __init__(self, field_name: str, expected_type: str, actual_type: str): 

51 self.field_name = field_name 

52 self.expected_type = expected_type 

53 self.actual_type = actual_type 

54 super().__init__( 

55 f"Field '{field_name}' type mismatch: expected {expected_type}, got {actual_type}", 

56 context={ 

57 "field_name": field_name, 

58 "expected_type": expected_type, 

59 "actual_type": actual_type, 

60 }, 

61 ) 

62 

63 

64class DatabaseError(ResourceError): 

65 """General database error.""" 

66 

67 pass 

68 

69 

70class DatabaseConnectionError(ResourceError): 

71 """Raised when database connection fails.""" 

72 

73 def __init__(self, backend: str, message: str): 

74 self.backend = backend 

75 super().__init__( 

76 f"Failed to connect to {backend} backend: {message}", context={"backend": backend} 

77 ) 

78 

79 

80class DatabaseOperationError(OperationError): 

81 """Raised when a database operation fails.""" 

82 

83 def __init__(self, operation: str, message: str): 

84 self.operation = operation 

85 super().__init__( 

86 f"Database operation '{operation}' failed: {message}", context={"operation": operation} 

87 ) 

88 

89 

90class QueryError(OperationError): 

91 """Raised when query execution fails.""" 

92 

93 def __init__(self, message: str, query: Query | None = None): 

94 self.query = query 

95 context = {"query": str(query)} if query else None 

96 super().__init__(f"Query error: {message}", context=context) 

97 

98 

99class SerializationError(BaseSerializationError): 

100 """Raised when serialization/deserialization fails.""" 

101 

102 def __init__(self, format: str, message: str): 

103 self.format = format 

104 super().__init__(f"Serialization error ({format}): {message}", context={"format": format}) 

105 

106 

107class DataFormatError(ValidationError): 

108 """Raised when data format is invalid or unsupported.""" 

109 

110 def __init__(self, format: str, message: str): 

111 self.format = format 

112 super().__init__(f"Data format error ({format}): {message}", context={"format": format}) 

113 

114 

115class BackendNotFoundError(NotFoundError): 

116 """Raised when a requested backend is not available.""" 

117 

118 def __init__(self, backend: str, available: list | None = None): 

119 self.backend = backend 

120 self.available = available or [] 

121 message = f"Backend '{backend}' not found" 

122 if self.available: 

123 message += f". Available backends: {', '.join(self.available)}" 

124 super().__init__(message, context={"backend": backend, "available": self.available}) 

125 

126 

127class ConfigurationError(BaseConfigurationError): 

128 """Raised when configuration is invalid.""" 

129 

130 def __init__(self, parameter: str, message: str): 

131 self.parameter = parameter 

132 super().__init__( 

133 f"Configuration error for '{parameter}': {message}", context={"parameter": parameter} 

134 ) 

135 

136 

137class ConcurrencyError(BaseConcurrencyError): 

138 """Raised when a concurrency conflict occurs.""" 

139 

140 def __init__(self, message: str): 

141 super().__init__(f"Concurrency error: {message}") 

142 

143 

144class TransactionError(OperationError): 

145 """Raised when a transaction fails.""" 

146 

147 def __init__(self, message: str): 

148 super().__init__(f"Transaction error: {message}") 

149 

150 

151class MigrationError(OperationError): 

152 """Raised when data migration fails.""" 

153 

154 def __init__(self, source: str, target: str, message: str): 

155 self.source = source 

156 self.target = target 

157 super().__init__( 

158 f"Migration from {source} to {target} failed: {message}", 

159 context={"source": source, "target": target}, 

160 )